Skip to main content
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
checked
Boolean
required
Whether the checkbox is currently checked
onCheckedChange
((Boolean) -> Unit)?
default:"null"
Callback invoked when the checkbox is toggled. If null, checkbox becomes non-interactive
enabled
Boolean
default:"true"
Whether the checkbox is enabled and can be toggled
colors
CheckboxColors
default:"CheckboxDefaults.colors()"
Colors for different checkbox states

TriStateCheckbox Parameters

state
ToggleableState
required
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

Build docs developers (and LLMs) love