302

I am using The new Navigation Architecture Component in android and I am stuck in clearing the navigation stack after moving to a new fragment.

Example: I am in the loginFragment and I want this fragment to be cleared from the stack when I navigate to the home fragment so that the user will not be returned back to the loginFragment when he presses the back button.

I am using a simple NavHostFragment.findNavController(Fragment).navigate(R.id.homeFragment) to navigate.

Current Code :

mAuth.signInWithCredential(credential) .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment); } else { Log.w(TAG, "signInWithCredential:failure", task.getException()); } } }); 

I tried using the NavOptions in the navigate(), but the back button is still sending me back to the loginFragment

NavOptions.Builder navBuilder = new NavOptions.Builder(); NavOptions navOptions = navBuilder.setPopUpTo(R.id.homeFragment, false).build(); NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment, null, navOptions); 
6
  • You can use popBackStack or don't add LoginFragment to backstack provide null to addToBackStack(null); and replace it with new Fragment Commented May 24, 2018 at 17:13
  • I think @Yupi has provided a good suggestion. Or you could use the navigate() method like navigate(int resId, Bundle args, NavOptions navOptions) and provide the NavOptions that best fit your senario Commented May 24, 2018 at 17:38
  • I tried using The NavOptions but the back button is still sending me back to the loginFragment Commented May 24, 2018 at 18:05
  • In navigation graph you can add app:popUpTo="@+id/desiredFragment" for homeFragment action, when user will click back he will be navigated to desiredFragment and not loginFragment Commented May 25, 2018 at 4:34
  • @Alex I tried doing that but it's not having any effects. Commented May 25, 2018 at 9:44

22 Answers 22

430

First, add attributes app:popUpTo='your_nav_graph_id' and app:popUpToInclusive="true" to the action tag.

<fragment android:id="@+id/signInFragment" android:name="com.glee.incog2.android.fragment.SignInFragment" android:label="fragment_sign_in" tools:layout="@layout/fragment_sign_in" > <action android:id="@+id/action_signInFragment_to_usersFragment" app:destination="@id/usersFragment" app:launchSingleTop="true" app:popUpTo="@+id/main_nav_graph" app:popUpToInclusive="true" /> </fragment> 

Second, navigate to the destination, using above action as parameter.

findNavController(fragment).navigate( SignInFragmentDirections.actionSignInFragmentToUserNameFragment()) 

See the docs for more information.

NOTE: If you navigate using method navigate(@IdRes int resId), you won't get the desired result. Hence, I used method navigate(@NonNull NavDirections directions).

Sign up to request clarification or add additional context in comments.

17 Comments

Best answer as clearTask is now deprecated in new versions of Android Navigation Component!
One can also use the id of the action like so findNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)
I think the key to this answer lies in that the "popUpTo" (which removes all fragments between your current fragment and the first occurrence of the id that you pass to this argument) is set to the id of the navigation graph itself. Effectively completely clearing the backstack. Only adding this comment because I haven't seen it explained exactly like this, and it was what I was looking for. +1!
NOTE: Not that "Directions" class will not be generated if you didn't included safe-args gradle. For more info visit here
Anybody investigated why " If you navigate using method navigate(@IdRes int resId), you won't get the desired result." ?
|
106

I think your question specifically pertains on how to use the Pop Behavior / Pop To / app:popUpTo (in xml)

In documentation,
Pop up to a given destination before navigating. This pops all non-matching destinations from the back stack until this destination is found.

Example (Simple Job hunting app)
my start_screen_nav graph is like this:

startScreenFragment (start) -> loginFragment -> EmployerMainFragment -> loginFragment -> JobSeekerMainFragment 

if I want to navigate to EmployerMainFragment and pop all including startScreenFragment then the code will be:

 <action android:id="@+id/action_loginFragment_to_employerMainFragment" app:destination="@id/employerMainFragment" app:popUpTo="@+id/startScreenFragment" app:popUpToInclusive="true" /> 

if I want to navigate to EmployerMainFragment and pop all excluding startScreenFragment then the code will be:

 <action android:id="@+id/action_loginFragment_to_employerMainFragment" app:destination="@id/employerMainFragment" app:popUpTo="@+id/startScreenFragment"/> 

if I want to navigate to EmployerMainFragment and pop loginFragment but not startScreenFragment then the code will be:

 <action android:id="@+id/action_loginFragment_to_employerMainFragment" app:destination="@id/employerMainFragment" app:popUpTo="@+id/loginFragment" app:popUpToInclusive="true"/> 

OR

 <action android:id="@+id/action_loginFragment_to_employerMainFragment" app:destination="@id/employerMainFragment" app:popUpTo="@+id/startScreenFragment"/> 

2 Comments

How to do this programmatically instead of in XML?
@IgorGanapolsky KotlinDSL
79

In my case i needed to remove everything in the back Stack before i open a new fragment so i used this code

navController.popBackStack(R.id.fragment_apps, true); navController.navigate(R.id.fragment_company); 

the first line removes the back Stack till it reaches the fragment specified in my case it's the home fragment so it's removes all the back stack completely , and when the user clicks back in the fragment_company he closes the app.

3 Comments

What is fragment_apps??
The current fragment. It's like if u are on fragmentOne and want to go fragmentTwo u need too use navController.popBackStack(R.id.fragmentOne, true); navController.navigate(R.id.fragmentTwo);
This is the only thing that worked for me (Navigation v2.3.5).
30

Going to add another answer here as none of the above worked for me ... we have multiple nav graphs.

findNavController().navigate(R.id.dashboard_graph,null,NavOptions.Builder().setPopUpTo(findNavController().graph.startDestination, true).build()) 

This was the only way that I could successfully clear the full back stack. Google really need to make this simpler.

2 Comments

This was very useful. I had already defined popUpTo and popUpToInclusive in XML AND preforming the navigation using the NavDirections not the ID but still at times it failed. Using the back/up navigation retuned the user to the original starting destination :(
In a multi-module environment, this seems to be the only working answer besides some really other horrifying hacks.
17

NOTE: Clear task is deprecated, official description is

This method is deprecated. Use setPopUpTo(int, boolean) with the id of the NavController's graph and set inclusive to true.

Old Answer

If you don't wanna go through all that fuzz in code, you can simply check Clear Task in Launch Options in properties of the action.

Launch Options

Edit: As of Android Studio 3.2 Beta 5, Clear Task is no longer visible in Launch Options window, but you can still use it in navigation's XML code, in action tag, by adding

app:clearTask="true" 

5 Comments

You should use the id of the root of the graph instead as the clearTask xml attributedeprecation message says: "set popUpTo to the root of your graph and use popUpToInclusive". I have done it like this and achieved the same desired behavior with findNavController().navigate(@IdRes resId, bundle)
Using the recommended approach of setting popUpTo and popUpToInclusive doesn't seem to be working for actions between destinations other that the start destination: issuetracker.google.com/issues/116831650
I'm actually encountering an issue where it doesn't work with start destination, frustrating.
How would you do this if you don't know the fragment to which you wish to pop? For example I have a fragment that creates a new item. After creating I want to show the details view of this item. But I don't want the user to pop back to the create fragment afterwards but to the previous view. That can either be a list of all items or a different item on which the new one was based. Is then clearTask the only option?
No, it actually shouldn't be used anymore. Instead you should use popUpTo inclusively. I will update the answer very soon with my findings of popUpTo usage.
15
 NavController navController =Navigation.findNavController(requireActivity(), R.id.nav_host_fragment);// initialize navcontroller if (navController.getCurrentDestination().getId() == R.id.my_current_frag) //for avoid crash { NavDirections action = DailyInfoSelectorFragmentDirections.actionGoToDestionationFragment(); //for clear current fragment from stack NavOptions options = new NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build(); navController.navigate(action, options); } 

Comments

12

I finally figure it out thanks to How to disable UP in Navigation for some fragment with the new Navigation Architecture Component?

I had to specify .setClearTask(true) as a NavOption.

mAuth.signInWithCredential(credential) .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { Log.d(TAG, "signInWithCredential:success"); NavOptions.Builder navBuilder = new NavOptions.Builder(); NavOptions navOptions = navBuilder.setClearTask(true).build(); NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.homeFragment,null,navOptions); } else { Log.w(TAG, "signInWithCredential:failure", task.getException()); } } }); 

7 Comments

Glad to see you were able to implement my suggestion of using navigate(int resId, Bundle args, NavOptions navOptions) and NavOptions
bad to bad style idea
yeah "setClearTask" is deprecated but instead you can use: val navOptions = NavOptions.Builder().setPopUpTo(R.id.my_current_frag, true).build() findNavController().navigate(R.id.my_next_frag, null, navOptions)
@c-an it should work. Probably you're using a wrong destination fragmentId (e.g. R.id.my_next_frag could be like login or splash screen); also if you want to popBack just one, you can use: NavHostFragment.findNavController(this).popBackStack().
@PabloReyes This is the correct answer. Just specify which fragment is the last to remove: Say you have this stack: Fragment_1 Fragment_2 Fragment_3 You are now on Fragment_3 and want to navigate to Fragment_4 and clear all other fragments. Just specify in the navigation xml: app:popUpTo="@id/Fragment_1" app:popUpToInclusive="true"
|
11

use this code

navController.navigateUp(); 

then call new Fragment android version 4.1.2

Comments

10

In my case where I used Navigation component with NavigationView (menu drawer):

1.

mNavController.popBackStack(R.id.ID_OF_FRAGMENT_ROOT_TO_POP, true) 
 mNavController.navigate( R.id.DESTINATION_ID, null, NavOptions.Builder() .setPopUpTo(R.id.POP_TO_DESTINATION_ID, true) .build() ) 

I wanted to clear the stack after clicking on logout on side menu drawer! Hope that helped someone!

2 Comments

Could you explain a bit what you have done here? Do I have to copy/paste those 2 options or is it either one or the other?
godlike answer @Yasser AKBBACH
9

Here is how I am getting it done.

 //here the R.id refer to the fragment one wants to pop back once pressed back from the newly navigated fragment val navOption = NavOptions.Builder().setPopUpTo(R.id.startScorecardFragment, false).build() //now how to navigate to new fragment Navigation.findNavController(this, R.id.my_nav_host_fragment) .navigate(R.id.instoredBestPractice, null, navOption) 

2 Comments

Why is your inclusive flag false?
According to documentation inclusive boolean: true to also pop the given destination from the back stack So, I set the inclusive to false because I don't want to pop the current fragment from the stack.
7

For

// Navigation library def nav_version = "2.3.5" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" 

This solution work for me

 findNavController().popBackStack(R.id.<Current Fragment Id In NavGraph>, true) findNavController().navigate(R.id.< Your Destination Fragment in NavGraph>) 

Comments

4

You can override the back pressed of the base activity like this :

override fun onBackPressed() { val navigationController = nav_host_fragment.findNavController() if (navigationController.currentDestination?.id == R.id.firstFragment) { finish() } else if (navigationController.currentDestination?.id == R.id.secondFragment) { // do nothing } else { super.onBackPressed() } } 

Comments

4

Non of the solutions above works for me. After spending hours on it, here is my solution:

Note: I have multiple nav_graphs and switching between fragments in different nav_graphs.

  1. Define your action as below in xml:
<action android:id="@id/your_action_id" app:destination="@id/the_fragment_id_you_want_to_navigate_to" app:popUpTo="@id/nav_graph_which_contains_destination_fragment" app:popUpToInclusive="true" /> 
  1. Navigate using action above from your Java/Kotlin code:
findNavController(R.id.your_nav_name)?.apply { navigate(R.id.your_action_id) backQueue.clear() } 

1 Comment

backQueue is private now unfortunately. Any new way to archive this?
3

When you are doing it programmatically you can continue with the following way:

 findNavController().navigate( //your navigation direction R.id.action_fragmentB_to_fragmentC, //pass arguments if you have, else null null, //Pass the fragment id where you want to land on the back press at fragmentC, //true/false, in case you want to remove the fragmentA from the back stack use the TRUE and vice versa NavOptions.Builder().setPopUpTo(R.id.fragmentA, true).build() ) 

Also, you can achieve that in the following way in the navigation graph XML as below screenshot

enter image description here

Comments

2

I am using button to navigate to other fragments so on each button click I am doing this.

 val navOptions = NavOptions.Builder().setLaunchSingleTop(true).setPopUpTo(R.id.homeFragment, false).build() findNavController(R.id.mainNavHostFragment).navigate(R.id.destination, null, navOptions) 

Comments

1

For Jetpack Compose ❤️

navHostController.navigate(Routes.HOME) { this.popUpTo(Routes.ONBOARDING) { this.inclusive = true } } 

Comments

0

I struggled for a while to prevent the back button from going back to my start fragment, which in my case was an intro message that should only appear once.

The easy solution was to create a global action pointing to the destination that the user should stay on. You have to set app:popUpTo="..." correctly - set it to the destination you want to get popped off. In my case it was my intro message. Also set app:popUpToInclusive="true"

Comments

0

For androidx.compose version 1.2.0+

I had a few issues with lower versions but 1.2 plus (beta at the time of writing this), works perfectly.

Better syntax for the navGraph in Compose:

navController.navigate(item.name) { navController.graph.startDestinationRoute?.let { route -> // use saveState = false to NOT save the state for the popped route popUpTo(route) { saveState = true } } launchSingleTop = true restoreState = true } 

Comments

0

It's not the best practice but it works for me (Kotlin/Fragment)

while (findNavController().navigateUp()){findNavController().popBackStack()} findNavController().navigate(R.id.other_fragment) 

Comments

0

you can try setPopUpTo(findNavController().backQueue.first().destination.id, true)

1 Comment

backQueue is a private api.
0

LAST RESORT SOLUTION!

NOTE: This workaround uses restricted API (getCurrentBackStack()) and relies on internal behavior. It may break in future updates. Use only if nothing else works.

private final static int EMPTY_STACK_CONSTANT = 1; @SuppressLint("RestrictedApi") public void clearNavigationBackStackAndNavigate(NavController navController, int destinationId, Bundle bundle) { while (navController.getCurrentBackStack().getValue().size() > EMPTY_STACK_CONSTANT) { navController.popBackStack(); } navController.navigate(destinationId, bundle); } 

Comments

-6

You can do as simple as:

getFragmentManager().popBackStack(); 

If you want to check the count you can do as:

getFragmentManager().getBackStackEntryCount() 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.