285

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
  • 416
  • 1
  • 7
  • 24
Youssef El Behi
  • 3,042
  • 2
  • 13
  • 22
  • 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
  • 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

21 Answers21

405

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.
  • 13,726
  • 10
  • 50
  • 63
Subhojit Shaw
  • 4,645
  • 1
  • 11
  • 8
  • 2
    Best answer as clearTask is now deprecated in new versions of Android Navigation Component! – Michał Ziobro Dec 18 '18 at 09:02
  • 23
    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
  • 1
    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
  • 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
  • 38
    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
  • 13
    **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
  • @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
  • 2
    How to clear backstack if I navigate from fragment to activity using navigation architecture component? – Aminul Haque Aome Jul 23 '20 at 17:02
  • @ScottO'Toole your comment is literally the best answer to any popUp related questions. Game changer! – grrigore Jan 13 '22 at 14:28
  • if you dont use safe-args and you want the desired result try: val navController = findNavController() navController.popBackStack() navController.navigate(navController.graph.startDestinationId) – Mostafa Onaizan Feb 28 '22 at 01:31
  • Wow. If you're like me, you were trying this with a fragment that navigates to an activity and it would. Not. Work. Here is a solution, but be warned, it's ugly https://stackoverflow.com/questions/68355036/navigation-component-how-to-clear-back-stack-when-navigating-from-fragment-to-a. I might just refactor my two nav graphs into one so I don't have this problem. – Chucky Mar 29 '22 at 17:19
96

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
  • 1,071
  • 6
  • 4
73

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.

Nour abo elsoud
  • 967
  • 1
  • 9
  • 16
22

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,335
  • 21
  • 25
  • 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 :( – DevinM Aug 04 '21 at 17:08
  • In a multi-module environment, this seems to be the only working answer besides some really other horrifying hacks. – Kenneth Murerwa Jul 01 '22 at 09:46
16

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,730
  • 17
  • 33
  • 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) – theme_an 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
13
    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
  • 654
  • 7
  • 12
11

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
  • 3,042
  • 2
  • 13
  • 22
  • 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
  • bad to bad style idea – Serg Burlaka Sep 24 '18 at 15:48
  • 19
    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 Pantaleon 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 Pantaleon 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
10

use this code

navController.navigateUp();

then call new Fragment android version 4.1.2

Ramesh
  • 504
  • 5
  • 9
8

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)
Abdul Rahman Shamair
  • 580
  • 1
  • 11
  • 22
  • Why is your **inclusive** flag `false`? – IgorGanapolsky Feb 04 '20 at 18:53
  • 3
    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
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>)

shahar keysar
  • 837
  • 5
  • 9
7

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!

Yasser AKBBACH
  • 539
  • 5
  • 7
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()
  }

}
adiga
  • 34,372
  • 9
  • 61
  • 83
ROHIT LIEN
  • 497
  • 3
  • 8
3

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()
}
ThaiPD
  • 3,503
  • 3
  • 30
  • 48
1

For Jetpack Compose ❤️

navHostController.navigate(Routes.HOME) {
                            this.popUpTo(Routes.ONBOARDING) {
                                this.inclusive = true
                            }
                     }
abhi
  • 961
  • 9
  • 13
1

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

Ammar
  • 765
  • 1
  • 8
  • 18
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,741
  • 3
  • 22
  • 19
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
}
Nadeem Iqbal
  • 2,357
  • 1
  • 28
  • 43
Codeversed
  • 9,287
  • 3
  • 43
  • 42
0

In my case, I am using 2 different activities that have their own respective navigation graphs. My first activity is the host for "nav_graph" and has fragments that deal with authentification and the second is the host for "nav_graph_home". Here you can see the settings I have done for nav_graph.

nav_graph example

1

Then back in my code for the login fragment, I have this written :

findNavController().navigate(R.id.action_logInFragment_to_nav_graph_home)

After the user logs in and they hit the back button the app will close. Remember to set the pop behavior so it pops till your current navigation graph that contains your login fragment without including it.

Edit: After this, the up button still appears in the top bar. To avoid this behavior we need to tell the first activity which fragments are considered top level. To do this simply add in the params list of the "setupActionBarWithNavController()" method in addition to the nav host fragment an App bar configuration that contains a set of the home fragment of your first navigation graph and your second. Your code should look something like this:

class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var navController: NavController

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Retrieve NavController from the NavHostFragment
    val navHostFragment = supportFragmentManager
        .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    navController = navHostFragment.navController

    // Set up the action bar for use with the NavController
    setupActionBarWithNavController(navController, AppBarConfiguration(setOf(R.id.logInFragment,R.id.homeFragment)))
}

/**
 * Handle navigation when the user chooses Up from the action bar.
 */
override fun onSupportNavigateUp(): Boolean {
    return navController.navigateUp() || super.onSupportNavigateUp()
}

}

This is my first ever contribution, hope this helps.

vimuth
  • 5,064
  • 33
  • 79
  • 116
Purple6666
  • 26
  • 4
0

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)
Faizan Haidar Khan
  • 1,099
  • 1
  • 15
  • 20
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)
-6

You can do as simple as:

getFragmentManager().popBackStack();

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

getFragmentManager().getBackStackEntryCount()
Mr T
  • 1,409
  • 1
  • 17
  • 24