Sean
Sean

Reputation: 3015

How to handle popping back multiple screens with Jetpack Compose Navigation

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)
    }
}

Upvotes: 5

Views: 7351

Answers (3)

Juan Camilo Villada
Juan Camilo Villada

Reputation: 11

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
                        }
                    }
                }
            )
        }
...

Upvotes: 1

Sean
Sean

Reputation: 3015

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)

Upvotes: 7

Rofie Sagara
Rofie Sagara

Reputation: 323

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
                        }
                    }

Upvotes: -1

Related Questions