Why a new ViewModel is created in each Compose Navigation route?

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 1:

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:

  1. By passing it directly to viewModel call
  2. 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>()
        }
    }
}