1

I have a list of items where each has a checkbox. The state of the checkbox needs to be stored in the viewmodel and preferable the list in the viewmodel should be the only source of truth.

@Composable
fun Screen(viewModel: ViewModel) {
    val list by viewModel.items.observeAsState()

    LazyColumn {
        list?.let { items ->
            items(items) { item -> 
                ListItem(
                    text = {
                        Text(item.name)
                    },
                    secondaryText = {
                        Text(item.phoneNumber)
                    },
                    icon = {
                        Checkbox(
                            modifier = Modifier.padding(8.dp),
                            checked = item.selected,
                            onCheckedChange = {
                                //TODO

                            }
                        )
                    }
                )
            }
        }
    }
}

I have tried to update the item.selected by updating the list (MutableLiveData<List<Object>> in viewmodel) in the onCheckedChange callback, but the UI does not update. If i scroll down and then up the UI updates and the checkbox appears to be checked. Why does it not update?

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
OscarCreator
  • 374
  • 4
  • 13

2 Answers2

10

MutableLiveData knows nothing about the object it holds. It cannot send you new value when some inner object property is updated.

To solve this with live data, you need to set new value to you items, setting the same value will be enough:

fun setSelected(index: Int, selected: Boolean) {
    items.value!![index].selected = selected
    items.value = items.value
}

But if you're not bind to LiveData by other dependencies, I suggest you not using it. Using mutableStateListOf with immutable data class is much cleaner:

data class Object(val selected: Boolean)

class VM: ViewModel() {
    val items = mutableStateListOf<Object>()

    fun setSelected(index: Int, selected: Boolean) {
        items[index] = items[index].copy(selected = selected)
    }
}

In both cases, you need the object index, you can get it with itemsIndexed:

val list = viewModel.items
LazyColumn {
    itemsIndexed(list) { index, item -> 
        // ...
        onCheckedChange = {
            viewModel.setSelected(index, it)
        }
    // ...
    }
}
OscarCreator
  • 374
  • 4
  • 13
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
0

In my Case

I were using a mutable list like

MutableLiveData<MutableList<Car>>()

I changed it to a list like this and it worked. :(

MutableLiveData<List<Car>>()