Issue
I have a single activity app using only composables for the ui (one activity, no fragments). I use one viewmodel to keep data for the ui in two different screens (composables). I create the viewmodel in both screens as described in state documentation
@Composable
fun HelloScreen(helloViewModel: HelloViewModel = viewModel())
Now I noticed that the data that was loaded or set in the first screen is reset in the second.
I also noticed that init{}
is called every time viewModel()
is called. Is this really the expected behavior?
According to the method's documentation it should return either an existing ViewModel or create a new one.
I also see that the view models are different objects. So viewModel()
always creates a new one. But why?
Any ideas what I could be doing wrong? Or do I misunderstand the usage of the method?
Solution
Usually view model is shared for the whole composables scope, and init
shouldn't be called more than once.
But if you're using compose navigation, it creates a new model store owner for each destination. If you need to share models between destination, you can do it like in two ways:
- By passing it directly to
viewModel
call - By proving value for
LocalViewModelStoreOwner
, so all composables inside will have access to the same view model store owner.
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
}
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "first") {
composable("first") {
val model = viewModel<Model>(viewModelStoreOwner = viewModelStoreOwner)
}
composable("second") {
CompositionLocalProvider(
LocalViewModelStoreOwner provides viewModelStoreOwner
) {
val model = viewModel<Model>()
}
}
}
Answered By - Philip Dukhov