Home > Mobile >  Compose navigation - replace starting route and clear back stack
Compose navigation - replace starting route and clear back stack

Time:10-06

I'm using JetPack Compose with composable NavHost.

I have a scenario where I need a Launch screen that connects bluetooth device so I've set it as my starting route in NavHost.

After connection is done I want to enter Home screen and never get back to that Launch screen.

So after connection is done Launch screen I'm doing this:

   navController.graph.setStartDestination(newHomeRoute)
   navController.navigate(newHomeRoute) {
      popUpTo(0)
      launchSingleTop = true
   }

That doesn't work as I get a constant loop going back to LaunchScreen and forward to Home.

So maybe I should do this in some other way?

CodePudding user response:

I had this exact problem.

Sharing my code here.

SHORT ANSWER,

navHostController.popBackStack("routeOfLaunchingScreen", true)
navHostController.navigate("newHomeRoute")
            

The true denotes that pop back stack till and including the given route. Once the back stack is popped as required, we navigate to the new screen.

Hope this solves your issue. :)

LONG ANSWER (copy-paste solution)

MyNavActions.

class MyNavActions(navHostController: NavHostController) {
    val navigateTo = { navBackStackEntry: NavBackStackEntry, route: String ->
        if (navBackStackEntry.lifecycleIsResumed()) {
            navHostController.navigate(route)
        }
    }

    val navigateUp = { navBackStackEntry: NavBackStackEntry ->
        if (navBackStackEntry.lifecycleIsResumed()) {
            navHostController.navigateUp()
        }
    }

    val popBackStackAndNavigate =
        { navBackStackEntry: NavBackStackEntry, route: String?, popUpTo: String, inclusive: Boolean ->
            if (navBackStackEntry.lifecycleIsResumed()) {
                navHostController.popBackStack(popUpTo, inclusive)
                route?.let {
                    navHostController.navigate(route)
                }
            }
        }
    }
}

/**
 * If the lifecycle is not resumed it means this NavBackStackEntry already processed a nav event.
 *
 * This is used to de-duplicate navigation events.
 */
private fun NavBackStackEntry.lifecycleIsResumed() =
    this.lifecycle.currentState == Lifecycle.State.RESUMED

Usage

val myNavActions = remember(navHostController) {
    MyNavActions(navHostController)
}

Pop back stack till given route and navigate

chcNavActions.popBackStackAndNavigate(
    navBackStackEntry,
    routeToPopUpTo,
    routeToNavigateTo,
    true, // inclusive flag - boolean denoting if the specified route `routeToPopUpTo` should also be popped
)

Back Navigation

chcNavActions.navigateUp(navBackStackEntry)

Simple navigation

chcNavActions.navigateTo(navBackStackEntry, route)

CodePudding user response:

Your navigation code is fine, but looks like you're calling it directly from the view builder.

View builder content can be called many times to recompose the view, that's why you shouldn't change state directly inside composable functions. In this case it happens because of navigation transition animation.

Instead of that you should use side effects. In case when you need to run the code only once, go for LaunchedEffect:

LaunchedEffect(Unit) {
    // your navigation code
}

To better understand what are side effects and how to work with them in Compose, I suggest you start with Thinking in Compose, and after that you can check out side effects documentation.

CodePudding user response:

I managed to do this with extension like this:

fun NavHostController.navigateAndReplaceStartRoute(newHomeRoute: String) {
    popBackStack(graph.startDestinationId, true)
    graph.setStartDestination(newHomeRoute)
    navigate(newHomeRoute)
}

CodePudding user response:

AFAIK you cannot do that in Jetpack Navigation (both for fragment and compose)

  • Related