Android Navigation Component : BottomNavigationView's selected tab icon is not updated - android

I'm using BottomNavigationView with Navigation Component. When showing fragment is not root fragment, the tab icon is not updated (selected).
Example:
When I switch between Tab Home with Fragment A (which is root fragment) and Tab Star with Fragment B (which is also root fragment) it is working fine.
But when I navigate from Tab Home to another fragment, like fragment A2, and tap on Tab Star and again return to Tab Home, still Tab Star is selected in BottomNavigationView.
It was working fine with version 2.4.0-alpha05, This is happening when I updated it to 2.5.0-alpha01.
build.gradle (app)
implementation "androidx.navigation:navigation-fragment-ktx:2.5.0-alpha01"
implementation "androidx.navigation:navigation-ui-ktx:2.5.0-alpha01"
implementation "androidx.navigation:navigation-dynamic-features-fragment:2.5.0-alpha01"
build.gradle (root)
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-alpha01"
Graph:
<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/graph"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="ui.test.FragmentA"
tools:layout="#layout/fragment_test"
android:label="FragmentA" >
<action
android:id="#+id/action_fragmentA_to_fragmentA2"
app:destination="#id/fragmentA2" />
</fragment>
<fragment
android:id="#+id/fragmentA2"
android:name="ui.test.FragmentA2"
tools:layout="#layout/fragment_test"
android:label="FragmentA2" />
<fragment
android:id="#+id/fragmentB"
android:name="ui.test.FragmentB"
tools:layout="#layout/fragment_test"
android:label="FragmentB" />
</navigation>
Menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/fragmentA"
android:icon="#drawable/ic_home"
android:title="" />
<item
android:id="#+id/fragmentB"
android:icon="#drawable/ic_star"
android:title="" />
</menu>
Am I doing something wrong? or this is bug?
How can I resolve this problem?

So what worked for me was the solution that ianhanniballake hinted at in his answer: using setOnItemSelectedListener.
// always show selected Bottom Navigation item as selected (return true)
bottomNavigationView.setOnItemSelectedListener { item ->
// In order to get the expected behavior, you have to call default Navigation method manually
NavigationUI.onNavDestinationSelected(item, navController)
return#setOnItemSelectedListener true
}
This will always select the item and navigate to the associated destination while maintaining multiple back stacks.

Given your navigation graph, there is no way to associate fragmentA2 with your menu item fragmentA, so fragmentA is not selected when you return to fragmentA2. As per this issue:
NavigationUI has always used the current destination and what graph it is part of as the source of truth for what tab should be selected.
This can be seen by calling navigate() to go to your SecondFragment - even though you haven't used the bottom nav button, the selected tab was changed because the current destination has changed to R.id.frag_second.
So when you navigate() to R.id.frag_hint via your button in HomeFragment, NavigationUI receives a callback that the current destination has changed to R.id.frag_hint. It looks at that NavDestination and notes that there's no menu item that matches R.id.frag_hint. It then looks at the destination's parent graph - your R.id.sample <navigation> element. There's no menu item that matches that ID either, so NavigationUI can't associated that destination with any menu item and therefore simply does nothing. That is true on all versions of Navigation.
So what is different when you tap on a bottom navigation item? Well, nothing different from a NavigationUI perspective in fact: the exact same code runs and the current destination and what graph it is part of is the source of truth for what tab should be selected. In the Navigation 2.3.5, there was no state saved for each tab, so it only 'worked' because selecting a tab forced the ID of the current destination to match the destination of the menu item you just tapped.
So what you're seeing in your sample app is that there's no link between R.id.frag_hint and any menu item, which means NavigationUI does nothing. If you want to link R.id.frag_hint to your Home tab, then that's exactly what a nested navigation graph can be used for.
I.e., your navigation graph should instead look like:
<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/sample"
app:startDestination="#id/home">
<navigation
android:id="#+id/home"
app:startDestination="#id/frag_home">
<fragment
android:id="#+id/frag_home"
android:name="eu.rekisoft.android.navbug.HomeFragment"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/cause_bug"
app:destination="#id/frag_hint"/>
</fragment>
<fragment
android:id="#+id/frag_hint"
android:name="eu.rekisoft.android.navbug.HintFragment"
android:label="Hint"
tools:layout="#layout/fragment_hint"/>
</navigation>
<fragment
android:id="#+id/frag_second"
android:name="eu.rekisoft.android.navbug.SecondFragment"
android:label="Second Fragment"
tools:layout="#layout/fragment_second"/>
</navigation>
And your menu XML should be updated to use android:id="#id/home" to match your navigation graph.
Now, when you select the Home bottom nav item, the current destination changes to R.id.frag_hint (as your state was restored due to Navigation 2.4's support for multiple back stacks) and NavigationUI looks at the ID - R.id.frag_hint still doesn't match any menu item, but now the parent graph's ID, R.id.home does match a menu item - your Home menu item, hence, it becomes selected.
The intention that your navigation graph and its structure drives the UI is a key part of how NavigationUI works and is working as intended (there was a bug on earlier versions of Navigation 2.4 that broke this driving principle, but that has since been fixed in beta02). All of NavigationUI is built on public APIs specifically so that if you want to use some different logic for which bottom nav item is selected that is independent from your navigation graph structure, you can absolutely do that.
You'll note from the source code that you can call the public onNavDestinationSelected with your MenuItem to get the exact same navigate() logic which retaining your own ability to return any value from the setOnItemSelectedListener (which is what controls whether the tab becomes selected). Similarly, your own OnDestinationChangedListener can choose to look at the hierarchy of a destination to choose whether to change the selected bottom nav item or not.
So your graphs should also be using nested graphs for each tab:
<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/graph"
app:startDestination="#id/graphA">
<navigation
android:id="#+id/graphA"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="ui.test.FragmentA"
tools:layout="#layout/fragment_test"
android:label="FragmentA" >
<action
android:id="#+id/action_fragmentA_to_fragmentA2"
app:destination="#id/fragmentA2" />
</fragment>
<fragment
android:id="#+id/fragmentA2"
android:name="ui.test.FragmentA2"
tools:layout="#layout/fragment_test"
android:label="FragmentA2" />
</navigation>
<navigation
android:id="#+id/graphB"
app:startDestination="#id/fragmentB">
<fragment
android:id="#+id/fragmentB"
android:name="ui.test.FragmentB"
tools:layout="#layout/fragment_test"
android:label="FragmentB" />
</navigation>
</navigation>
Menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/graphA"
android:icon="#drawable/ic_home"
android:title="" />
<item
android:id="#+id/graphB"
android:icon="#drawable/ic_star"
android:title="" />
</menu>

Related

Bottom navigation bar malfunctions when navigating from a fragment

I'm using the bottom navigation bar with the navigation component
To make the two components work together I called:
bottomNavigationView.setupWithNavController(navController)
Everything works as expected except when I navigate from inside a fragment instead of the bottom navigation bar
"View all" opens the same fragment as "Reports" from the bottom navigation bar
binding.viewAllScansTv.setOnClickListener {
val action = MainFragmentDirections.actionMainFragmentToReportsFragment()
navController.navigate(action)
}
After clicking on "View all", the fragment is opened, the "Reports" button gets selected, however, navigating back "Home" does not work anymore
How can I fix this weird behavior?
The nav graph:
<navigation app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.package.name.ui.main.MainFragment"
android:label="MainFragment">
<action android:id="#+id/action_mainFragment_to_reportsFragment"
app:destination="#id/reportsFragment" />
</fragment>
</navigation>
The bottom navigation menu:
<menu>
<item
android:id="#+id/mainFragment"
android:title="Home"/>
<item
android:id="#+id/reportsFragment"
android:title="Reports"/>
<item
android:id="#+id/settingsFragment"
android:title="My account"/>
</menu>
As #ianhanniballake mentioned in a comment, a similar question was posted here
What I ended up doing was replacing
val action = MainFragmentDirections.actionMainFragmentToReportsFragment()
navController.navigate(action)
with
val item = mainBottomNavigationView.menu.findItem(R.id.reportsFragment)
NavigationUI.onNavDestinationSelected(item, navController)
So basically I used the NavigationUI API to navigate so that it correctly tracks the back stack. The same NavigationUI API is being used by the BottomNavigationView internally
Setting a click listener on any view should have the same effect as if the user taps the corresponding item in the bottom navigation. So you need to call setSelectedItemId() on the BottomNavigationView.
val mainBottomNav =
activity?.findViewById(R.id.homeBottomNavigation)
mainBottomNav?.selectedItemId = R.id.baseHomeFragment
Your current answer is fine but if you need to pass arguments it wont work, So use this
In the Navigation XML add these lines to the action
app:launchSingleTop="true"
app:popUpTo="#+id/main_navigation"
app:popUpToInclusive="true"
And make sure app:popUpTo="#+id/main_navigation" has the same id as your navigation xml
So the final action should look like:
<action
android:id="#+id/action_cameraFragment_to_searchFragment"
app:destination="#id/searchFragment"
app:launchSingleTop="true"
app:popUpTo="#+id/main_navigation"
app:popUpToInclusive="true"/>
And Navigate normally using the action
val action = CameraFragmentDirections.actionCameraFragmentToSearchFragment()
findNavController().navigate(action)

Android BottomNavigationView Tab Indicator Not Consistent with Mutiple Back Stack [duplicate]

I'm using BottomNavigationView with Navigation Component. When showing fragment is not root fragment, the tab icon is not updated (selected).
Example:
When I switch between Tab Home with Fragment A (which is root fragment) and Tab Star with Fragment B (which is also root fragment) it is working fine.
But when I navigate from Tab Home to another fragment, like fragment A2, and tap on Tab Star and again return to Tab Home, still Tab Star is selected in BottomNavigationView.
It was working fine with version 2.4.0-alpha05, This is happening when I updated it to 2.5.0-alpha01.
build.gradle (app)
implementation "androidx.navigation:navigation-fragment-ktx:2.5.0-alpha01"
implementation "androidx.navigation:navigation-ui-ktx:2.5.0-alpha01"
implementation "androidx.navigation:navigation-dynamic-features-fragment:2.5.0-alpha01"
build.gradle (root)
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-alpha01"
Graph:
<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/graph"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="ui.test.FragmentA"
tools:layout="#layout/fragment_test"
android:label="FragmentA" >
<action
android:id="#+id/action_fragmentA_to_fragmentA2"
app:destination="#id/fragmentA2" />
</fragment>
<fragment
android:id="#+id/fragmentA2"
android:name="ui.test.FragmentA2"
tools:layout="#layout/fragment_test"
android:label="FragmentA2" />
<fragment
android:id="#+id/fragmentB"
android:name="ui.test.FragmentB"
tools:layout="#layout/fragment_test"
android:label="FragmentB" />
</navigation>
Menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/fragmentA"
android:icon="#drawable/ic_home"
android:title="" />
<item
android:id="#+id/fragmentB"
android:icon="#drawable/ic_star"
android:title="" />
</menu>
Am I doing something wrong? or this is bug?
How can I resolve this problem?
So what worked for me was the solution that ianhanniballake hinted at in his answer: using setOnItemSelectedListener.
// always show selected Bottom Navigation item as selected (return true)
bottomNavigationView.setOnItemSelectedListener { item ->
// In order to get the expected behavior, you have to call default Navigation method manually
NavigationUI.onNavDestinationSelected(item, navController)
return#setOnItemSelectedListener true
}
This will always select the item and navigate to the associated destination while maintaining multiple back stacks.
Given your navigation graph, there is no way to associate fragmentA2 with your menu item fragmentA, so fragmentA is not selected when you return to fragmentA2. As per this issue:
NavigationUI has always used the current destination and what graph it is part of as the source of truth for what tab should be selected.
This can be seen by calling navigate() to go to your SecondFragment - even though you haven't used the bottom nav button, the selected tab was changed because the current destination has changed to R.id.frag_second.
So when you navigate() to R.id.frag_hint via your button in HomeFragment, NavigationUI receives a callback that the current destination has changed to R.id.frag_hint. It looks at that NavDestination and notes that there's no menu item that matches R.id.frag_hint. It then looks at the destination's parent graph - your R.id.sample <navigation> element. There's no menu item that matches that ID either, so NavigationUI can't associated that destination with any menu item and therefore simply does nothing. That is true on all versions of Navigation.
So what is different when you tap on a bottom navigation item? Well, nothing different from a NavigationUI perspective in fact: the exact same code runs and the current destination and what graph it is part of is the source of truth for what tab should be selected. In the Navigation 2.3.5, there was no state saved for each tab, so it only 'worked' because selecting a tab forced the ID of the current destination to match the destination of the menu item you just tapped.
So what you're seeing in your sample app is that there's no link between R.id.frag_hint and any menu item, which means NavigationUI does nothing. If you want to link R.id.frag_hint to your Home tab, then that's exactly what a nested navigation graph can be used for.
I.e., your navigation graph should instead look like:
<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/sample"
app:startDestination="#id/home">
<navigation
android:id="#+id/home"
app:startDestination="#id/frag_home">
<fragment
android:id="#+id/frag_home"
android:name="eu.rekisoft.android.navbug.HomeFragment"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/cause_bug"
app:destination="#id/frag_hint"/>
</fragment>
<fragment
android:id="#+id/frag_hint"
android:name="eu.rekisoft.android.navbug.HintFragment"
android:label="Hint"
tools:layout="#layout/fragment_hint"/>
</navigation>
<fragment
android:id="#+id/frag_second"
android:name="eu.rekisoft.android.navbug.SecondFragment"
android:label="Second Fragment"
tools:layout="#layout/fragment_second"/>
</navigation>
And your menu XML should be updated to use android:id="#id/home" to match your navigation graph.
Now, when you select the Home bottom nav item, the current destination changes to R.id.frag_hint (as your state was restored due to Navigation 2.4's support for multiple back stacks) and NavigationUI looks at the ID - R.id.frag_hint still doesn't match any menu item, but now the parent graph's ID, R.id.home does match a menu item - your Home menu item, hence, it becomes selected.
The intention that your navigation graph and its structure drives the UI is a key part of how NavigationUI works and is working as intended (there was a bug on earlier versions of Navigation 2.4 that broke this driving principle, but that has since been fixed in beta02). All of NavigationUI is built on public APIs specifically so that if you want to use some different logic for which bottom nav item is selected that is independent from your navigation graph structure, you can absolutely do that.
You'll note from the source code that you can call the public onNavDestinationSelected with your MenuItem to get the exact same navigate() logic which retaining your own ability to return any value from the setOnItemSelectedListener (which is what controls whether the tab becomes selected). Similarly, your own OnDestinationChangedListener can choose to look at the hierarchy of a destination to choose whether to change the selected bottom nav item or not.
So your graphs should also be using nested graphs for each tab:
<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/graph"
app:startDestination="#id/graphA">
<navigation
android:id="#+id/graphA"
app:startDestination="#id/fragmentA">
<fragment
android:id="#+id/fragmentA"
android:name="ui.test.FragmentA"
tools:layout="#layout/fragment_test"
android:label="FragmentA" >
<action
android:id="#+id/action_fragmentA_to_fragmentA2"
app:destination="#id/fragmentA2" />
</fragment>
<fragment
android:id="#+id/fragmentA2"
android:name="ui.test.FragmentA2"
tools:layout="#layout/fragment_test"
android:label="FragmentA2" />
</navigation>
<navigation
android:id="#+id/graphB"
app:startDestination="#id/fragmentB">
<fragment
android:id="#+id/fragmentB"
android:name="ui.test.FragmentB"
tools:layout="#layout/fragment_test"
android:label="FragmentB" />
</navigation>
</navigation>
Menu:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/graphA"
android:icon="#drawable/ic_home"
android:title="" />
<item
android:id="#+id/graphB"
android:icon="#drawable/ic_star"
android:title="" />
</menu>

BottomNavigationView icons not highlighted with Navigation 2.4

I updated to Navigation 2.4 (def nav_version = "2.4") and now when tapping on a BottomNavigationView item it does not always highlight the icon or show the fragment the BottomNavigationView item id points to.
There are 3 bottom navigation tabs called Home, Actions, My Progress. From the Home fragment, you can navigate to SubFragment.
So the flow might be to start at Home --> go to SubFragment --> go to Actions with the BottomNavigationView --> and then tap on Home to go back. What this did before the update was open the Home fragment (desired behavior). Now when tapping on the Home icon it shows SubFragment and does not highlight the icon.
More details
This is the navController setup:
bottomNavigationView = findViewById(R.id.bottom_navigation_view)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
bottomNavigationView.setupWithNavController(navController)
The nav_graph structure is like this:
<fragment
android:id="#+id/home"
android:name="com.example.app.home"
android:label="Home"
tools:layout="#layout/home"
<action
android:id="#+id/action_home_fragment_to_sub_fragment"
app:destination="#id/sub_fragment"/>
</fragment>
<fragment
android:id="#+id/subfragment"
android:name="com.example.app.subfragment"
android:label="Sub Fragment"
tools:layout="#layout/subfragment" />
<fragment
android:id="#+id/actions"
android:name="com.example.app.actions"
android:label="Actions"
tools:layout="#layout/actions" />
<fragment
android:id="#+id/myprogress"
android:name="com.example.app.myprogress"
android:label="My Progress"
tools:layout="#layout/myprogress" />
The menu items id's for the BottomNavigationView are identical to nav_graph.
I thought the issue might be with the nav_graph structure not playing well with the new SDK, so I tried adjusting the structure of the nav_graph so that each navigation tab was within its own like this question answer has setup. It highlights the tab with this approach but still does not navigate to Home like the example above. Only to SubFragment.
Ideas on what I might be missing are appreciated!
Haven't figured out a fix for the issue yet, so downgraded to def nav_version = "2.3.5" and navigation works properly again.
This is the version before Navigation 2.4 as mentioned by # ianhanniballake above in comments.
I also came across with this issue. It is not a bug in the library.
Actually, when you link menu.xml with nav_graph.xml
You only specified only one fragmentId for each destination. Therefore, it is natural for icon not to change its current state when subfragment is selected.
Instead, you should use nested navigation graphs and use the id of that graph for menu.xml.
<navigation
android:id="#+id/home_destination"
app:startDestination="#id/home" >
<fragment
android:id="#+id/home"
android:name="com.example.app.home"
android:label="Home"
tools:layout="#layout/home"
<action
android:id="#+id/action_home_fragment_to_sub_fragment"
app:destination="#id/sub_fragment"/>
</fragment>
<fragment
android:id="#+id/subfragment"
android:name="com.example.app.subfragment"
android:label="Sub Fragment"
tools:layout="#layout/subfragment" />
</navigation>
<fragment
android:id="#+id/actions"
android:name="com.example.app.actions"
android:label="Actions"
tools:layout="#layout/actions" />
<fragment
android:id="#+id/myprogress"
android:name="com.example.app.myprogress"
android:label="My Progress"
tools:layout="#layout/myprogress" />
And when you are specifying the navigation_menu you will do like this:
<item
android:id="#+id/home_destination"
android:icon="#drawable/ic_read_quran"
android:title="#string/read_quran"
app:showAsAction="always" />

Is it possible to move the screen without connecting in nav_graph?

I am using BottomNavigation.
The transition from the screen of menu A to another screen, not the screen transition from menu A to another, is as follows.
menu A(fragment) -> B screen(fragment) -> C screen(fragment) -> B screen(fragment)
I hooked up these screen transitions in nav_graph
I am using BottomNavigation.
The transition from the screen of menu A to another screen, not the screen transition from menu A to another, is as follows.
menu A(fragment) -> B screen(fragment) -> C screen(fragment) -> D screen(fragment)
I hooked up these screen transitions in nav_graph
However, the screen transition from D to C was not connected, but it was possible to switch the screen using view.findNavController.navigate().
I thought transitioning the screen was impossible without connecting to the nav_graph.
How is this possible?
UPDATED
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<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/calendar">
<fragment
android:id="#+id/calendar"
android:name="com.example.writeweight.fragment.CalendarFragment"
android:label="fragment_calendar"
tools:layout="#layout/fragment_calendar" >
</fragment>
<fragment
android:id="#+id/list"
android:name="com.example.writeweight.fragment.WorkoutListFragment"
android:label="fragment_workout_list"
tools:layout="#layout/fragment_workout_list" />
<!-- menu A fragment -->
<fragment
android:id="#+id/write_home"
android:name="com.example.writeweight.fragment.WriteRoutineHomeFragment"
android:label="fragment_write_routine_home"
tools:layout="#layout/fragment_write_routine_home" >
<action
android:id="#+id/action_write_home_to_bodyPartDialog"
app:destination="#id/bodyPartDialog" />
</fragment>
<!-- B screen -->
<dialog
android:id="#+id/bodyPartDialog"
android:name="com.example.writeweight.fragment.BodyPartDialogFragment"
android:label="BodyPartDialogFragment"
tools:layout="#layout/fragment_body_part_dialog">
<action
android:id="#+id/action_bodyPartDialog_to_write"
app:destination="#id/write"/>
</dialog>
<!-- C screen -->
<fragment
android:id="#+id/write"
android:name="com.example.writeweight.fragment.WritingRoutineFragment"
android:label="WritingRoutineFragment"
tools:layout="#layout/fragment_writing_routine">
<action
android:id="#+id/action_write_to_bodyPartDialog"
app:destination="#id/bodyPartDialog" />
<argument
android:name="title"
app:argType="string"
android:defaultValue="" />
</fragment>
<!-- D screen -->
<fragment
android:id="#+id/workoutListTabFragment"
android:name="com.example.writeweight.fragment.WorkoutListTabFragment"
android:label="fragment_workout_list_tab"
tools:layout="#layout/fragment_workout_list_tab" />
</navigation>
As per the Navigate using ID documentation:
navigate(int) takes the resource ID of either an action or a destination.
So both directly navigating to any destination is possible (by using the ID of the destination) and navigating via an action is supported.
The documentation goes on to say:
Note: When navigating using IDs, we strongly recommend using actions where possible. Actions provide additional information in your navigation graph, visually showing how your destinations connect to each other. By creating actions, you can replace resource IDs with Safe Args-generated operations, providing additional compile-time safety. By using an action, you can also animate transitions between the destinations. For more information, see Animate transitions between destinations.

pop up to graph startDestination fragment for a global action - Android navigation components

I've a multi-graph navigation app and I'd like to switch between graphs by using a global action as defined in my root main_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<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/main_graph"
app:startDestination="#id/loadingFragment">
<include app:graph="#navigation/learn_graph" />
<action
android:id="#+id/action_global_learn_graph"
app:destination="#id/learn_graph"
app:launchSingleTop="true"
/>
</navigation>
Since I'm trying to switch between graphs, I'd like to clear the back stack from the fragments loaded by the source graph (main_graph) when navigating the global action to the destination graph (explore_graph). The expected behavior would be to navigate to the startDestination fragment of the destination graph keeping only that fragment in the backstack.
For normal actions (actions in the same graph) I'm able to use popUpTo flag, how it's possible to get the same behavior for a global action?
After a lot of attemps, I found out a solution. The base idea is to pop up the backstack to the graph that "owns" the global action. In my case main_graph is the owner, so I did:
<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/main_graph"
app:startDestination="#id/loadingFragment">
<include app:graph="#navigation/learn_graph" />
<action
android:id="#+id/action_global_learn_graph"
app:destination="#id/learn_graph"
app:popUpTo="#+id/main_graph"
app:launchSingleTop="true" />
</navigation>
In addition, you have to set the app:launchSingleTop flag to true in order to make the instance of destination graph unique in your backstack
You can also include app:popUpToInclusive="true" to indicate that the destination specified in app:popUpTo should also be removed from the back stack.

Categories

Resources