I'm reading about Navigation in Jetpack Compose, and found this example I don't understand.
From the docs:
By using the
saveStateandrestoreStateflags, the state and back stack of that item is correctly saved and restored as you swap between bottom navigation items.
val navController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(navController, startDestination = Screen.Profile.route, Modifier.padding(innerPadding)) {
composable(Screen.Profile.route) { Profile(navController) }
composable(Screen.FriendsList.route) { FriendsList(navController) }
}
}
Specifically, I don't understand how the back stack can be saved if clicking an item in the bottom bar pops the navigation stack to the root.
I would imagine a journey like:
/FriendsList/Friend(A)/Friend(B)/Friend(C)Profile button, resetting the navigation stack to /ProfileFriendsList button.Based on the explanation, I would expect the navigation stack to be re-set to /FriendsList/FriendA/FriendB/FriendC, even though the onClick listener seems to set the stack to /FriendsList?
I really don't understand how this can happen, how does the navigation controller link the route to the entire navigation sub-stack? Is item.route changing state containing the full route to /FriendsList/Friend(A)/Friend(B)/Friend(C), or is something else going on? Or do I understand the example wrong?
I suspect maybe the underlying mechanism is that FriendsList contains a nested navigation graph, since the example doesn't actually show any Friend route definitions. The state of this entire nested graph is contained somehow, i.e., something like /FriendsList{FriendA/FriendB/FriendC}, and a move to /FriendsList will unpack this navigation stack. Is that kind of how it works?
The code in the example you gave has only 1 navHost and 1 navController so it is not possible to save the FriendList back stack seperated from the rest back stack. To achive this behavior you need to declare nested navHosts. Each of them with his own navController and thats it. All the rest is working automatically.
Here is an example
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
Scaffold(
bottomBar = {
BottomNavigation {
items.forEach { screen ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(stringResource(screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
) { innerPadding ->
NavHost(
navController,
startDestination = Screen.ProfileNavHost.route,
Modifier.padding(innerPadding)
) {
composable(Screen.ProfileNavHost.route) {
val profileNavController = rememberNavController()
NavHost(
navController = profileNavController,
startDestination = Screen.ProfileRoot.route
) {
composable(Screen.ProfileRoot.route) {
ProfileRoot(navController)
}
composable(Screen.ProfileScreen.route) {
ProfileScreen(navController)
}
}
}
composable(Screen.FriendsListNavHost.route) {
val friendsListNavController = rememberNavController()
NavHost(
navController = friendsListNavController,
startDestination = Screen.FriendsListRoot.route
) {
composable(Screen.FriendsListRoot.route) {
FriendsList(navController)
}
composable(Screen.FriendsListFriend.route) {
FriendsListFriend(navController)
}
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With