Android Jetpack Compose mutableStateListOf not doing Recomposition
So, I have a mutableStateListOf
in viewModel
:
var childTravellersList = mutableStateListOf<TravellersDetails>()
TravellersDetails
is a data class having a field called error
.
this childTravellersList
is used in the UI as:
val list = remember{viewModel.childTravellersList}
LazyColumn(state = lazyColumnState) {
itemsIndexed(list) { index, item ->
SomeBox(show = if(item.error) true else false)
}
}
I have wrote a function in viewModel
that updates error
of TravellersDetails at given index
of childTravellersList
as:
fun update(index){
childTravellersList[index].error = true
}
So, whenever I call this function, the list should get updated.
This updates the list, but UI recomposition is not triggered 😕. Where am I doing wrong?
Solution 1:
mutableStateListOf
can only notify about adding/removing/replacing some element in the list. When you change any class inside the list, the mutable state cannot know about it.
Data classes are very good for storing immutable state in unidirectional data flow, because you can always "change" it with copy
, while you see the need to pass the new data to view or mutable state. So avoid using var
variables with data classes, always declare them as val
to prevent such errors.
var childTravellersList = mutableStateListOf<TravellersDetails>()
fun update(index){
childTravellersList[index] = childTravellersList[index].copy(error = true)
}
An other problem is that you're using val list = remember{viewModel.childTravellersList}
: it saves the first list value and prevents updates in future. With ViewModel you can use it directly itemsIndexed(viewModel.childTravellersList)
Solution 2:
Recomposition will happen only when you change the list itself. You can do it like this.
var childTravellersList by mutableStateOf(emptyList<TravellersDetails>())
fun update(indexToUpdate: Int) {
childTravellersList = childTravellersList.mapIndexed { index, details ->
if(indexToUpdate == index) details.copy(error = true)
else details
}
}
Also, you need not remember this list in you composable as you have done here val list = remember{viewModel.childTravellersList}
. Since the MutableState is inside view model it will always survive all recompositions. Just use the viewModel.childTravellersList
inside LazyColumn.