Issue
In all applications there will always be this three scopes of state:
With Compose, a "Per Screen State" could be achieved by:
NavHost(navController, startDestination = startRoute) {
...
composable(route) {
...
val perScreenViewModel = viewModel() // This will be different from
}
composable(route) {
...
val perScreenViewModel = viewModel() // this instance
}
...
}
The "App State" could be achieved by:
val appStateViewModel = viewModel()
NavHost(navController, startDestination = startRoute) {
...
}
But how about for "Scoped State"? How could we achieve it in Compose?
Solution
This is precisely what navigation graph scoped view models are used for.
This involves two steps:
Finding the
NavBackStackEntry
associated 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()
val navigationGraphEntry = remember {
navController.getBackStackEntry("graph_route")
}
val navigationGraphScopedViewModel = 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 parent
val parentId = navBackStackEntry.destination.parent!!.id
// Now get the NavBackStackEntry associated with the parent
// making sure to remember it
return remember {
navController.getBackStackEntry(parentId)
}
}
Which allows you to write something like:
val parentEntry = it.rememberParentEntry()
val navigationGraphScopedViewModel = 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.
Answered By - ianhanniballake
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.