I am using the navigation component from Jetpack.
My main graph is huge, so I have decided to split it into different graphs and use the <include /> functionality.
Problem is that some of the included graphs receive arguments, but Android seems to ignore the arguments when creating the navigation Directions
Ie:
Main Graph (simplified)
<?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">
<fragment
android:id="#+id/listFragment"
android:name="com......ListFragment">
<action
android:id="#+id/view_detail"
app:destination="#id/item_detail"/>
</fragment>
<include app:graph="#navigation/included_graph"/>
</navigation>
Included graph: (simplified)
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/item_detail"
app:startDestination="#id/itemDetailFragment">
<argument
android:name="item_id"
app:argType="integer" />
<fragment
android:id="#+id/itemDetailFragment"
android:name="com......ItemDetailFragment">
<argument
android:name="item_id"
app:argType="integer" />
</fragment>
[...]
</navigation>
The class generated by android is
class ListFragmentDirections private constructor() {
companion object {
fun viewDetail(): NavDirections =
ActionOnlyNavDirections(R.id.view_detail)
}
}
It seems the argument received by the included graph is ignored - I can't pass any argument safely.
Is there are workaround for this?
I'm interested in any alternative letting me breaking down the graph into smaller files, based on <include /> or not.
There is a workaround: include the argument to the action explicitly, i.e.
<?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">
<fragment
android:id="#+id/listFragment"
android:name="com......ListFragment">
<action
android:id="#+id/view_detail"
app:destination="#id/item_detail">
<argument
android:name="item_id"
app:argType="integer" />
</action>
</fragment>
<include app:graph="#navigation/included_graph"/>
</navigation>
Related
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.
<?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 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.
I have 2 modules in my app (app module and splash module). I'm trying to leverage the <include> tag in an effort to make the FTUE self contained and not a part of the main nav graph as the docs suggest.
My main nav graph is defined in the app module and 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/nav_graph"
app:startDestination="#id/home">
<include app:graph="#navigation/splash_nav_graph"/>
<fragment
android:id="#+id/home"
android:name="com.example.Home"
android:label="fragment_home"
tools:layout="#layout/fragment_home">
<action
android:id="#+id/action_home_to_splash_nav_graph"
app:destination="#id/splash_nav_graph"/>
</fragment>
</navigation>
splash_nav_graph is defined in the splash module (app module obviously has a dependency on it):
<?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/splash_nav_graph"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/splashFragment"
android:name="com.example.splash.SplashFragment"
android:label="example"
tools:layout="#layout/fragment_splash">
<action
android:id="#+id/action_splashFragment_to_signInFragment"
app:destination="#id/signInFragment"
app:enterAnim="#anim/slide_in_right"
app:exitAnim="#anim/slide_out_left"
app:popEnterAnim="#anim/slide_in_left"
app:popExitAnim="#anim/slide_out_right"/>
</fragment>
<fragment
android:id="#+id/signInFragment"
android:name="com.example.splash.signin.ui.SignInFragment"
android:label="fragment_sign_in"
tools:layout="#layout/fragment_sign_in">
<action
android:id="#+id/action_signInFragment_pop"
app:popUpTo="#id/splash_nav_graph"/>
</fragment>
</navigation>
Finally, here's the layout where the main nav graph is tied to the NavHostFragment back in the app module:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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/mainContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"
>
<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"
/>
</LinearLayout>
This will run and navigating through the flow (home-> splash -> sign in -> home) yields the expected behavior. However, the following lint error is present at the top of splash_nav_graph:
This navigation graph is not referenced from any layout files (expected to find it in at least one layout file with a NavHostFragment with app:navGraph="#navigation/splash_nav_graph" attribute). more... (⌘F1)
What the lint error says is true (I don't have another NavHostFragment that has app:navGraph="#navigation/splash_nav_graph") , however:
I am including it into another graph and it's working (thinking there is that this is somehow transitive and lint can't detect that)
I have no idea where to add another NavHostFragment because I only have a single activity in the app module.
Do I actually need to add another NavHostFragment somewhere or is this a bug in lint?
Looks like this will be fixed in AS 3.6
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)