androiduser-interfaceandroid-jetpack-composeviewmodelkotlin-stateflow

Attribute of an object is not updated when changing the state with StateFlow in Compose


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

Solution

  • 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))