Matthew Laser
02/10/2022, 5:44 PMViewModel , along with a data class MyUIState. I’m initializing my property as such: var uiState by mutableStateOf(MyUiState.empty). , where empty is just a default initializer with default values.
I’m then updating individual properties of uiState as events happen, for instance uiState.loading = true
The `ViewModel`s is passed into my composable MyScreen as such from MyActivity
setContent { MyScreen(viewModel) }
Finally, inside of my composable MyScreen I'm doing val uiState = viewModel.uiState and driving changes off of those values, for instance if (uiState.loading) - however, this is the point at which they aren't recomposed.
Most of the examples I can find use a very simple value like mutableStateOf(true), so I'm assuming there is something going on where just updating a property isn't having the right effect.Casey Brooks
02/10/2022, 5:50 PMmutableStateOf container. Mutable objects, in general, are going to cause lots of problems for you in Compose.
Instead of having your state have var properties, they should all be val (thus making the class immutable), and you copy the class to change its values, re-assigning it to uiState. Or better yet, use StateFlow to hold your state in the ViewModel and use .collectAsState() to avoid coupling the ViewModel directly to ComposeMatthew Laser
02/10/2022, 6:21 PMMatthew Laser
02/10/2022, 6:22 PMFlow in this basic case?Rick Regan
02/10/2022, 6:41 PMuiState is changed. Presumably, the somethingRelatedToBusinessLogic() function is doing what Casey said ("_copy the class to change its values, re-assigning it to `uiState`_").Casey Brooks
02/10/2022, 6:41 PMmutableStateOf() and StateFlow in the ViewModel will do the same thing, functionally. It's more a matter of semantics, keeping the ViewModel as something that is isolated from the UI and doesn't even know about anything Compose, so that it will still work in something like a Unit Test that isn't running Compose. StateFlow is a more general concept and offers a few extra APIs like .update { } that make it more suitable in a ViewModel, when it may get updated from multiple coroutines/threads concurrentlyMatthew Laser
02/10/2022, 6:44 PMuiState.loading = true or uiState = uiState.copy(loading = true) don't trigger a recompositionMatthew Laser
02/10/2022, 6:45 PMMatthew Laser
02/10/2022, 6:49 PMCasey Brooks
02/10/2022, 6:55 PMMatthew Laser
02/10/2022, 6:56 PMcopy function out of this documentationRick Regan
02/10/2022, 6:59 PMuiState = uiState.copy(loading = true) to trigger recomposition. You might have to show more of your code.Casey Brooks
02/10/2022, 7:01 PMuiState = uiState.copy(loading = true) should trigger recomposition, because you're changing the value of the mutableStateOf or StateFlow itself. But uiState.loading = true will not, because it's only mutating the state object in-place, which neither Compose nor StateFlow is tracking the individual properties ofMatthew Laser
02/10/2022, 7:07 PMMatthew Laser
02/10/2022, 7:10 PMDialog like so:
@Composable
fun MyScreen(viewModel: ViewModel) {
val uiState = viewModel.uiState
MyAppTheme {
if (uiState.loading) {
LoadingDialog()
}
// the rest of my screen
...
}
}Matthew Laser
02/10/2022, 7:11 PMuiState is getting updated via the copy call by setting the loading value to the value of a Text and watched it change the string printed to screenMatthew Laser
02/10/2022, 7:11 PMif check, as in:
// if (uiState.loading)
LoadingDialog()
// }
the LoadingDialog displays as desiredMatthew Laser
02/10/2022, 7:12 PMif isn't somehow being marked as dirty or something that drives recompositionMatthew Laser
02/10/2022, 7:12 PMRick Regan
02/10/2022, 8:40 PMuiState is getting updated with new state that has loading = true but it's hard to reconcile that with LoadingDialog() not running. If you could post a minimal example with the exact way you update maybe we could figure it out ...Matthew Laser
02/10/2022, 8:41 PM