Search code examples
android-jetpack-composeandroid-livedataandroid-jetpacklazycolumn

Android Compose lazycolumn does not update when livedata is changed


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?


Solution

  • 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)
            }
        // ...
        }
    }