I am trying to navigate from one navigation graph (with the host fragment on the LoginActivity) to another navigation graph (with the host fragment on the HomeActivity). I know Google is advocating for single activity applications and thus I am only going to be using these two Activities (to keep it minimal).
My login_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/mobile_navigation"
app:startDestination="#id/nav_login">
<fragment
android:id="#+id/nav_login"
android:name="your.appname.LoginFragment"
android:label="#string/login_menu"
tools:layout="#layout/fragment_login" >
<action
android:id="#+id/action_nav_login_to_enterCellphone"
app:destination="#id/login_enter_cellphone"
app:popUpTo="#id/nav_login" />
</fragment>
<fragment
android:id="#+id/login_enter_cellphone"
android:name="your.appname.LoginEnterCellphoneFragment"
android:label="#string/enter_cellphone_fragment_label"
tools:layout="#layout/fragment_login_enter_cellphone" >
<action
android:id="#+id/action_enter_cellphone_to_enterOTPFragment"
app:destination="#id/enterOTPFragment"
app:popUpTo="#id/login_enter_cellphone" />
</fragment>
<fragment
android:id="#+id/enterOTPFragment"
android:name="your.appname.EnterOTPFragment"
android:label="#string/enter_otp_label"
tools:layout="#layout/fragment_enter_o_t_p" >
<action
android:id="#+id/action_enterOTPFragment_to_main_navigation"
app:destination="#id/main_navigation"
app:launchSingleTop="false" />
</fragment>
<include app:graph="#navigation/main_navigation" />
</navigation>
And main_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/main_navigation"
app:startDestination="#id/nav_home">
<fragment
android:id="#+id/nav_home"
android:name="your.appname.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/nav_profile"
android:name="your.appname.ProfileFragment"
android:label="#string/profile_menu"
tools:layout="#layout/fragment_profile" />
</navigation>
I found this answer and implemented it as follows
private fun navigateToHomeActivity() {
val action = EnterOTPFragmentDirections.actionEnterOTPFragmentToMainNavigation()
findNavController().navigate(action)
(activity as LoginActivity).finish() <-- later commented out
}
but it gives me the following error E/InputMethodManager: prepareNavigationBarInfo() rootView is null
If I comment out the above line the error goes away but it only replaces the current fragment with the HomeFragment.
How do I navigate to another Activity so that the source Activity is removed (Can't navigate back to it) and the Destination Activity is used?
You can define 2 navigation graphs:
ActivityA with navigation Graph A
ActivityB with navigation Graph B
In the GraphA you can define a destination to start ActivityB
<!-- Graph A -->
<navigation
....
<activity
android:id="#+id/activityB"
android:name=".ActivityB"
...>
<argument
.... />
</activity>
</navigation>
Then you can navigate to that activity ( = startActivity(intent)) using it like a destination:
Navigation.findNavController(view).navigate(R.id.activityB);
More info here.
Related
Given I have a Jetpack Navigation Graph as below, where by
BlankFragment1 -> BlankFragment2 -> BlankFragment3 -> BlankFragment4 -> BlankFragment5
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
app:startDestination="#id/blankFragment1">
<fragment
android:id="#+id/blankFragment1"
android:name="com.example.cashdog.cashdog.BlankFragment1"
android:label="#string/label_blank1"
tools:layout="#layout/fragment_blank1" >
<action
android:id="#+id/action_blankFragment1_to_blankFragment2"
app:destination="#id/blankFragment2" />
</fragment>
<fragment
android:id="#+id/blankFragment2"
android:name="com.example.cashdog.cashdog.BlankFragment2"
android:label="#string/label_blank2"
tools:layout="#layout/fragment_blank2" >
<action
android:id="#+id/action_blankFragment2_to_blankFragment3"
app:destination="#id/blankFragment3" />
</fragment>
<fragment
android:id="#+id/blankFragment3"
android:name="com.example.cashdog.cashdog.BlankFragment3"
android:label="#string/label_blank3"
tools:layout="#layout/fragment_blank3" >
<action
android:id="#+id/action_blankFragment3_to_blankFragment4"
app:destination="#id/blankFragment4" />
</fragment>
<fragment
android:id="#+id/blankFragment4"
android:name="com.example.cashdog.cashdog.BlankFragment4"
android:label="#string/label_blank4"
tools:layout="#layout/fragment_blank4" >
<action
android:id="#+id/action_blankFragment4_to_blankFragment5"
app:destination="#id/blankFragment5" />
</fragment>
<fragment
android:id="#+id/blankFragment5"
android:name="com.example.cashdog.cashdog.BlankFragment5"
android:label="#string/label_blank5"
tools:layout="#layout/fragment_blank5" />
</navigation>
If I have another Fragment, named FragmentSomething, that can be open by and of the FragmentBlank1 to FragentBlank5
Is there a way to update the NavGraph above for my FragmentSomething, without the need to add actions for all 5 of them?
Apparently, there's something called the global action
https://developer.android.com/guide/navigation/navigation-global-action
We just need to add this
<action android:id="#+id/action_global_somethingFragment"
app:destination="#id/somethingFragment"/>
<fragment
android:id="#+id/somethingFragment"
android:name="com.example.cashdog.cashdog.SomethingFragment"
android:label="#string/label_something"
tools:layout="#layout/fragment_something" />
Or we can navigate the code directly to somethingFragment using
findNavController().navigate(R.id.somethingFragment)
I'm trying to pass a Parcelable argument to a nested graph, but the generated function takes 0 arguments.
I found this thread and I can't see the difference between what I have and what they have.
This is my XML, I removed code to show just the problem
logged_content_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/logged_content_nav_graph"
app:startDestination="#id/chooseCategoryFragment">
<fragment
android:id="#+id/productDetailFragment"
android:name="com.vendyapp.vendy.features.loggedcontent.productdetail.ui.ProductDetailFragment"
android:label="fragment_product_detail"
tools:layout="#layout/fragment_product_detail" >
<action
android:id="#+id/action_productDetailFragment_to_loginAndRegisterFragment"
app:destination="#id/login_nav_graph" />
<argument
android:name="product"
app:argType="com.vendyapp.vendy.features.loggedcontent.home.Product" />
</fragment>
<include app:graph="#navigation/login_nav_graph"/>
login_nav_graph.xml (NESTED)
<?xml version="1.0" encoding="utf-8"?>
<navigation android:id="#+id/login_nav_graph"
app:startDestination="#id/loginAndRegisterFragment"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<fragment
android:id="#+id/loginAndRegisterFragment"
android:name="com.vendyapp.vendy.features.login.ui.LoginAndRegisterFragment"
android:label="fragment_login_and_register"
tools:layout="#layout/fragment_login_and_register">
<argument
android:name="product"
app:argType="com.vendyapp.vendy.features.loggedcontent.home.Product"
app:nullable="true" />
<action
android:id="#+id/action_loginAndRegisterFragment_to_registerFragment"
app:destination="#id/registerFragment" />
<action
android:id="#+id/action_loginAndRegisterFragment_to_loginFragment"
app:destination="#id/loginFragment" />
</fragment>
</navigation>
With the nav graphs like this, in my Kotlin class I do
val action = ProductDetailFragmentDirections.actionProductDetailFragmentToLoginAndRegisterFragment(product)
findNavController().navigate(action)
But it says this function takes 0 arguments.
What am I getting wrong? Thanks in advance.
I've met a problem with the back stack while starting the app from a deep link (via notification).
The general idea is that I have 3 fragments (A, B, C). The A screen works as splash screen, so I've put a popUpTo and popUpToInclusive to the action, so I can never see back to it again. My deep link destination is fragment C. To achieve the proper back stack I've merged B and C into nested navigation. Unfortunately, when I press Back button in fragment B the app is showing fragment A. My guess this is because the action from A to B with popUpTo was never called when the app was started from the deep link itself. My question is: how to avoid going back from B to A in this particular scenario?
Thanks in advance! I attach the sample code:
<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/splashFragment">
<fragment
android:id="#+id/A"
android:name="A"
android:label="A" >
<action
android:id="#+id/action_A_to_B"
app:destination="#id/B"
app:popUpTo="#id/A"
app:popUpToInclusive="true" />
</fragment>
<navigation android:id="#+id/nested_nav"
app:startDestination="#id/B">
<fragment
android:id="#+id/B"
android:name="B"
android:label="B">
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C" />
</fragment>
<fragment
android:id="#+id/C"
android:name="C"
android:label="C">
</fragment>
</navigation>
</navigation>
And my DeepLinkBuilder (nothing special, same as the docs)
NavDeepLinkBuilder(requireContext())
.setGraph(R.navigation.nav_graph)
.setDestination(R.id.userProfileFragment)
.createPendingIntent()
.send()
You have set the navGraph that contain the splash screen as the default startDestination which lead to show it when you press back button from the nested navGraph , I think the easiest solution is to do the following :
First edit you nav file to have only one navigation 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/nav_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/A"
android:name="A"
android:label="A" >
<action
android:id="#+id/action_A_to_B"
app:destination="#id/B"
app:popUpTo="#id/A"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/B"
android:name="B"
android:label="B">
<action
android:id="#+id/action_B_to_C"
app:destination="#id/C" />
</fragment>
<fragment
android:id="#+id/C"
android:name="C"
android:label="C">
</fragment>
</navigation>
Secondly set a listener to navController to listen when a destination change occur , like this :
yourNavController.setOnDestinationChangedListener(this);
override onDestinationChangedListener() method and check if the fragment which about to get showed is A , then finish the app like :
#override
public void onDestinationChanged(NavController controller, NavDestination destination, Bundle arguments){
if(destination.getId()==R.id.splashFragmentId){
finish();
}
}
<?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/restaurantListFragment">
<fragment
android:id="#+id/scanQRFragment"
android:name="com.example.dine.ui.view.home.scan_qr.ScanQRFragment"
android:label="fragment_scan_q_r"
tools:layout="#layout/fragment_scan_q_r" />
<fragment
android:id="#+id/restaurantListFragment"
android:name="com.example.dine.ui.view.home.restaurants.RestaurantListFragment"
android:label="fragment_restaurant_list"
tools:layout="#layout/fragment_restaurant_list" >
<action
android:id="#+id/action_restaurantListFragment_to_restaruntDetailFragment"
app:destination="#id/restaruntDetailFragment"
app:popUpTo="#id/restaurantListFragment" />
</fragment>
<fragment
android:id="#+id/myPlateFragment"
android:name="com.example.dine.ui.view.home.my_plate.MyPlateFragment"
android:label="fragment_my_plate"
tools:layout="#layout/fragment_my_plate" />
<fragment
android:id="#+id/accountFragment"
android:name="com.example.dine.ui.view.home.account.AccountFragment"
android:label="fragment_account"
tools:layout="#layout/fragment_account" />
<fragment
android:id="#+id/restaruntDetailFragment"
android:name="com.example.dine.ui.view.restaurant_detail.RestaruntDetailFragment"
android:label="RestaruntDetailFragment"
tools:layout="#layout/fragment_restarunt_detail" />
</navigation>
Here is the navigation graph file I have used for this navigation fragments.
private fun onItemSelected() {
findNavController().navigate(R.id.action_restaurantListFragment_to_restaruntDetailFragment)
}
I called the `onItemSelected() method on a action, any help would be great. thanks
While Navigating from Restaurant list to RestaurantDetail fragment It comes like this. I have been suffering from this issue so long
I have implemented a nested graph in Navigation graph, which is having 2 graphs. In the first graph, there are 3 fragments and in the second graph, there are 2 fragments. Graph 2 is included in graph 1. I want to navigate to (graph 1 step 1) to (graph 2 step 2). We can not define action between two nested fragments. So is there any way by which we can assign the dynamic destination to navigation?
Graph 1
<?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/mobile_navigation"
app:startDestination="#id/dashboardFragment">
<fragment
android:id="#+id/dashboardFragment"
android:name="com.navigationgraphexample.fragments.DashboardFragment"
android:label="fragment_dashboard"
tools:layout="#layout/fragment_dashboard" >
<action
android:id="#+id/action_dashboardFragment_to_stepOneFragment2"
app:destination="#id/stepOneFragment" />
<action
android:id="#+id/action_dashboardFragment_to_sub_nav"
app:destination="#id/sub_nav" />
</fragment>
<fragment
android:id="#+id/stepOneFragment"
android:name="com.navigationgraphexample.fragments.StepOneFragment"
android:label="fragment_step_one"
tools:layout="#layout/fragment_step_one">
<action
android:id="#+id/action_stepOneFragment_to_stepTwoFragment"
app:destination="#id/stepTwoFragment" />
</fragment>
<fragment
android:id="#+id/stepTwoFragment"
android:name="com.navigationgraphexample.fragments.StepTwoFragment"
android:label="fragment_step_two"
tools:layout="#layout/fragment_step_two" />
<fragment
android:id="#+id/notificationFragment"
android:name="com.navigationgraphexample.fragments.NotificationFragment"
android:label="fragment_notification"
tools:layout="#layout/fragment_notification" >
<deepLink
android:id="#+id/deepLink"
app:uri="myapp.com/{myargs}"
/>
<argument android:name="myargs"
app:argType="integer"
android:defaultValue="1" />
</fragment>
<include app:graph="#navigation/sub_nav" />
</navigation>
Graph 2
<?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/sub_nav"
app:startDestination="#id/createNewTaskFragment">
<fragment
android:id="#+id/listFragment"
android:name="com.navigationgraphexample.fragments.ListFragment"
android:label="fragment_list"
tools:layout="#layout/fragment_list" />
<fragment
android:id="#+id/createNewTaskFragment"
android:name="com.navigationgraphexample.fragments.CreateNewTaskFragment"
android:label="fragment_create_new_task"
tools:layout="#layout/fragment_create_new_task" >
<action
android:id="#+id/action_createNewTaskFragment_to_listFragment"
app:destination="#id/listFragment" />
</fragment>
</navigation>
I have checked this solution but it is not for the nested graph!
As far as class NavGraph extends NavDestination, you can try the following to change nested graph's startDestination before calling navigate to sub_nav:
val graph = navController.graph.findNode(R.id.sub_nav)
if (graph is NavGraph) {
graph.startDestination = R.id.createNewTaskFragment
}
UPD: Since version 2.4.0 all Navigation artifacts have been rewritten in Kotlin, so the setter should be accessed by it's original name:
graph.setStartDestination(R.id.createNewTaskFragment)