8

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?

miszmaniac
  • 825
  • 2
  • 10
  • 21

4 Answers4

8

I managed to do this with extension like this:

fun NavHostController.navigateAndReplaceStartRoute(newHomeRoute: String) {
    popBackStack(graph.startDestinationId, true)
    graph.setStartDestination(newHomeRoute)
    navigate(newHomeRoute)
}
miszmaniac
  • 825
  • 2
  • 10
  • 21
  • If anyone else gets a bouncing issue where it infinitely alternates between start destination and replaced destination, removing the `setStartDestination`, as with @abhimanyu 's short answer, might suit you better – Jarrod Moldrich Jan 24 '22 at 22:17
  • Hi @Jarrod, you want to say that we just need to replace setStartDestination with Abhimanyu's short answer which is 2 lines of code? – Rahul Bhavani Mar 30 '22 at 12:03
  • Unfortunately, this doesn't seem to work for me. I'm using accompanist version 0.23.1. I see constant blinking – sha Jun 21 '22 at 00:47
6

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)
Abhimanyu
  • 11,351
  • 7
  • 51
  • 121
4

You can use this extension:

fun NavHostController.navigateAndClean(route: String) {
    navigate(route = route) {
        popUpTo(graph.startDestinationId) { inclusive = true }
    }
    graph.setStartDestination(route)
}
Daniele
  • 1,030
  • 9
  • 20
1

This worked for me

navController.navigate(
            route = "new_destination", navOptions = NavOptions.Builder()
                .setPopUpTo("new_destination", inclusive = true)
                .build()
        )
mikail yusuf
  • 197
  • 3
  • 5