5

I'll try to do some ASCII art to describe the problem:

       <--------------------------------------\
DestinationA --> DestinationC ---------> DestinationE
DestinationB ------/    \-----> DestinationD --/

I hope that's decipherable. C can be reached from destinations A and B. E can be reached from C and D. E returns to either A or B (whichever is in the back stack). Destinations C, D, and E take an argument (id).

What is the best way to implement this? Using nested navigation graphs looks like it might be possible.

The following works, but it feels more like a work-around than how the navigation component is intended to work.

val destination = navController.getBackStackEntry("DestinationC/{id}").destination
navController.popBackStack(destination.id, true)

The usage NavHost is currently:

val navController = rememberNavController()
NavHost(navController = navController, startDestination = "DestinationA") {
    compose("DestinationA") {
        ScreenA(hiltNavGraphViewModel(it))
    }
    compose("DestinationB") {
        ScreenB(hiltNavGraphViewModel(it))
    }
    compose("DestinationC/{id}", arguments = listOf(navArgument("id") { type = NavType.StringType })) {
        val viewModel = hiltNavGraphViewModel(it)
        val id = it.arguments?.getString("id")
        viewModel.setId(id)
        ScreenC(viewModel)
    }
    compose("DestinationD/{id}", arguments = listOf(navArgument("id") { type = NavType.StringType })) {
        val viewModel = hiltNavGraphViewModel(it)
        val id = it.arguments?.getString("id")
        viewModel.setId(id)
        ScreenD(viewModel)
    }
    compose("DestinationE/{id}", arguments = listOf(navArgument("id") { type = NavType.StringType })) {
        val viewModel = hiltNavGraphViewModel(it)
        val id = it.arguments?.getString("id")
        viewModel.setId(id)
        ScreenE(viewModel)
    }
}
Sean
  • 2,632
  • 2
  • 27
  • 35
  • `DestinationA ` & `DestinationB` are supposed to be two different `startDestination` ? If yes then what's component used bottomNav, Tabs or something else? – Mayur Gajra May 01 '21 at 12:35
  • I'll admit to not understanding navigation very deeply. Maybe `DestinationA` and `DestinationB` should different `startDestination`, but they aren't. I have a flat `NavHost` with all of the destinations in it, `DestinationA` is the `startDestination`. I'm using a navigation drawer. Does that change how I should design the navigation? If so, how? Thank you. – Sean May 01 '21 at 13:03
  • Can you please post your current nav graph & also SS would help like. Where `DestinationA` & `DestinationB` belong? Are they direction item of the drawer? – Mayur Gajra May 01 '21 at 13:12
  • Yes, DestinationA and DestinationB are top level destinations in the drawer. The nav graph is completely uninteresting. I'll edit the question to format it decently. – Sean May 01 '21 at 13:59

3 Answers3

6

The answer from @rofie-sagara did not work for me. There is a navigation extension that supports routes. I think nested navigation is an unrelated topic. The docs don't really explain why nested navigation is actually useful. My final solutions to move from E back to A or B is:

navigation.popBackStack(route = "DestinationC/{id}", inclusive = true)
Sean
  • 2,632
  • 2
  • 27
  • 35
0

Use popUpTo with the desired destination, not with the current nested nav graph, this will remove all the destinations until the destination with a matching route is found

In the below example all the routes from the nested graph up to the NavigationGraph.Welcome.route inluded are removed

NavHost(
    navController = navController,
    startDestination = NavigationGraph.Welcome.route
) {
    composable(
        route = NavigationGraph.Welcome.route
    ) {
        WelcomeScreen { destination ->
            navController.navigate(destination)
        }
    }
    navigation(
        startDestination = LoginNavigationRoute.RefCode.route,
        route = NavigationGraph.Login.route
    ) {
        ...
        composable(
            route = LoginNavigationRoute.SignIn.route
        ) {
            SignInScreen(
                onSuccessfulLogin = {
                    navController.navigate(NavigationGraph.Main.route) {
                        popUpTo(NavigationGraph.Welcome.route) {
                            inclusive = true
                        }
                    }
                }
            )
        }
...
-1

Using nested navigation graphs Make DestinationC and DestinationE on diff navigations.

val navController = rememberNavController()
NavHost(navController = navController, startDestination = "DestinationA") {
    compose("DestinationA") {
        ScreenA(hiltNavGraphViewModel(it))
    }
    compose("DestinationB") {
        ScreenB(hiltNavGraphViewModel(it))
    }
    navigation("DestinationC".plus("/{id}"), "DestinationC".plus("_Route")) {
      compose("DestinationC/{id}", arguments = listOf(navArgument("id") { type = NavType.StringType })) {
          val viewModel = hiltNavGraphViewModel(it)
          val id = it.arguments?.getString("id")
          viewModel.setId(id)
          ScreenC(ViewModel)
      }
    }
    compose("DestinationD/{id}", arguments = listOf(navArgument("id") { type = NavType.StringType })) {
        val viewModel = hiltNavGraphViewModel(it)
        val id = it.arguments?.getString("id")
        viewModel.setId(id)
        ScreenD(viewModel)
    }
    navigation("DestinationE".plus("/{id}"), "DestinationE".plus("_Route")) {
      compose("DestinationE/{id}", arguments = listOf(navArgument("id") { type = NavType.StringType })) {
          val viewModel = hiltNavGraphViewModel(it)
          val id = it.arguments?.getString("id")
          viewModel.setId(id)
          ScreenE(ViewModel)
      }
   }
}

example you want to move from C to E and popUpTo A.

navController.navigate("DestinationE".plus("/${data.id}")) {
                        popUpTo("DestinationA") {
                            inclusive = false
                        }
                    }
Rofie Sagara
  • 289
  • 5
  • 17
  • Can you explain this? What would the navigation code look like? – Sean May 03 '21 at 12:11
  • From DestinationE, I want to pop back to either A or B, whichever is the one on the stack. I don't want to pop DestinationC off the stack when navigating to DestinationE, because you should still be able to navigate back from E to C. – Sean May 04 '21 at 13:09
  • popUpTo DestinationA it will skip DestinationC – Rofie Sagara May 04 '21 at 13:19
  • What if DestinationA isn't on the backstack? There's no guarantee that it is. That was my point. – Sean May 04 '21 at 17:33
  • This solution misses the point of my question. Navigation can arrive at E with B on the stack, but not A. In which case `popUpTo("DestinationA")` fails. Also, I don't want to remove C from the stack when navigating to E, because it should be possible to go back to C. Thank you for your answer. Maybe it will be useful to someone attempting to do what you mentioned. – Sean Jul 19 '21 at 10:43