i have an object which hold the state of my ui which looks like this:
data class MainActivityUiState(
/*** rest **/
val usersData : ArrayList<Users>
)
i intailize it like this in the view model
private val _uiState = MutableStateFlow(
MainActivityUiState(
/*** rest ***/
usersData = ArrayList()
)
)
var uiState = _uiState.asStateFlow()
then when the changes happen i do this:
val listToAdd = _uiState.value.usersData
listToAdd.add(one)
_uiState.value = _uiState.value.copy(
usersData = listToAdd
)
i put a loging statement before and after so i make sure the size is adding up , and it is.
in the activity , i collect the state like this:
uiState = viewModel.uiState.collectAsState()
and the link is working because i have other states in the uistate which when changes , get captured by the ui
here how i catch the state changes for the array list
LazyColumn(
modifier = Modifier.padding(innerPadding)
.background(Color.White)
.fillMaxSize()
) {
items(uiState.value.usersData ){ match ->
Text(match.name)
}
}
i also tried adding items directly to the ui state like this inside the viewmodel:
_uiState.value.usersData.add(one)
which didn't work
Your UI doesn't update because the value of the StateFlow never changes.
A StateFlow only emits a new value when the new value you set is not equal to the previous value (i.e. !=
). Let's take a look at what happens when you update the value:
_uiState.value = _uiState.value.copy(
usersData = listToAdd
)
Although you create a new MainActivityUiState
object, its usersData
poperty is identical: Both hold the exact same ArrayList object. From that follows that both MainActivityUiState
objects are equal, so the StateFlow never updates. Please note that adding a new value to the list doesn't make it a different list: It is still the same list, just the content is different.
This is a subtle bug which can be effectively prevented if you avoid using mutable lists in the first place. In your case that means you should replace your ArrayList
with a simple List
:
data class MainActivityUiState(
/*** rest **/
val usersData: List<Users>,
)
Now the compiler prevents you to do the following so you don't fall into the trap that you think that changing the list will change the StateFlow's value:
listToAdd.add(one)
Instead you need to create a new list when you want to add the item. You can simply use the +
operator for that. Updating the flow's value would then look like this:
_uiState.update {
it.copy(usersData = it.usersData + one)
}
Please note that I use the update
method here instead of setting the value
property. That has nothing to do with your initial problem, but it prevents another issue that could occur when a different thread changes the flow's value while you calculate the new value.