The Checkbox component provides a customizable checkbox with support for checked, unchecked, and indeterminate states, along with smooth animations.
Import
import com.nomanr.lumo.ui.components.Checkbox
import com.nomanr.lumo.ui.components.TriStateCheckbox
import com.nomanr.lumo.ui.components.CheckboxColors
Checkbox
A standard two-state checkbox for binary selections.
Component Signature
@Composable
fun Checkbox(
modifier: Modifier = Modifier,
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit)? = null,
enabled: Boolean = true,
colors: CheckboxColors = CheckboxDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
)
Source: Checkbox.kt:62-83
TriStateCheckbox
A checkbox that supports three states: checked, unchecked, and indeterminate.
Component Signature
@Composable
fun TriStateCheckbox(
state: ToggleableState,
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: CheckboxColors = CheckboxDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
)
Source: Checkbox.kt:86-122
Parameters
Checkbox Parameters
modifier
Modifier
default:"Modifier"
Modifier to be applied to the checkbox
Whether the checkbox is currently checked
onCheckedChange
((Boolean) -> Unit)?
default:"null"
Callback invoked when the checkbox is toggled. If null, checkbox becomes non-interactive
Whether the checkbox is enabled and can be toggled
colors
CheckboxColors
default:"CheckboxDefaults.colors()"
Colors for different checkbox states
TriStateCheckbox Parameters
The current state: ToggleableState.On, ToggleableState.Off, or ToggleableState.Indeterminate
onClick
(() -> Unit)?
default:"null"
Callback invoked when the checkbox is clicked. If null, checkbox becomes non-interactive
Examples
Basic Checkbox
var isChecked by remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Checkbox(
checked = isChecked,
onCheckedChange = { isChecked = it }
)
Text("Accept terms and conditions")
}
Checkbox with Label
From preview (Checkbox.kt:383-393):
var isChecked by remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Checkbox(
checked = isChecked,
onCheckedChange = { isChecked = it }
)
BasicText("Basic Checkbox")
}
Disabled Checkboxes
From preview (Checkbox.kt:395-418):
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Column {
Checkbox(
checked = true,
onCheckedChange = null,
enabled = false
)
BasicText("Disabled Checked")
}
Column {
Checkbox(
checked = false,
onCheckedChange = null,
enabled = false
)
BasicText("Disabled Unchecked")
}
}
Tri-State Checkbox
From preview (Checkbox.kt:420-437):
var triState by remember { mutableStateOf(ToggleableState.Off) }
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TriStateCheckbox(
state = triState,
onClick = {
triState = when (triState) {
ToggleableState.Off -> ToggleableState.Indeterminate
ToggleableState.Indeterminate -> ToggleableState.On
ToggleableState.On -> ToggleableState.Off
}
}
)
BasicText("Tri-State Checkbox")
}
Checkbox Group
From preview (Checkbox.kt:468-497):
var selectedItems by remember { mutableStateOf(setOf<String>()) }
val items = listOf("Option 1", "Option 2", "Option 3")
Column {
BasicText("Checkbox Group")
Spacer(modifier = Modifier.height(8.dp))
items.forEach { item ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Checkbox(
checked = selectedItems.contains(item),
onCheckedChange = { checked ->
selectedItems = if (checked) {
selectedItems + item
} else {
selectedItems - item
}
}
)
BasicText(item)
}
}
}
Custom Colors
From preview (Checkbox.kt:439-466):
var customColorChecked by remember { mutableStateOf(false) }
val customColors = CheckboxColors(
checkedCheckmarkColor = AppTheme.colors.onPrimary,
uncheckedCheckmarkColor = AppTheme.colors.transparent,
checkedBoxColor = AppTheme.colors.primary,
uncheckedBoxColor = AppTheme.colors.transparent,
disabledCheckedBoxColor = AppTheme.colors.disabled,
disabledUncheckedBoxColor = AppTheme.colors.transparent,
disabledIndeterminateBoxColor = AppTheme.colors.primary,
checkedBorderColor = AppTheme.colors.primary,
uncheckedBorderColor = AppTheme.colors.primary,
disabledBorderColor = AppTheme.colors.disabled,
disabledUncheckedBorderColor = AppTheme.colors.disabled,
disabledIndeterminateBorderColor = AppTheme.colors.disabled
)
Checkbox(
checked = customColorChecked,
onCheckedChange = { customColorChecked = it },
colors = customColors
)
CheckboxColors
The CheckboxColors data class allows customization of checkbox appearance (Checkbox.kt:299-312):
data class CheckboxColors(
val checkedCheckmarkColor: Color,
val uncheckedCheckmarkColor: Color,
val checkedBoxColor: Color,
val uncheckedBoxColor: Color,
val disabledCheckedBoxColor: Color,
val disabledUncheckedBoxColor: Color,
val disabledIndeterminateBoxColor: Color,
val checkedBorderColor: Color,
val uncheckedBorderColor: Color,
val disabledBorderColor: Color,
val disabledUncheckedBorderColor: Color,
val disabledIndeterminateBorderColor: Color,
)
Styling
Default Styling
From CheckboxDefaults (Checkbox.kt:269-278):
- Checkbox Size: 20.dp
- Stroke Width: 2.dp
- Border Radius: 4.dp
- Minimum Interactive Size: 44.dp
- Padding: 2.dp
Animation Durations
- Box In Duration: 50ms
- Box Out Duration: 100ms
- Check Animation Duration: 100ms
Default Colors
From CheckboxDefaults.colors() (Checkbox.kt:280-296):
- Checked Box: Primary color
- Checked Checkmark: On-primary color
- Unchecked Box: Transparent
- Unchecked Border: Primary color
- Disabled: Uses disabled theme colors
Animations
The checkbox features smooth animations:
- Checkmark draws with a path animation (Checkbox.kt:132-148)
- Box color transitions when toggling (Checkbox.kt:323-344)
- Border color transitions based on state (Checkbox.kt:346-368)
- Indeterminate state shows a horizontal line instead of a checkmark
Accessibility
- Minimum interactive size of 44.dp for touch targets (Checkbox.kt:97)
- Automatically assigned
Role.Checkbox semantic property (Checkbox.kt:102)
- Ripple effect on interaction with unbounded radius (Checkbox.kt:105-108)
- Disabled state clearly indicated with reduced opacity colors
- Support for tri-state for parent-child checkbox relationships
Advanced Usage
Parent-Child Checkbox Relationship
var childStates by remember { mutableStateOf(listOf(false, false, false)) }
val parentState = when {
childStates.all { it } -> ToggleableState.On
childStates.any { it } -> ToggleableState.Indeterminate
else -> ToggleableState.Off
}
Column {
// Parent checkbox
TriStateCheckbox(
state = parentState,
onClick = {
val newState = parentState != ToggleableState.On
childStates = childStates.map { newState }
}
)
// Child checkboxes
childStates.forEachIndexed { index, checked ->
Checkbox(
checked = checked,
onCheckedChange = { newValue ->
childStates = childStates.toMutableList().also {
it[index] = newValue
}
}
)
}
}
Source Reference
Full implementation: com.nomanr.lumo.ui.components.Checkbox.kt