Scoping States In Jetpack Compose
Solution 1:
This is precisely what navigation graph scoped view models are used for.
This involves two steps:
Finding the
NavBackStackEntryassociated with the graph you want to scope the ViewModel toPass that to
viewModel().
For part 1), you have two options. If you know the route of the navigation graph (which, in general, you should), you can use getBackStackEntry directly:
// Note that you must always use remember with getBackStackEntry// as this ensures that the graph is always available, even while// your destination is animated out after a popBackStack()valnavigationGraphEntry= remember {
navController.getBackStackEntry("graph_route")
}
valnavigationGraphScopedViewModel= viewModel(navigationGraphEntry)
However, if you want something more generic, you can retrieve the back stack entry by using the information in the destination itself - its parent:
fun NavBackStackEntry.rememberParentEntry(): NavBackStackEntry {
// First, get the parent of the current destination// This always exists since every destination in your graph has a parentval parentId = navBackStackEntry.destination.parent!!.id
// Now get the NavBackStackEntry associated with the parent// making sure to remember itreturn remember {
navController.getBackStackEntry(parentId)
}
}
Which allows you to write something like:
valparentEntry= it.rememberParentEntry()
valnavigationGraphScopedViewModel= viewModel(parentEntry)
While the parent destination will be equal to the root graph for a simple navigation graph, when you use nested navigation, the parent is one of the intermediate layers of your graph:
NavHost(navController, startDestination = startRoute) {
...
navigation(startDestination = nestedStartRoute, route = nestedRoute) {
composable(route) {
// This instance will be the same
val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
}
composable(route) {
// As this instance
val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
}
}
navigation(startDestination = nestedStartRoute, route = secondNestedRoute) {
composable(route) {
// But this instance is different
val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
}
}
composable(route) {
// This is also different (the parent is the root graph)// but the root graph has the same scope as the whole NavHost// so this isn't particularly helpful
val parentViewModel: YourViewModel = viewModel(it.rememberParentEntry())
}
...
}
Note that you are not limited to only the direct parent: every parent navigation graph can be used to provide larger scopes.
Solution 2:
From the Compose and other libraries - Hilt doc
To retrieve the instance of a ViewModel scoped to navigation routes, pass the destination root as a parameter:
val loginBackStackEntry = remember { navController.getBackStackEntry("Parent") }
val loginViewModel: LoginViewModel = hiltViewModel(loginBackStackEntry)
The same can be done without Hilt
val loginBackStackEntry = remember { navController.getBackStackEntry("Parent") }
val loginViewModel: LoginViewModel = viewModel(loginBackStackEntry)
This achieves the same thing acheived by @ianhanniballake but lesser code
Note: The navigation graph has its own route = "Parent"
Full code example
Scoped State Example with Jetpack compose and navigation
// import androidx.hilt.navigation.compose.hiltViewModel// import androidx.navigation.compose.getBackStackEntry@ComposablefunMyApp() {
NavHost(navController, startDestination = startRoute) {
navigation(startDestination = innerStartRoute, route = "Parent") {
// ...
composable("exampleWithRoute") { backStackEntry ->
val parentEntry = remember {navController.getBackStackEntry("Parent")}
val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
ExampleWithRouteScreen(parentViewModel)
}
}
}
}
Post a Comment for "Scoping States In Jetpack Compose"