How to clear navigation Stack after navigating to another fragment in Android

AndroidAndroid FragmentsAndroid Architecture-ComponentsAndroid Architecture-Navigation

Android Problem Overview


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

Android Solutions


Solution 1 - Android

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

Solution 2 - Android

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"/>

Solution 3 - Android

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.

Solution 4 - Android

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"

Solution 5 - Android

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

Solution 6 - Android

I finally figure it out thanks to https://stackoverflow.com/questions/50336112/how-to-disable-up-in-navigation-for-some-fragment-with-the-new-navigation-archit

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

                    }

                }
            });

Solution 7 - Android

use this code

navController.navigateUp();

then call new Fragment android version 4.1.2

Solution 8 - Android

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.

Solution 9 - Android

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)

Solution 10 - Android

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

}

Solution 11 - Android

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

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!

Solution 12 - Android

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

Solution 13 - Android

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:

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

Solution 14 - Android

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"

Solution 15 - Android

You can do as simple as:

getFragmentManager().popBackStack();

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

getFragmentManager().getBackStackEntryCount()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYoussef El BehiView Question on Stackoverflow
Solution 1 - AndroidSubhojit ShawView Answer on Stackoverflow
Solution 2 - AndroiddongkichanView Answer on Stackoverflow
Solution 3 - AndroidlambaView Answer on Stackoverflow
Solution 4 - AndroidMelView Answer on Stackoverflow
Solution 5 - Androidemad pirayeshView Answer on Stackoverflow
Solution 6 - AndroidYoussef El BehiView Answer on Stackoverflow
Solution 7 - AndroidRameshView Answer on Stackoverflow
Solution 8 - AndroidTonyView Answer on Stackoverflow
Solution 9 - AndroidAbdul Rahman ShamairView Answer on Stackoverflow
Solution 10 - AndroidROHIT LIENView Answer on Stackoverflow
Solution 11 - AndroidYasser AKBBACHView Answer on Stackoverflow
Solution 12 - Androidshahar keysarView Answer on Stackoverflow
Solution 13 - AndroidThaiPDView Answer on Stackoverflow
Solution 14 - AndroidTylerView Answer on Stackoverflow
Solution 15 - AndroidMr TView Answer on Stackoverflow