0

I have read the article Why a new ViewModel is created in each Compose Navigation route?

If I create a instance of SoundViewModel with mViewMode: SoundViewModel = viewModel() in compose navigation, it creates a new model store owner for each destination, so I get many instances of SoundViewModel.

If I create a instance of SoundViewModel with mViewMode: SoundViewModel = hiltViewModel() in compose navigation, it will create only one instance of SoundViewModel no matter I invoke mViewMode: SoundViewModel = hiltViewModel() many times, right?

HelloCW
  • 843
  • 22
  • 125
  • 310

1 Answers1

0

As described in this Android Developers article, yes, you can.

If you need to retrieve the instance of a ViewModel scoped to navigation routes or the navigation graph instead, use the hiltViewModel composable function and pass the corresponding backStackEntry as a parameter:

// import androidx.hilt.navigation.compose.hiltViewModel
// import androidx.navigation.compose.getBackStackEntry

@Composable
fun MyApp() {
    NavHost(navController, startDestination = startRoute) {
        navigation(startDestination = innerStartRoute, route = "Parent") {
            // ...
            composable("exampleWithRoute") { backStackEntry ->
                val parentEntry = remember(backStackEntry) {
                    navController.getBackStackEntry("Parent")
                }
                val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
                ExampleWithRouteScreen(parentViewModel)
            }
        }
    }
}

To avoid writing all this code every time I need to share a ViewModel between destinations, I devised a little function to do this for me:

@Composable
inline fun <reified T: ViewModel> NavBackStackEntry.viewModelScopedTo(route: String): T {
    val navController = LocalNavController.current
    val parentEntry = remember(this) { navController.getBackStackEntry(route) }
    return hiltViewModel(parentEntry)
}

Where LocalNavController is a Composition Local that provides NavHostController for the entire app.

You can use the function like this:

navigation(
    startDestination = AppScreen.Persons.route,
    route = AppScreen.Persons.graphDestination
) {
    composable(
        route = AppScreen.Persons.route
    ) {
        PersonsScreen(it.viewModelScopedTo(AppScreen.Persons.graphDestination))
    }
    composable(
        route = AppScreen.AddEditPerson.route,
        arguments = listOf(navArgument("id") {type = NavType.IntType})
    ) {
        AddPerson(
            viewModel = it.viewModelScopedTo(AppScreen.Persons.graphDestination)
        )
    }
}

Both PersonsScreen and AddPerson composables will receive the same instance of a ViewModel.

Calamity
  • 700
  • 7
  • 23