How to restore state with nested NavHostController in android compose

40 views Asked by At

In my current project I have the problem that the first restore state attempt (triggered via popBackStack) failed. The navigation after that first failure is working fine and the app manages to restore all states afterwards fine.

//  MainActivity
val appCoordinator = AppCoordinator(rememberNavController())
val mainCoordinator = MainCoordinator()
NavHost(
    navController = appCoordinator.getController(),
    startDestination = "MAIN",
) {
    composable("MAIN") {
        MainView(appCoordinator, mainCoordinator)
    }
    composable("SECOND"){
        SecondView(appCoordinator)
    }
}

// MainView basically
mainCoordinator.setNavController(rememberNavController())
Scaffold(
    bottomBar = { BottomNavigation(mainCoordinator) }
) { padding ->
    NavHost(
        modifier = Modifier.padding(padding),
        navController = mainCoordinator.getNavController(),
        startDestination = mainCoordinator.getStartDestination().route
    ) {
        composable("MAIN-FEED") { FeedView() }
        composable("MAIN-TEST") { TestView(appCoordinator) }
    }
}

// Main Coordinator when some bottom nav icon is pressed
navController.navigate(bottomNavScreen.route) {
    launchSingleTop = true
    restoreState = true
    popUpTo(findStartDestination(navController.graph).id) {
        saveState = true
    }
}

// App Coordinator
//when "go to second" button on TestView "MAIN-TEST" is pressed
navController.navigate("SECOND")
//when back back button on SECOND Component is pressed
navController.popBackStack()

In my FeedView I have a Column with multiple LazyRows. So I can scroll up or down and also within a LazyRow sideways. When switching with the bottom nav icons I can at some point go back to the FeedView and the scroll position for the column and the lazyRows are the same. But when I open the SECOND component and go back to MAIN component the FeedView is not shown. When switching to TestView and back to FeedView the view is shown but with all scroll positions reset. But when doing the same procedure again everything works like charm.

Does someone has an idea why and how to fix that?

1

There are 1 answers

0
Carlo-Rodriguez On BEST ANSWER

After struggling a lot I found the solution (or maybe the mistake). In the MainCoordinator I was storing the startDestination of the MainView. In the NavHost of MainView I was using:

startDestination = mainCoordinator.getStartDestination().route

This was needed to get back to the correct tab of the MainView when coming from a different App Screen (parent NavHost). But this code:

popUpTo(findStartDestination(navController.graph).id) { saveState = true }

is already taking care of this and the additional storing of the startDestination leads to the follwing behaviour.

The first time the parent navController (AppCoordinator) switches, every NavBackStackEntry of child navController (MainCoordinator) that comes before the new startDestination will be removed (including MAIN-FEED). The second time the parent navController (AppCoordinator) switches, the startDestination wont be changed as it already is MAIN-TEST. So the FEED NavBackStackEntry wont be removed.

Long story short: I changed the startDestination of the NavHost in MainView to always be the same (in this case MAIN-FEED).