Compose-Navigation: Remove previous composable from stack before navigating - android

I'm using compose-navigation(alpha09) to handle the navigation between composables
I want to remove the Splash screen when moving to the next destination (I don't want the back pressed to get back to Splash)
Following attempts did not work as expected:
navHostController.navigate(Route.login.id) {
navHostController.graph.clear()
}
navHostController.navigate(Route.login.id)
navHostController.graph.clear()
val currentDest = navHostController.currentDestination
navHostController.navigate(Route.login.id)
if (currentDest != null) {
navHostController.graph.remove(currentDest)
}
So how can I remove the Splash screen and then move to next?

In Jetpack Compose 1.0.0-rc01 to navigate and remove previous Composable from back stack You can use:
navController.navigate(Screens.Login.name) {
popUpTo(Screens.Splash.name) {
inclusive = true
}
}
The above code will navigate from the Splash screen to Login and will pop everything up, including the Splash screen.
Navigate to a composable - docs

For v1.0.0-alpha09 (And 1.0 stable)
Using popUpTo(0) you can clear the stack before navigating to the next destination. So:
navHostController.navigate(Route.login.id) {
// popUpTo = 0 // DEPRECATED
popUpTo(0)
}

For a consistent reusable function that does not need to be aware of the current route, use this NavOptionsBuilder extension function
fun NavOptionsBuilder.popUpToTop(navController: NavController) {
popUpTo(navController.currentBackStackEntry?.destination?.route ?: return) {
inclusive = true
}
}
^ Similar to other answers, it popUpTo the current route, but rather than needing to name the specific current route, it instead gets it from the backstack entry.
Now you can use it like so:
navController.navigate(ScreenRoutes.Login.route) { popUpToTop(navController) }
^ That example navigates to Login, and should clear the entire backstack before it.

For clearing all back stack
To remove multiple composable screens from the stack use the below snippet
navController.navigate(ScreenRoutes.Login.route){
popUpTo(navController.graph.findStartDestination().id){
inclusive = true }}
Or To keep Home in back stack
navController.navigate(ScreenRoutes.SelectCourseLayout.route){
popUpTo(ScreenRoutes.Home.route)
}

Apart from screens, back stack contains navigational graphs, and its root is always the first thing in back stack. Our NavHostController contains graph, so by popping its id, you are able to clear your back stack:
popUpTo(navHostController.graph.id)
For more info, here is the detailed explanation https://medium.com/#banmarkovic/jetpack-compose-clear-back-stack-popbackstack-inclusive-explained-14ee73a29df5

To clear the back-stack, you can simply create this Extension function and reuse it wherever applicable.
fun NavHostController.navigateAndClean(route: String) {
navigate(route = route) {
popUpTo(graph.startDestinationId) { inclusive = true }
}
graph.setStartDestination(route)
}

Jetpack Compose v1.0.5
navController.backQueue.removeIf { it.destination.route == "Splash" }
navController.popBackStack()

After so many try, I've found the better way to clear the back stack during the logout scenario. Most of the production app will clear the splash or sign in screen as soon as we navigate to Home screen and there would be a multiple way to land into Home screen as well.
So, we may not know the initial screen to perform the popupTo. If there is a bottom bar, then story would be too difficult as well.
Here is a magic could that work all the scenario
val firstBackStackRoute = navController.backQueue.firstOrNull()?.destination?.route
firstBackStackRoute?.let {
navController.popBackStack(firstBackStackRoute, true)
}

Related

Jetpack Compose navigation NavController.popBackStack() not working properly

When working with Compose Navigation and calling NavController.popBackStack() multiple times on the first shown Composable (startDestination) the backnavigation does not work anymore. For example when navigating to another Composable from this point on and then calling popBackStack does not have an effect.
For some Reason the size of the NavController.backQueue is at least 2 even though it's supposed to only show one Composable. If popping the backstack lower than that, the navigation does not seem to work anymore. (I don't know why)
Therefore I wrote the following simple extension function which prevents popping the BackQueue lower than 2:
fun NavController.navigateBack(onIsLastComposable: () -> Unit = {}) {
if (backQueue.size > 2) {
popBackStack()
} else {
onIsLastComposable()
}
}
You can use it like this:
val navController = rememberNavController()
...
navController.navigateBack {
//do smth when Composable was last one on BackStack
}

Can't remove backstack using Jetypack Compose

i'm having really hard time understanding what is going wrong here.
Let's assume i have an app with 2 screens , Home (A) and an adding form (B).
What i'm trying to achieve is having the screen A to be in the backstack, then launching B to do some work , and the going back to A reflecting the changes made in B.
Using compose, navigating from A to B works fine with this command
navController.navigate(Screen.AddingForm.route) {
}
But when i try to navigate back (for example pressing the back button , both phisical and a custom one) with this code :
BackHandler(enabled = true) {
navController.navigate(Screen.Home.route) {
popUpTo(Screen.AddingForm.route){
inclusive = true
}
}
}
if (viewModel.isBackPressed) {
navController.navigate(Screen.Home.route) {
popUpTo(Screen.AddingForm.route){
inclusive = true
}
}
}
What happens is that the A screen is shown, but if i press again the back button, the B is shown.
That does not makes any sense to me, is anything wrong with my code?
EDIT:
As suggested, i removed the BackHandler and modified the logic as it follows:
if (viewModel.isBackPressed) {
navController.popBackStack(route = Screen.Home.route, inclusive = false)
}
But with the physical back button and the one that triggers the code, the same result happens, the Form (B) is redrawn and the navigation remains where it started

How navigate without backstack in Jetpack Compose?

Now I'm switching between screens this way
navController.navigate(Screen.Overview.route + "/${state.accessToken}")
How can I switch to another screen, but still clear the backstack?
Please check this function from the NavController Documentation. By using the inclusive parameter and popping the backstack up to the specified topmost destination, you should be able to handle navigation the way you want to.
navController.navigate(Screen.Overview.route + "/${state.accessToken}") {
popUpTo(Screen.Overview.route + "/${state.accessToken}") {
inclusive = true
}
}
should do the trick

Navigation Controller (Managing Backstack) Jetpack Android

Good day. So I've been working around with NavComponent of Jetpack for Android
I've thought that management of BackStack of fragments had to be implemented there already, well in fact it is there but I have faced an issue.
Here is my structure:
I have and entry Activity
I have a NavHost in the activity
I have Bottom Navigation bar in the Activity
For each Bottom Item I am using separate Fragments to navigate through.
Here is the code for the navigation.
bottomNavigationView.setOnNavigationItemSelectedListener {
when (it.itemId) {
R.id.navigation_home -> {
navController.apply {
navigate(R.id.navigation_home)
}
true
}
R.id.navigation_dashboard -> {
navController.apply {
navigate(R.id.dashboardFragment)
}
true
}
R.id.navigation_notifications -> {
true
}
else -> {
false
}
}
}
Never mind the last item.
So the issue is next.
If I try to switch between home and dashboard multiple times, when I press back then the stack surely will start popping all the items included there. So if I move like 6 times it will take me 12 attempts to actually exit the app.
Currently I couldn't find any source where for example the navigate() method will accept some sort of argument to cash my fragments instead of recreating it each time and adding to the BackStack.
So what kind of approach would you suggest?
If I to manage the BackStack manually on each back button pressed, what's the purpose of NavController at all? Just for creating and FORWARD navigation?
I think I'm missing some source in Android's official docs.
Thank you beforehand.
P.S.
using navController.popBackStack() before calling navigate() surely isn't the correct choice.
According to the documentation here :
NavigationUI can also handle bottom navigation. When a user selects a menu item, the NavController calls onNavDestinationSelected() and automatically updates the selected item in the bottom navigation bar.
to do so you have to give your bottom navigation items an ids as same as the corresponding destination in your navigation graph , and then tie you bottom view to the controller like this :
NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
NavigationUI.setupWithNavController(bottomNav, navController);
Note : from my personal experience , when the startDestination in the graph , that start by default is not currently in back stack (In my case it was the landing page which i pop it out when going to home fragment) then the app act with weird behavior like this . so make sure the start destination is existed in your back stack on should work fine .

How can i go to previous fragment when i am using navigation

i am now currently in signupfragment and need to go to loginfragment which is my previous fragment but its not working
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
Log.e("BackPressed","Onbackpressed")
findNavController().popBackStack(R.id.signupfragment,false)
}
The second parameter to popBackStack(R.id.signupfragment,false) is popUpToInclusive:
popUpToInclusive: boolean: Whether the given destination should also be popped.
If you use popUpToInclusive of false, you are saying "pop everything up to R.id.signupfragment, but not R.id.signupfragment.
Therefore, if you are at R.id.signupfragment, this correctly pops nothing (and returns false). You want to either 1) use true for popUpToInclusive or 2) use the version of popBackStack() that doesn't take any parameters, as this is the equivalent of popping just your current destination off the back stack:
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
Log.e("BackPressed","Onbackpressed")
findNavController().popBackStack()
}
Of course, Navigation already sets up the back button to call popBackStack() when you hit the back button, so besides your extra logging, your call is entirely unnecessary.
Another way of doing it easily is by putting a test back button and coding it with.
testBackButton.setOnClickListener {
val intent = Intent(this, ActivityThatYouWantToGo::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
I dont know its a good answer but its working
findNavController().popBackStack()
findNavController().popBackStack()
findNavController().navigate(R.id.loginFragment)

Categories

Resources