Reputation: 467
In all the examples I have seen with view models in combination with Jetpack Compose, one usually stores a state in the view model as MutableStateFlow and then applies collectAsState in the compose function in order to get a Compose state.
My question: Why not store the state directly in the view model, and not some flow? E.g.
class MyViewModel: ViewModel() {
val showDialog = mutableStateOf(false)
}
@Compose
fun MyScreen(viewModel: MyViewModel) {
Button(onClick = { viewModel.showDialog = true })
if (viewModel.showDialog) {
AlertDialog(...)
}
}
The above code seems to run as intended. Is this a valid solution then?
Upvotes: 27
Views: 26844
Reputation: 1033
I think it is better to use MutableStateFlow
rather than mutableStateOf
of Compose in ViewModel.
MutableStateFlow
is multi-thread safe. You can use MutableStateFlow.update{}
to update it in any thread or any coroutine context. However, mutableStateOf
is not. You should use Snapshot.withMutableSnapshot{}
to guarantee atomic updates in a multi-thread environment. See official document.viewModelScope
is hardcoded to Dispatcher.Main
, so in most case it's OK to use mutableStateOf
.mutableStateOf
directly in ViewModel
. There is no direct way to notify the upstream data source to stop updating when the application is switched to the background. For Flow
Compose provides a extend function collectAsStateWithLifecycle()
, see the blog for details.Of course, if this is a simple program, I think it is acceptable to use Compose state directly in ViewModel (Some official documentation does the same).
Upvotes: 11
Reputation: 465
Using mutableStateOf()
in a view model will work, but this is no longer the best approach.
If you look at the latest version of the official documentation, including the Now in Android reference app, view models should stay data-centric and therefore use the data-centric MutableStateFlow
instead of the Compose-centric mutableStateOf()
.
Android Developer Guide – Foundation: Screen UI state
Android Developer Guide – App Architecture: Mutating the UI state from background threads
Using MutableStateFlow
also makes much more sense from a unit test perspective. When testing a view model in isolation, any dependency on Compose (or other UI-centric packages and classes) should be a red flag. In other words, why would you need any Compose dependencies when testing a view model?
Upvotes: 16
Reputation: 2147
Flow comes from kotlinx.coroutines
it's used to observe async changes, while State is for updating compose UI changes. You're to use Flow regardless of UI if you're following subscribe to stream for changes pattern. Again, you can follow some complicated/incomplete pattern, but why would you?
Upvotes: -2
Reputation: 6817
Yes it certainly is. I don't know where you saw those examples, but this is indeed the recommended practice. You can check the State
Codelab; it demonstrates how to replace the LiveData
objects to mutableStateOf
inside the viewmodel
. Also, as far as the usage of LiveData
and Flow
is concerned, it is mainly for interoperability, as far as I know. The apps which are not fully built in Compose, but are being transferred, or apps which plan to use the view system alongside Compose. mutableStateOf
is only for Jetpack compose and hence, developers will want to use LiveData
in such cases. However, if you are building a brand new project, and want it to be composed of only Compose, then definitely go for what you've mentioned in the question. It is the correct way.
Upvotes: 21