How to disable UP in Navigation for some fragment with the new Navigation Architecture Component?
Asked Answered
Y

6

27

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!

Yeti answered 14/5, 2018 at 17:42 Comment(0)
V
33

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

Vergievergil answered 22/8, 2018 at 20:21 Comment(6)
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.Tympanist
Can you be more specific to that?Partlet
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.Beneficence
Note: you need to add the Navigation Component Safe Args plugin to your project for that *Directions class in step 2 to be generated.Retentivity
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?Agitprop
@Agitprop you can configure a set of top level destinations (i.e. these fragments won't display the up button). See this answer: https://mcmap.net/q/103079/-remove-up-button-from-action-bar-when-navigating-using-bottomnavigationview-with-android-navigation-ui-libraryOrissa
C
8

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"

Cedeno answered 14/8, 2018 at 22:47 Comment(2)
Hi, welcome to StackOverflow. Can you please re-format your answer so that it would easy to understand?Biforate
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.Tympanist
Y
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).

Yeti answered 14/5, 2018 at 17:55 Comment(4)
But it say setClearTask has been deprecated. Shouldn't we look for alternatives.Navarra
@Navarra good find! I just commented on the issue (issuetracker.google.com/issues/80338878), hopefully will got a reply soon. Will also add a warning to the answer.Yeti
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. linkNavarra
@Navarra 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)Tympanist
P
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

Pregnable answered 29/4, 2020 at 6:8 Comment(0)
G
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())
Gargoyle answered 3/2, 2019 at 1:27 Comment(0)
V
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

Visby answered 11/12, 2020 at 1:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.