Android navigation component open a modal graph by deep link - android

I am using the single activity approach with the navigation component. I have the main activity which is a container for the main fragment. The main fragment contains a container for all other fragments in the app and the bottom navigation. I've created several graphs to allow each tab to own its own navigation back stack. The main problem I don't know how to add the ability to open a modal graph by the deep link. By clicking on the deep link, should be opened an info fragment, and clicking the back button should be shown the previous one not depending on the tab. For example, I have 4 tabs: home, search, tools, and account. If the search was opened, and the screen by deep link was shown, by clicking the back button search or any of its nested fragments should be opened. For now, in any case, the account tab will be opened because the deep link flow belongs to its nav graph. Here is the navigation code:
The app navigation for activity:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/nav_graph"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.app.flow.main.MainFragment"
android:label="MainFragment">
<deepLink app:uri="------/password/edit" />
<deepLink app:uri="------/confirmation" />
</fragment>
The main fragment navigation 1 graph per tab:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_nav_graph"
app:startDestination="#id/home_graph">
<include app:graph="#navigation/home_graph" />
<include app:graph="#navigation/search_graph" />
<include app:graph="#navigation/tools_graph" />
<include app:graph="#navigation/account_graph" />
</navigation>
And the part of the account graph which is responsible for deep link flow:
<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/account_graph"
app:startDestination="#id/accountFragment">
***************************
<fragment
android:id="#+id/checkLinkFragment"
android:name="com.app.flow.checklink.CheckLinkFragment"
android:label="CheckLinkFragment">
<deepLink app:uri="-----/confirmation" />
<action
android:id="#+id/action_checkLinkFragment_to_emailConfirmedFragment"
app:destination="#id/emailConfirmedFragment" />
<action
android:id="#+id/action_checkLinkFragment_to_confirmationExpiredFragment"
app:destination="#id/confirmationExpiredFragment" />
</fragment>
***********************
</navigation>

Related

Popping back to "home" using multiple back stacks

I have a project that's using multiple back stacks via Jetpack Navigation androidx.navigation:navigation-fragment-ktx:2.4.0-beta02. I'm investigating a way to navigate back home from another navigation graph. For simplicity, let's assume my main navigation graph contains two root destinations that get configured as bottom navigation destinations.
graph_main:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/graph_main"
app:startDestination="#+id/graph_home">
<include app:graph="#navigation/graph_home"/>
<include app:graph="#navigation/graph_settings"/>
</navigation>
graph_home contains a start destination and a details screen:
<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_home"
app:startDestination="#+id/home">
<fragment
android:id="#+id/home"
android:name="com.example.playground.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/action_home_to_details"
app:destination="#id/details"/>
</fragment>
<fragment
android:id="#+id/details"
android:name="com.example.playground.ui.home.DetailsFragment"
android:label="#string/title_details"
tools:layout="#layout/fragment_details"/>
</navigation>
graph_settings just contains a start destination with some action(?) to navigate back to the start destination of graph_home:
<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_settings"
app:startDestination="#+id/settings">
<fragment
android:id="#+id/settings"
android:name="com.example.playground.ui.settings.SettingsFragment"
android:label="#string/title_settings"
tools:layout="#layout/fragment_settings">
<!-- maybe? -->
<action
android:id="#+id/action_settings_to_home"
app:destination="#id/graph_home"/>
</fragment>
</navigation>
User launches the app to the home destination in graph_home.
User taps a list item, and system navigates to the details destination in graph_home.
User taps the "Settings" bottom navigation tab, and system navigates to the settings destination in graph_settings.
User taps a button on the settings screen that causes the system to pop all back stacks back to the home screen.
How can I set this up so that in step #4, the original home screen Fragment is shown (not a copy of it) and the back stacks are empty after navigating back home (tapping the system back button closes the app)?

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.

Pressing back button exits the app instead of navigating to the previous screen while using navigation component

I am using the navigation architecture component in my app and whenever I navigate to a screen and then try to return to the previous screen using the back button, instead of navigating back the app exits.
My Navigation Graph looks like this:
<?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_nav_host_fragment"
app:startDestination="#id/authFragment">
<fragment
tools:layout="#layout/fragment_main_screen"
android:id="#+id/sellerListScreenFragment"
android:name="com.example.paylater.ui.SellerMainScreenFragment"
android:label="SellerListScreenFragment" >
<action
android:id="#+id/action_sellerListScreenFragment_to_creditFragment"
app:destination="#id/credit_nav_graph" />
</fragment>
<fragment
android:id="#+id/authFragment"
android:name="com.example.paylater.ui.AuthFragment"
android:label="AuthFragment"
tools:layout="#layout/fragment_auth" >
<action
android:id="#+id/action_authFragment_to_sellerListScreenFragment"
app:destination="#id/sellerListScreenFragment" />
</fragment>
<navigation android:id="#+id/credit_nav_graph"
app:startDestination="#id/creditFragment">
<fragment
android:id="#+id/creditAddFragment"
android:name="com.example.paylater.ui.CreditAddFragment"
android:label="CreditAddFragment"
tools:layout="#layout/fragment_credit_add">
<action
android:id="#+id/action_creditAddFragment_self"
app:destination="#id/creditAddFragment" />
<action
android:id="#+id/action_creditAddFragment_to_transactionResultFragment"
app:destination="#id/transactionResultFragment" />
</fragment>
<fragment
android:id="#+id/transactionResultFragment"
android:name="com.example.paylater.ui.TransactionResultFragment"
android:label="TransactionResultFragment"
tools:layout="#layout/fragment_transaction_result">
<action
android:id="#+id/action_transactionResultFragment_self"
app:destination="#id/transactionResultFragment" />
</fragment>
<fragment
android:id="#+id/creditFragment"
android:name="com.example.paylater.ui.CreditFragment"
android:label="CreditFragment"
tools:layout="#layout/fragment_credit_enter_no">
<action
android:id="#+id/action_creditFragment_to_creditAddFragment"
app:destination="#id/creditAddFragment" />
<action
android:id="#+id/action_creditFragment_self"
app:destination="#id/creditFragment" />
</fragment>
</navigation>
<action android:id="#+id/action_global_sellerListScreenFragment" app:destination="#id/sellerListScreenFragment"/>
</navigation>
I've tried to fix this using a custom onBackPressedDispatcher callback, but on top of leading to some strange behaviour it is also tedious to do it for every fragment and seems to be too much work for something that should be handled by the navigation component.
So is there a way to fix this problem as I have gone through my code but there doesn't seem to be anything that influences the navigation behaviour yet it doesn't work as intended.
Thanks for helping out!
Edit :-
As asked, I am providing the setup using which I navigate from , for example, SellerMainScreenFragment to CreditFragment
binding.fabCredit.setOnClickListener {
findNavController().
navigate(R.id.action_sellerListScreenFragment_to_creditFragment)
}
Here I use a FAB to navigate from SellerMainScreenFragment to CreditFragment.
But after navigating to CreditFragment, on pressing the back button the app just exits instead of navigating back.
I think you forgot to add app:defaultNavHost="true" to the NavHostFragment in your main Activity layout:
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
From the documentation:
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.
https://developer.android.com/guide/navigation/navigation-getting-started

Android Is it possible to have multiple nav_graph files?

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.

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