'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:[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. In this case only the passed view model will be bind to parent store owner, and all other view models created inside will be bind(and so destroyed when route is removed from the stack) to current route.
  2. By proving value for LocalViewModelStoreOwner, so all composables inside will be bind to the parent view model store owner, and so are not gonna be freed when route is removed from the stack.
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>()
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1