I followed the last Google tutorials to introduce Compose/ViewModel/State in a new project, but I encounter a problem that I don't understand. When I use a method in Viewmodel to update an object from null to new instance, UI is updated, but when I use the same method to update just a parameter of this object, the modification is not visible.
Here the code ViewModel
data class AppOscarUiState(
val selectedStep: Step? = null
)
class AppViewModel() : ViewModel(){
private val _uiState = MutableStateFlow(AppUiState())
val uiState: StateFlow<AppUiState> = _uiState.asStateFlow()
fun updateSelectedStep(newStep: step){
_uiState.update { currentState ->
currentState.copy(selectedStep = newStep)
}
// also tried _uiState.value = _uiState.value.copy(selectedStep = newStep)
}
}
And in the Composable
fun CardDetail(
appViewModel: AppViewModel
) {
val appUiState by appViewModel.uiState.collectAsState()
Column(
Modifier
.fillMaxSize()
.padding(horizontal = 16.dp, vertical = 8.dp),
) {
Text(
text = appUiState.selectedStep!!.status,
)
OutlinedButton(
onClick = {
selectedStep!!.status = 16
appViewModel.updateSelectedStep(selectedStep)
},
) {
Text(
stringResource(R.string.it_starts),
)
}
}
When the step is selected from a list, updateSelectedStep(newStep)
from the viewmodel is called and a detail container is filled. And when I want to change a parameter, the same is done. A log in updateSelectedStep(newStep)
indicates that the new value is well transmetted, and when the step is deselected and selected again, the new data is visible.
Step is a data class.
So why the modification is not instantaneous ? I have a similar method to update a boolean (not an object) which works fine.
Thanks for your help
You pass the same object to currentState.copy(selectedStep = newStep)
- you can log object address to see it - from Compose perspective it means that object hasn't changed, so no recomposition is needed.
One option is to define status
as mutableStateOf
, in this case you won't need too update state with copy
:
var status by mutableStateOf(0)
But if you wanna split your code into view/data layers for better testability/to make it cleaner, you shouldn't use var
for properties you wanna update, and use copy
on all levels:
appViewModel.updateSelectedStep(selectedStep!!.copy(status = 16))