One Off the Slack: Why Does My List Not Recompose?

Colton Idle has this code:

class ScreenAState {
    val people = mutableStateListOf<PersonUIWrapper>()
}

data class PersonUIWrapper(var selected: Boolean, val person: Person)

…as well as:

vm.state.people.forEach {
    TextButton(onClick = {
    it.selected = true
}) {
  if (it.selected) {
      Icon(Icons.Default.Check, null)
  }

  Text(text = it.person.name)
}

Colton’s problem was:

but my code doesn’t recompose, and therefore doesn’t show a checkmark. Is my PersonUIWrapper supposed to use snapshot state internally for it’s two fields?

In general, an ordinary var is unlikely to do what you want, particularly with Compose. In this case, Colton is modifying selected, and nothing in the system really cares. It is just a var, and a var is not special. Nothing is paying attention to that var to do anything when it is changed.

Or, as Joseph-Hawkes-Cates put it:

the mutableStateList will recompose on changes to the list itself rather than changes to the internal state of the values in the list

Slack user nitrog42 pointed to this similar Stack Overflow question.

Google’s Alex Vanyo offered:

To partially answer your original question, if PersonUIWrapper was backed by state that was observable to Compose (like mutableStateOf), then this would work like you’d expect.

And that can be preferable as an alternative to the .copy solution with a data class.

Colton adapted his wrapper to be:

class PersonUIWrapper(val selected: MutableState<Boolean> = mutableStateOf(true), val person: Person)

…but he was not completely happy:

It kind of sucks that I can’t use by in this case, but using .value isn’t the worst thing!

Google’s Adam Powell then offered this approach:

class PersonUIWrapper(
  selected: Boolean,
  val person: Person
) {
  var selected by mutableStateOf(selected)
}

Here, selected is both a constructor parameter and a var property backed by the mutableStateOf() property delegate. The constructor parameter is used for the initial value of the property. Now, you can refer to selected like an ordinary var, while it still is a MutableState and participates in the recomposition system.


Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!