21

I am trying out the new Navigation Architecture Component, and I can't figure out how to do this:

I have 1 Activity (MainActivity) + 3 Fragments:

  • SplashFragment (Home)
  • MainFragment
  • SignUpFragment

I would like to use SplashFragment to determine if I should navigate to MainFragment or SignUpFragment, but once it reaches either of those 2, you should not be able to pop back to SplashFragment. How can I do that with the new navigation component?

I tried popBackStack before and after calling navigate(R.id.action_xxx), but neither of them work (which make sense: before it has nothing to pop; after it just closes the fragment that just got added). Does that mean the only way to do that is to override onBackPress to intercept it and make sure navigateUp does not get call in those cases?

Thanks!

Louis Tsai
  • 1,443
  • 2
  • 19
  • 33

6 Answers6

24

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 the above action as parameter.

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

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

Subhojit Shaw
  • 3,255
  • 1
  • 7
  • 8
  • The popUpTo value need to be not nav_graph_id, but the id of the root of the graph, thus using navigate(@IdRes int resId) worked just fine for me. – artnest Aug 31 '18 at 07:48
  • Can you be more specific to that? – Dr. aNdRO Dec 11 '18 at 06:21
  • 9
    To clarify for those who were confused like me - `popUpTo` means "remove everything before this fragment" if `popUpToInclusive` is set to true it means "Remove this fragment AND everything before it" Also make sure you use the id of the **action** when navigating, not the id of the fragment you're navigating to. – bug56 Feb 01 '19 at 12:10
  • 3
    Note: you need to add the Navigation Component Safe Args plugin to your project for that *Directions class in step 2 to be generated. – soren121 Feb 08 '19 at 02:44
  • 1
    It's working with back button but the navigation up button is visible in MainFragment and clicks on up, navigate to splashFragment. How to solve this problem? – shantanu Apr 14 '21 at 20:26
7

WARNING: clearTask has been deprecated and will be remove in future release, not sure what the solution is. Please follow this issue for now to keep up to date


Oh after 10 minutes finally found the key: use clearTask.

All I have to do is add app:clearTask="true" to that specific action, or use .navigate(R.id.actionXXXX, null, NavOptions.Builder().setClearTask(true).build()), and it's done. Just make sure you add it to all the children of SplashFragment (in this case, both MainFragment and SignUpFragment).

Louis Tsai
  • 1,443
  • 2
  • 19
  • 33
  • 1
    But it say setClearTask has been deprecated. Shouldn't we look for alternatives. – MuM6oJuM6o Aug 12 '18 at 17:01
  • @MuM6oJuM6o good find! I just commented on the issue (https://issuetracker.google.com/issues/80338878), hopefully will got a reply soon. Will also add a warning to the answer. – Louis Tsai Aug 13 '18 at 16:05
  • 1
    I did some looking up in the docs, turns out Google has another method to achieve this. Its called NavOptions.setPopUpTo(destinationId:Int , inclusive:boolean). Unfortunately, i was unable to achieve the desired outcome. [link](https://developer.android.com/reference/androidx/navigation/NavOptions.Builder.html#setPopUpTo(int,%20boolean)) – MuM6oJuM6o Aug 13 '18 at 18:33
  • 1
    @MuM6oJuM6o I had the same problem, but after looking again at the deprecation message of clearTask xml attribute attentively, which says "set popUpTo to the root of your graph and use popUpToInclusive", I have done as it says and achieved the desired behavior. With this, you can still use findNavController().navigate(@IdRes resId, bundle) – artnest Aug 31 '18 at 07:53
7

This worked for me in alpha05 release. Add app:popUpTo="@id/nav_graph" in the action tag(inside your nav_graph.xml file). Here "@id/nav_graph is the id of my graph or also called as the Root.

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/startFragment">
.......
<action
android:id="@+id/action_startFragment_to_homeFragment"
app:destination="@id/homeFragment" 
app:popUpTo="@id/nav_graph"/>
.......

You can also do this in design tab:- select "SplashFragment" and select the action you want to change and then pick "root" for "Pop To"

Akii
  • 71
  • 1
  • 4
  • Hi, welcome to StackOverflow. Can you please re-format your answer so that it would easy to understand? – Abhinav Aug 14 '18 at 23:21
  • I think it's better to set popUpTo value to the id of the root of the graph, I mean your startDestination id value as the clearTask attribute deprecation message says. You should also set popUpToInclusive to true. – artnest Aug 31 '18 at 08:02
3

So if you have splash fragment and main fragment and you don't want to go back to splash fragment after the main fragment below method you can achieve this

<fragment
     android:id="@+id/splashFragment"
     android:name="com.example.youappname.views.SplashFragment"
     android:label="fragment_splash"
     tools:layout="@layout/fragment_splash">
     <action
         android:id="@+id/action_splashFragment_to_mainFragment"
         app:destination="@id/mainFragment"
         app:popUpTo="@id/splashFragment"
         app:popUpToInclusive="true"/>
</fragment>

In you Kotlin Splash Fragment:

private lateinit var navController: NavController

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)

      navController = Navigation.findNavController(view)

}

private fun navigateToMainFrag() {
      navController.navigate(R.id.action_splashFragment_to_mainFragment)
}

Now when you press back button it will close the app instead of showing the splash screen

Amit Singh
  • 431
  • 4
  • 6
2

For anyone wanted to do this purely in code:

Navigation.findNavController(v)
.navigate(R.id.action_splashFragment_to_userProfileFragment2, null, 
new NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build())
Broak
  • 3,978
  • 4
  • 27
  • 54
0

The sample solution is add a onBackPressedDispatcher on Owner Activity of fragment/navigation:

https://developer.android.com/guide/navigation/navigation-custom-back#implement_custom_back_navigation

João Eudes Lima
  • 391
  • 1
  • 3
  • 17