TriStateCheckbox

A foundational component for creating three-state checkboxes that can be checked, unchecked, or in an indeterminate state. Perfect for "select all" scenarios and hierarchical selections.

Accessible out of the box and fully renderless, so that you can apply any styling you like.

Basic Example

To create a tri-state checkbox use the TriStateCheckbox component. The checkbox supports three states: On, Off, and Indeterminate. You can cycle through states by clicking or pressing Enter/Space while focused.

The checkIcon composable receives the current ToggleableState and should render the appropriate icon for each state.

var triState by remember { mutableStateOf(ToggleableState.Off) }

TriStateCheckbox(
    value = triState,
    onClick = {
        triState = when (triState) {
            ToggleableState.Off -> ToggleableState.On
            ToggleableState.On -> ToggleableState.Indeterminate
            ToggleableState.Indeterminate -> ToggleableState.Off
        }
    },
    shape = RoundedCornerShape(4.dp),
    backgroundColor = Color.White,
    borderWidth = 1.dp,
    borderColor = Color.Black.copy(0.33f),
    modifier = Modifier.size(24.dp)
) { state ->
    when (state) {
        ToggleableState.On -> Icon(Check, contentDescription = null)
        ToggleableState.Indeterminate -> Icon(Minus, contentDescription = null)
        ToggleableState.Off -> Unit // No icon shown
    }
}

Styling

The TriStateCheckbox is fully renderless and handles all UX logic, accessibility and keyboard interactions for you, but does not display any information on the screen by default.

The shape of the checkbox is used to clip the checkbox and applies to both the background and border.

The borderColor and borderWidth parameters place a border around the checkbox, taking the given shape into consideration.

The backgroundColor sets the color of the checkbox's surface.

The checkIcon composable is where you define what gets displayed for each state. It receives the current ToggleableState as a parameter.

Code Examples

Select All Pattern

A common use case for TriStateCheckbox is implementing "select all" functionality with a group of child checkboxes.

val checkboxOptions = listOf("Option 1", "Option 2", "Option 3")
var selected by remember { mutableStateOf(List(checkboxOptions.size) { false }) }

val triState = when {
    selected.all { it } -> ToggleableState.On
    selected.none { it } -> ToggleableState.Off
    else -> ToggleableState.Indeterminate
}

Column {
    // Parent TriState checkbox - "Select All"
    Row(verticalAlignment = Alignment.CenterVertically) {
        TriStateCheckbox(
            value = triState,
            onClick = {
                val newState = when (triState) {
                    ToggleableState.Off -> true
                    ToggleableState.Indeterminate -> true
                    ToggleableState.On -> false
                }
                selected = List(checkboxOptions.size) { newState }
            },
            shape = RoundedCornerShape(4.dp),
            backgroundColor = Color.White,
            borderWidth = 1.dp,
            borderColor = Color.Black.copy(0.33f),
            modifier = Modifier.size(24.dp),
            contentDescription = "Select all options"
        ) { state ->
            when (state) {
                ToggleableState.On -> Icon(Check, contentDescription = null)
                ToggleableState.Indeterminate -> Icon(Minus, contentDescription = null)
                ToggleableState.Off -> Unit
            }
        }
        Spacer(Modifier.width(12.dp))
        Text("Select All")
    }
    
    // Child checkboxes
    checkboxOptions.forEachIndexed { index, option ->
        Row(verticalAlignment = Alignment.CenterVertically) {
            Checkbox(
                checked = selected[index],
                onCheckedChange = { checked ->
                    selected = selected.toMutableList().apply {
                        this[index] = checked
                    }
                },
                shape = RoundedCornerShape(4.dp),
                backgroundColor = Color.White,
                borderWidth = 1.dp,
                borderColor = Color.Black.copy(0.33f),
                modifier = Modifier.size(24.dp),
                contentDescription = option
            ) {
                Icon(Check, contentDescription = null)
            }
            Spacer(Modifier.width(12.dp))
            Text(option)
        }
    }
}

Keyboard Interactions

KeyDescription
Enter
Triggers the onClick callback
Space
Triggers the onClick callback

Component API

ParameterDescription
valueThe current state (ToggleableState.On, ToggleableState.Off, or ToggleableState.Indeterminate)
onClickCallback invoked when the checkbox is clicked
modifierModifier to be applied to the checkbox
backgroundColorBackground color of the checkbox (defaults to Color.Transparent)
contentColorColor of the content inside the checkbox
enabledWhether the checkbox is enabled for interaction (defaults to true)
shapeShape of the checkbox (defaults to RectangleShape)
borderColorColor of the border
borderWidthWidth of the border (defaults to 1.dp)
interactionSourceMutableInteractionSource for handling interactions
indicationVisual indication for interactions
contentDescriptionAccessibility description of the checkbox
checkIconComposable function that receives the state and renders the appropriate icon

Icons

You can find the check and minus icons used in our examples and many other icons at composeicons.com.