184

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);
amira
  • 417
  • 7
  • 18
Youssef El Behi
  • 1,978
  • 2
  • 10
  • 16
  • You can use `popBackStack` or don't add `LoginFragment` to backstack provide `null` to `addToBackStack(null);` and replace it with new `Fragment` – Yupi May 24 '18 at 17:13
  • 1
    Please edit your post and add the code you are now using to navigate – Barns May 24 '18 at 17:17
  • I edited my question and added my code – Youssef El Behi May 24 '18 at 17:27
  • 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 – Barns May 24 '18 at 17:38
  • I tried using The NavOptions but the back button is still sending me back to the loginFragment – Youssef El Behi May 24 '18 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 – Alex May 25 '18 at 04:34
  • @Alex I tried doing that but it's not having any effects. – Youssef El Behi May 25 '18 at 09:44
  • you could you use alternative approach without clearing back stack described here https://stackoverflow.com/a/52997438/3278271 – Arsenius Feb 21 '19 at 14:41

13 Answers13

275

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

X.Y.
  • 12,991
  • 9
  • 48
  • 62
Subhojit Shaw
  • 3,255
  • 1
  • 7
  • 8
  • 2
    Best answer as clearTask is now deprecated in new versions of Android Navigation Component! – Michał Ziobro Dec 18 '18 at 09:02
  • 18
    One can also use the id of the action like so `findNavController(fragment).navigate(R.id.action_signInFragment_to_usersFragment)` – Calin Dec 28 '18 at 16:36
  • 1
    Awesome! However, now I have the back/up glyph, but pressing it doesn't do anything. What's a clean way to remove that too? – Danish Khan Jan 02 '19 at 08:02
  • I ended up creating a [new question](https://stackoverflow.com/questions/54003666/navigation-components-popupto-not-removing-up-button) for this. – Danish Khan Jan 02 '19 at 09:19
  • I can not understand this `app:popUpTo="@+id/main_nav_graph" app:popUpToInclusive="true"` but it is working - previous fragment destroyes. Without this 2 lines only going to hidden state.. – Nickolay Savchenko Jan 03 '19 at 22:26
  • This works well but is I am facing an issue [this](https://stackoverflow.com/questions/53976785/android-navigation-component-pop-to-transition-issue) can someone help me on this? – jaydeep_gedia Jan 04 '19 at 08:04
  • when using this way, it actually destroy the old one and create a new one, what if i want to get back to the old one? – Shalan93 Apr 13 '19 at 00:42
  • 13
    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! – Scott O'Toole May 31 '19 at 19:43
  • A bit late to the party but I tried this solution and faced some other problem. The up button still appears and takes me up to the 1st fragment. Check this question - https://stackoverflow.com/questions/56625934/android-unable-to-clear-the-back-stack-of-all-fragments-with-navigation-compone – Yashovardhan99 Jun 17 '19 at 06:47
  • 10
    **NOTE**: Not that "Directions" class will not be generated if you didn't included safe-args gradle. For more info visit [here](https://medium.com/google-developer-experts/android-navigation-components-part-3-19554ec9ae83.) – Jaydip Kalkani Jun 22 '19 at 15:38
  • Only work in one of my fragment, not all.Not sure why – John Joe Jun 25 '19 at 07:48
  • I am sorry, but what is "your_nav_graph_id"? – StayCool Feb 18 '20 at 17:07
  • Worked for me.Thanks – Rohit Sharma Mar 24 '20 at 07:52
  • @JaydipKalkani 's link 404d and is no longer available. Here are the official [docs](https://developer.android.com/guide/navigation/navigation-navigate#safeargs) – Lighterletter Jun 14 '20 at 05:46
  • 2
    Anybody investigated why " If you navigate using method navigate(@IdRes int resId), you won't get the desired result." ? – ror Jul 10 '20 at 08:45
  • How to clear backstack if I navigate from fragment to activity using navigation architecture component? – Aminul Haque Aome Jul 23 '20 at 17:02
  • Saved my ass. Thank you – viper May 30 '21 at 07:34
51

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"/>
dongkichan
  • 621
  • 3
  • 4
42

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.

lamba
  • 617
  • 7
  • 14
  • 5
    What is `fragment_apps`?? – IgorGanapolsky Feb 04 '20 at 18:32
  • 2
    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); – Bbeni Feb 17 '20 at 07:25
14

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"
Mel
  • 1,445
  • 13
  • 28
  • 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) – artnest Aug 31 '18 at 07:58
  • Using the recommended approach of setting popUpTo and popUpToInclusive doesn't seem to be working for actions between destinations other that the start destination: https://issuetracker.google.com/issues/116831650 – hsson Sep 28 '18 at 10:41
  • I'm actually encountering an issue where it doesn't work with start destination, frustrating. – Mel Sep 28 '18 at 12:25
  • 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? – findusl Dec 08 '18 at 19:39
  • 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. – Mel Dec 08 '18 at 20:21
7

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());

                    }

                }
            });
Youssef El Behi
  • 1,978
  • 2
  • 10
  • 16
  • 2
    Glad to see you were able to implement my suggestion of using `navigate(int resId, Bundle args, NavOptions navOptions)` and `NavOptions` – Barns May 25 '18 at 15:33
  • Yes. Thanks @Barns – Youssef El Behi May 26 '18 at 23:42
  • bad to bad style idea – Serg Burlaka Sep 24 '18 at 15:48
  • 14
    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)` – Pablo Reyes Oct 26 '18 at 16:52
  • @PabloReyes It works. However, it just remove the last one. Is there any way to clear all the fragments in the stack and start over? – c-an Jun 18 '19 at 02:04
  • 1
    @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()`. – Pablo Reyes Jun 19 '19 at 13:39
  • 2
    @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" – user3193413 Jul 17 '19 at 15:09
  • Why not just call `popBackStack`? – IgorGanapolsky Feb 04 '20 at 18:34
7
    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);
    }
emad pirayesh
  • 474
  • 5
  • 9
6

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)
  • Why is your **inclusive** flag `false`? – IgorGanapolsky Feb 04 '20 at 18:53
  • 2
    According to [documentation](https://developer.android.com/reference/androidx/navigation/NavOptions.Builder#setPopUpTo(int,%20boolean)) `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. – Abdul Rahman Shamair Feb 05 '20 at 10:07
4

use this code

navController.navigateUp();

then call new Fragment android version 4.1.2

Ramesh
  • 176
  • 1
  • 6
2

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

}
adiga
  • 28,937
  • 7
  • 45
  • 66
ROHIT LIEN
  • 419
  • 3
  • 8
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"

Tyler
  • 6,153
  • 3
  • 20
  • 14
0

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.

Tony
  • 2,085
  • 19
  • 23
-1

I can explain this through small example. I have two fragments employees list and delete employee. In delete employee, I have two click events cancel and delete. If i click cancel, then it simply back to employees list screen and if i click delete then i want to clear all previous backstacks and go to employees list screen.

For go to previous screen:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"/>


    btn_cancel.setOnClickListener {
                it.findNavController().popBackStack()
            }

For clear all previous backstack and go to new screen:

<action
        android:id="@+id/action_deleteEmployeeFragment_to_employeesListFragment2"
        app:destination="@id/employeesListFragment"
        app:popUpTo="@id/employeesListFragment"
        app:popUpToInclusive="true"
        app:launchSingleTop="true" />


 btn_submit.setOnClickListener {
                   it.findNavController().navigate(DeleteEmployeeFragmentDirections.actionDeleteEmployeeFragmentToEmployeesListFragment2())
        }

For more reference: Jetpack Navigation Example

Dhrumil Shah
  • 359
  • 2
  • 8
-4

You can do as simple as:

getFragmentManager().popBackStack();

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

getFragmentManager().getBackStackEntryCount()
Mr T
  • 1,036
  • 13
  • 21