The Android documentation for navigation suggests ...
# compose
d
The Android documentation for navigation suggests to use the following code to navigate using a bottom nav bar:
Copy code
onClick = {
            navController.navigate(topLevelRoute.route) {
              popUpTo(navController.graph.findStartDestination().id) {
                saveState = true
              }
              launchSingleTop = true
              restoreState = true
            }
          }
The problem I have with this, is that whenever I go to a screen (even if I've visited it before), it gets recreated with a new viewmodel instance and all state is lost. From my understanding,
saveState
argument should save the state to
SaveStateHandle
? which I'm not using in those viewmodels. But isn't there a way to just move the already existing screen and viewmodel to the front when navigating to it again, therefore them not getting recreated.
Actually there might be another problem that causes this behaviour. When switching between bottom tabs, this log gets printed:
Ignoring popBackStack to destination 1548479155 as it was not found on the current back stack
And clicking on the same tab, relaunches it over the top, even though I have
launchSingleTop
. My setup looks something like this:
Copy code
NavHost
  navigationGraph
    bottomNavComposable
    bottomNavComposable
    bottomNavComposable
This is the full code for the bottom nav:
Copy code
@OptIn(ExperimentalSharedTransitionApi::class)
inline fun <reified T : Any> NavGraphBuilder.bottomNavComposable(
    navController: NavController,
    noinline content: @Composable (AnimatedContentScope.(NavBackStackEntry) -> Unit),
) {
    composable<T> { backStackEntry ->
        val sharedTransitionScope = LocalSharedTransitionScope.current

        Scaffold(
            bottomBar = {
                with(sharedTransitionScope) {
                    CompositionLocalProvider(LocalRippleConfiguration provides null) {
                        NavigationBar(
                            modifier = Modifier.sharedElement(
                                rememberSharedContentState(SharedNavBarKey),
                                animatedVisibilityScope = this@composable
                            ),
                            containerColor = MaterialTheme.colorScheme.background
                        ) {
                            HomeDestinations.entries.forEachIndexed { index, destination ->
                                NavigationBarItem(
                                    colors = NavigationBarItemDefaults.colors().copy(
                                        selectedIndicatorColor = Color.Transparent,
                                        selectedTextColor = MaterialTheme.colorScheme.gold,
                                        selectedIconColor = MaterialTheme.colorScheme.gold,
                                        unselectedTextColor = Color(0xB3FFFFFF),
                                        unselectedIconColor = Color(0xB3FFFFFF)
                                    ),
                                    selected = navController.currentDestination?.hierarchy?.any { it.hasRoute(destination.route::class) } == true,
                                    onClick = {
                                        navController.navigate(destination.route) {
                                            popUpTo(navController.graph.startDestinationId) {
                                                saveState = true
                                            }
                                            launchSingleTop = true
                                            restoreState = true
                                        }
                                    },
                                    icon = {
                                        Icon(
                                            painter = painterResource(destination.icon),
                                            contentDescription = stringResource(destination.label)
                                        )
                                    },
                                    label = {
                                        Text(
                                            text = stringResource(destination.label),
                                            style = MaterialTheme.typography.labelSmall,
                                            letterSpacing = 0.24.sp
                                        )
                                    }
                                )
                            }
                        }
                    }
                }
            }
        ) { contentPadding ->
            Box(Modifier.padding(bottom = contentPadding.calculateBottomPadding())) {
                content(backStackEntry)
            }
        }
    }
}
I uncovered some more info, when I print the graph of the navController in the onClick of the bottom bar item, it prints the root NavGraph not the nested one for some reason... I can get it to use the nested one in the popUpTo like this:
navController.currentBackStackEntry?.destination?.parent?.startDestinationId!!
It now seems to work correctly, but why would it use the root and not the nested NavGraph in the first place?