11

According to this example I implemented shared viewModels in a nested navigation graph.

Setup

Nested Graph:

private fun NavGraphBuilder.accountGraph(navController: NavHostController) {
   navigation(
      startDestination = "main",
      route = "account") {

       composable("main") {
          val vm = hiltViewModel<AccountViewModel(navController.getBackStackEntry("account"))
          //... ui ...
       }

       composable("login") {
          val vm = hiltViewModel<AccountViewModel(navController.getBackStackEntry("account"))
          //... ui ...
       }
   }
}

NavHost:

@Composable
private fun NavHost(navController: NavHostController, modifier: Modifier = Modifier){
   NavHost(
      navController = navController,
      startDestination = MainScreen.Home.route,
      modifier = modifier
   ) {
      composable("home") { HomeScreen(hiltViewModel()) }
      composable("otherRoute") { OtherScreen(hiltViewModel()) }
      accountGraph(navController)
   }
}

BottomNavBar:

@Composable
private fun ButtonNav(navController: NavHostController) {
   BottomNavigation {
      val navBackStackEntry by navController.currentBackStackEntryAsState()
      val currentDestination = navBackStackEntry?.destination

      items.forEach { screen ->
         BottomNavigationItem(
            icon = { ... },
            label = { ... },
            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
                  navController.graph.startDestinationRoute?.let { route ->
                     popUpTo(route) { saveState = true }
                  }

                  // Avoid multiple copies of the same destination when
                  // re-selecting the same item
                  launchSingleTop = true

                  // Restore state when re-selecting a previously selected item
                  restoreState = true
               }
            }
         )
      }
   }
}

Problem

With this setup if I naviagte to "account" (the nested graph) and back to any other route I get the error:

java.lang.IllegalArgumentException: No destination with route account is on the NavController's back stack. The current destination is Destination(0x78dd8526) route=otherRoute

Assumptions / Research Results

BottomNavItem

The exception did not occure when I remove the popUpTo(route) onClick. But then I ended up with a large stack.

lifecycle of backStackEntry

Have a look at the following:

//...
composable("main") { backStackEntry ->
   val vm = hiltViewModel<AccountViewModel(navController.getBackStackEntry("account"))
   //... ui ...
}
//...

I found out when navigating back the composable which will be left will be recomposed but in this case the backStackEntry seams to have another lifecycle.currentState because if I wrap the whole composable like this:

//...
composable("main") { backStackEntry ->
  if(backStackEntry.lifecycle.currentState == Lifecycle.State.RESUMED){
     val vm = hiltViewModel<AccountViewModel(navController.getBackStackEntry("account"))
     //... ui ...
  }
}
//...

... the exception did not occure. The idea with the lifecycle issue came into my mind when I saw that the offical example has similar workarounds in place.

Summary

I actually do not know if I did something wrong or if I miss a conecept here. I can put the lifecycle-check-workaround into place but is this really as intended? Additional to that I did not find any hint in the doc regarding that.

Does anybody know how to fix that in a proper way?

Regards, Chris

3
  • I have the same problem as yours regarding multiple nested graph and the workaround you provided avoids the crash. But I also noticed that when navigating to the composable with issue, it takes a couple of millis to show up the composable. Commented Aug 30, 2021 at 14:47
  • Really comforting that I'm not the only one with this issue. :) Commented Sep 2, 2021 at 5:25
  • 1
    Same issue here!! so happy I found this. Commented Apr 9, 2022 at 21:25

2 Answers 2

2

This is how you do it now but make sure you have the latest compose navigation artefacts:

private fun NavGraphBuilder.accountGraph(navController: NavHostController) {
   navigation(
      startDestination = "main",
      route = "account") {

       composable("main") {
          val parentEntry = remember {
            navController.getBackstackEntry("account")
          }
          val vm = hiltViewModel<AccountViewModel(parentEntry)
          //... ui ...
       }

       composable("login") {
          val parentEntry = remember {
            navController.getBackstackEntry("account")
          }
          val vm = hiltViewModel<AccountViewModel(parentEntry)
          //... ui ...
       }
   }
}
Sign up to request clarification or add additional context in comments.

1 Comment

nop, this doesn't. I have it like this way before comming to this post, I have same issues with nested graphs.
1

There was an issue with the navigation component. It has been fixed for me with v2.4.0-alpha08

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.