I'm implementing a screen that contains a LazyColu...
# compose
k
I'm implementing a screen that contains a LazyColumn with items that have a checkbox in each of them. I want to have all the checkboxes disabled until the user makes an action (selects an option from separate dropdown menu). The problem is that when I update a checkbox state to enabled=true, the checkbox doesn't update until it is scrolled away from the screen and then back again. I made a simplified version (see thread below) with a single checkbox that is initially disabled and a button that enables it. Pressing the button, however, does not update the checkbox (it is not recomposed?). If I set the checkbox enabled initially and change the button to update the checked state, it works. Is there something special about the enabled parameter that I'm not getting or is there a bug?
Copy code
data class State(
    val checked: Boolean,
    val enabled: Boolean
)

@Composable
fun CheckboxTest() {
    var state by remember { mutableStateOf(State(checked = false, enabled = false)) }

    Column(modifier = Modifier.padding(16.dp)) {
        OutlinedButton(onClick = { state = state.copy(enabled = true) }) {
            Text("Enable")
        }

        Checkbox(
            checked = state.checked,
            enabled = state.enabled,
            onCheckedChange = { state = state.copy(checked = !state.checked) },
            modifier = Modifier.padding(end = 16.dp)
        )
    }
}
j
Your state object does not have observable fields, so Compose has no way of knowing (being notified) when it has been changed. You can model your state object like this:
Copy code
class State() {
    var checked: Boolean by mutableStateOf(false)
    var enabled: Boolean by mutableStateOf(false)
}
k
But if I set initial value of enabled to true (in the first line of the composable function) and change the on click of outlined button to set checked to true (instead of updating enabled), the checkbox composabe will update just fine. So the code works when changing checked but not when changing enabled.
r
If you add this to your CheckBox
Copy code
colors = CheckboxDefaults.colors(
        checkedColor = Color.Green,
        uncheckedColor = Color.Blue,
        checkmarkColor = Color.Black,
        disabledColor = Color.Red
)
and then click the enable button and then the checkbox, you'll get a black check in a red outlined box. If you change the line
enabled = state.enabled
to
enabled = true
(so no need to click enable button) you'll get a black check in a solid green box when you click the checkbox. I have no idea what that means.
k
There is definitely something weird going on with the checkbox. I think the state works fine in my example but the data class is remnant from the more complex version so I created a new version with just two mutable state variables:
Copy code
@Composable
fun CheckboxTest() {
    var checked by remember { mutableStateOf(false) }
    var enabled by remember { mutableStateOf(false) }

    Column(
        modifier = Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {

        Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
            Button(onClick = { enabled = true }) {
                Text("Enable")
            }

            Button(onClick = { enabled = false }) {
                Text("Disable")
            }
        }

        Checkbox(
            checked = checked,
            enabled = enabled,
            onCheckedChange = { checked = !checked },
            modifier = Modifier.padding(end = 16.dp)
        )

        Checkbox(
            checked = checked,
            enabled = enabled,
            onCheckedChange = { checked = !checked },
            modifier = Modifier.padding(end = 16.dp),
            colors = CheckboxDefaults.colors(
                checkedColor = Color.Green,
                uncheckedColor = Color.Blue,
                checkmarkColor = Color.Black,
                disabledColor = Color.Red
            )
        )

        Text(text = "Enabled $enabled, Checked: $checked")
    }
}
Here is also a screenshot after pressing enable button and clicking one of the checkboxes (which have the same state so they both should be checked). I'll file a bug after work later today.
r
If you add colors to the first checkbox as well you will see they will both be checked...
k
Yeah, it seems that the checkboxes are working (i.e., are clickable when enabled) but they are rendered incorrectly or something. If I set in the code the initial state of the checkboxes to be enabled and then check one of them, they both show a checkmark and the second checkbox has green color. So the state is the same as in the picture but the checkboxes look completely different.
r
Yes. Very strange. Red (disabled color) shouldn't be coming into play when the box is enabled. Good luck describing the bug :)
k
a
Very interesting case indeed. The problem seems to be somewhere in the boxColor and borderColor functions, which are called from the CheckboxImpl and return either mutable state or AnimatedState. Upon first recomposition with a different value of enabled argument everything just breaks.