Android Is it possible to have multiple nav_graph files? - android

So I was using Jetpack navigation and the number of fragments kept growing up.
We can separate fragments in different navigation graph as described in this document
jetpack nav graph docs
Then I tried to put different nav graphs in different files because that felt more organized and readable file but I get the following error when I try to navigate to different nav_graph files.
nav_graph_start.xml
<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_start"
app:startDestination="#id/splashScreen"
tools:ignore="UnusedNavigation">
<fragment
android:id="#+id/splashScreen"
android:name="com.timetoface.android.splash.SplashFragment"
android:label="Login Fragment"
tools:layout="#layout/fragment_splash">
<action
android:id="#+id/action_splash_to_login"
app:destination="#id/nav_graph_auth"
/>
<action
android:id="#+id/action_splash_to_home"
app:destination="#id/nav_graph_home"
/>
</fragment>
</navigation>
nav_graph_auth.xml
<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_auth"
app:startDestination="#id/emailLoginScreen"
tools:ignore="UnusedNavigation">
................................
</navigation>
nav_graph_home.xml
<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_home"
app:startDestination="#id/emailLoginScreen"
tools:ignore="UnusedNavigation">
................................
</navigation>
navigation destination com.app.android:id/nav_graph_home
referenced from action com.app.android:id/action_splash_to_home
is unknown to this NavController
So,
Are multiple navigation graph files not supported yet?
Am I missing something that I should change?

First of all you can use include. Take a look this
example: first_graph.xml
<include app:graph="#navigation/second_graph" />
then set action to included graph's id
<action
android:id="#+id/action_fragment_to_second_graph"
app:destination="#id/second_graph" />
Also you can use extension to use multiple graphs merged.
Take a look to this
Actually every activity should have it's own nav graph.

Ofcourse you can have multiple nav graph in your application. Each Activity can have only one Navigation Graph. I just added two Nav_Graph for different activity. Works fine. Here is a screenshot of my Navigation folder.

Related

Dynamic feature module navigation IllegalStateException: The included <navigation>'s id is different from the destination id

I have one app and one dynamic feature module i wish to navigate from
App nav graph
<?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/mainFragment">
<!-- Main Fragment from App Module -->
<fragment
android:id="#+id/mainFragment"
android:name="com.xyz.MainFragment"
android:label="MainFragment"
tools:layout="#layout/fragment_main">
<action
android:id="#+id/action_mainFragment_to_nav_graph_home"
app:destination="#id/nav_graph_home" />
</fragment>
<!-- Home Navigation from App Module-->
<include app:graph="#navigation/nav_graph_home" />
<include-dynamic
android:id="#+id/nav_graph_dashboard"
android:name="com.feature.dashboard"
app:graphResName="nav_graph_dashboard"
app:moduleName="dashboard" />
</navigation>
And feature navigation
<?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_dashboard"
app:moduleName="dashboard"
app:startDestination="#id/dashboardFragment1">
<fragment
android:id="#+id/dashboardFragment1"
android:name="com.feature.DashboardFragment1"
android:label="DashboardFragment1">
</fragment>
</navigation>
Returns error
java.lang.IllegalStateException: The included <navigation>'s id com.xyz.dashboard:id/nav_graph_dashboard is different from the destination id com.xyz:id/nav_graph_dashboard. Either remove the <navigation> id or make them match.
It seems that removing id from feature navigation solves the issue but i couldn't find how to make them match even though both <include-dynamic> and <navigation> have the same id android:id="#+id/nav_graph_dashboard" I don't need id for <navigation> for this example but i wonder if it's possible when <navigation> has one
Remove the + from your ID in your feature navigation graph:
android:id="#id/nav_graph_dashboard"
When you use the #+id syntax, you create a new ID in your dynamic feature's package (you'll note the exception explicitly calls out the package names for each resource ID for exactly that reason). By removing the +, you use the already created ID from the main module's package, which makes them match.
The same IllegalStateException can be achieved simply but including app:moduleName="app" in the root nav graph in the root module (usually app module). Take care to ensure that attribute is only set in dynamic feature module nav graphs.

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.

Android navigation component - navigating between included graphs of modules

I have a single activity application with 3 modules - app, list & detail. My activity is in app module, it's hosting the only NavHostFragment. All modules have their own navigation graphs. detail's starting point requires a long parameter. app's graph is parenting other graphs:
<?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"
android:id="#+id/nav_main"
app:startDestination="#id/nav_list">
<include app:graph="#navigation/nav_list" />
<include app:graph="#navigation/nav_detail" />
</navigation>
But by default it's disabled to add actions to included graphs on editor:
I can add a global action in xml file which then shows up in editor:
<?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"
android:id="#+id/nav_main"
app:startDestination="#id/nav_list">
<include app:graph="#navigation/nav_list" />
<include app:graph="#navigation/nav_detail" />
<action
android:id="#+id/action_global_detailFragment"
app:destination="#id/nav_detail" />
</navigation>
I don't want to use global actions but instead add proper actions which will encapsulate navigation pattern. Nested graphs already contain their navigation logic and only may need input for entry point. I'm not sure if this is not supported and I'm missing something or else why not ? What's the way to navigate between two or more included graphs ?
You have to set android:idto your included graphs, otherwise you cannot add actions.

Jetpack: Unable to transition to other navigation.xml's fragment

I'm currently trying to transition to a Fragment in navigation2.xml to navigation.xml. I thought Reference other navigation graphs with would do but doesn't work. If there is a sample or hints about transitioning between fragments of different navigation.xml files , I would love to hear from you!
<!-- (root) navigation.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/fragment">
<include app:graph="#navigation/included_graph" />
<fragment
android:id="#+id/fragment"
android:name="com.example.myapplication.BlankFragment"
android:label="Fragment in Root Graph"
tools:layout="#layout/fragment_blank">
<action
android:id="#+id/action_fragment_to_second_graph"
app:destination="#id/second_graph" />
</fragment>
...
</navigation>
second navigation file
<?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/second_graph"
app:startDestination="#id/includedStart">
<fragment
android:id="#+id/includedStart"
android:name="com.example.myapplication.IncludedStart"
android:label="fragment_included_start"
tools:layout="#layout/fragment_included_start" />
</navigation>
As per the Design navigation graphs documentation:
[Nested graphs] also provide a level of encapsulation — destinations outside of the nested graph do not have direct access to any of the destinations within the nested graph. Instead, they should navigate() to the nested graph itself, where the internal logic can change without affecting the rest of the graph.
Therefore you can use navigate(R.id.second_graph) without issue from anywhere in navigation.xml, but you can't directly access anything within that other graph.
Note that the one exception to this is navigating by URI, which allows you to navigate to any destination in your graph (whether nested or not) by navigating using an implicit deep link.

Navigation architecture: How to manage proper navigation without using clearTask as it is deprecated

While using navigation architecture as from here , here clearTask is deprecated.
My scenario is this: There are 2 screens Login and Registration, both have links to each other. So you can go to Registration from Login and also Login from Registration. But on back Press App should be closed.
It could simply be done by just adding clearTask to both actions as below.
<?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/nv_auth_phase"
app:startDestination="#id/fragment_login">
<fragment
android:id="#+id/fragment_login"
android:name="com.jobhook.ui.auth.login.LoginFragment"
android:label="LoginFragment"
tools:layout="#layout/fragment_login">
<action
android:id="#+id/nv_action_login_to_registration"
app:clearTask="true"
app:destination="#id/fragment_registration" />
</fragment>
<fragment
android:id="#+id/fragment_registration"
android:name="com.jobhook.ui.auth.registration.RegistrationFragment"
android:label="RegistrationFragment"
tools:layout="#layout/fragment_registration">
<action
android:id="#+id/nv_action_registration_to_login"
app:clearTask="true"
app:destination="#id/fragment_login" />
</fragment>
</navigation>
But as it was deprecated I have tried other solution like adding popUpTo -> navigation graph's Id, making launchSingleTop to true in both actions. Nothing seems to work in my scenario.
I have checked this question also but didn't get a solution.
You need to use in your action next code
app:popUpTo="#+id/fragment_login"
app:popUpToInclusive="true"
Set your NavHostFragment defaultNavHost value false,
<fragment
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="false"
app:navGraph="#navigation/nav_graph"
... />
The app:defaultNavHost="true" attribute ensures that your NavHostFragment intercepts the system Back button. Note that only one NavHost can be the default. If you have multiple hosts in the same layout (two-pane layouts, for example), be sure to specify only one default NavHost.
Simple and effective solution:
fun signOut(activity: Activity) = activity.finish()

Categories

Resources