Pass arguments safely to nested graph with Navigation Component
Asked Answered
H

2

23

I'm using Android Jetpack Navigation Component. I have a nested nav graph with id, say R.id.nested_graph The first Fragment of the nested graph receives one parameter.

<navigation
        android:id="@+id/nested_graph"
        android:label="Nested Graph"
        app:startDestination="@id/firstFragment">
        <fragment
            android:id="@+id/firstFragment"
            android:name="...."
            android:label="....">
            <argument
                android:name="item_id"
                app:argType="integer" />
        </fragment>
        [...]
    </navigation>

How can I pass the parameter to the nested graph using safe args?

At the moment, I need to pass the argument manually in the bundle, using the API that receives the id of the nested graph directly:

        val args = Bundle()
        args.putInt("item_id", itemId)
        navController.navigate(R.id.nested_graph, args)

I'd like to use safe args, and do something like:

        val directions = OrigininFragmentDirections.nestedGraph(itemId)
        navController.navigate(directions)

But when trying that, I get the following error at build time:

Too many arguments for public final fun nestedGraph(): NavDirections defined 

The issue is that the nav graph preprocessing is generating the factory method to create the NavDirections object without the required parameter in the signature.

The declaration of the nested graph looks like this:

Hawkie answered 18/12, 2019 at 12:12 Comment(1)
See also the Navigation subgraphs do not use the arguments of their startDestination issue on Google Issue TrackerBierman
H
24

After some trial and error experimentation (I don't think it's officially documented by Google, or at least I could not find it), I've discovered that navigating to nested nav graphs passing arguments safely can be done:

You need to add the argument XML object the first fragment expects in the root of the nested fragment itself.

In my case, the fragment with id firstFragment, which is the first fragment in the nested graph receives:

            <argument
                android:name="item_id"
                app:argType="integer" />

Hence, I need to add that argument to the nested graph:

<navigation
        android:id="@+id/nested_graph"
        android:label="Nested Graph"
        app:startDestination="@id/firstFragment">

            <argument
                android:name="item_id"
                app:argType="integer" />

        <fragment
            android:id="@+id/firstFragment"
            android:name="...."
            android:label="....">
            <argument
                android:name="item_id"
                app:argType="integer" />
        </fragment>
        [...]
    </navigation>

Now I can navigate to it with:

   val directions = OrigininFragmentDirections.nestedGraph(itemId)
        navController.navigate(directions)

Note that the navigation graph editor does not do it for you. This needs to be done manually in the XML code.

Hawkie answered 18/12, 2019 at 12:32 Comment(9)
your solution works, but I think it is more clear when you create action in OrigininFragment with destination to nested graph id and use it as normal action within graphChesney
what exactly do you mean with "use it as normal action within graph"? Can you pass the arguments safely like that, or you need to put them explicitly in the Bundle?Hawkie
from example above I'm not sure what "nestedGraph" is. What I ment was create <action> element within OriginFragment and destination with id of nested graph. This way directions action is created and you can safely pass arguments. As I'm looking on example maybe this .nestedGrahp is actually action only poorly named and cofusing, this indicates me that its some kind of "special" function within directionsChesney
hi! Is this solution still works? I cant make it workArchibald
@Archibald I cant make it work too, are u able to solve it?Olathe
@Olathe i solved it. u should copy fragment arguments to actionArchibald
@Archibald Can you show an example here? I am facing the same problem too.Shuping
I have just checked and this works. The key is that if the nested navigation graph receives arguments, these need to be declared twice: once in the root of the graph, and then as arguments of the root fragment of the graph.Hawkie
@Hawkie Also, the same arguments need to be added in the action of origin fragment. ` <action android:id="@+id/action_originfragment_to_nested_graph" app:destination="@id/nested_graph"> <argument android:name="item_id" app:argType="integer" /> </action> `Shuping
G
12

@GaRRaPeTa answer is almost correct, but if you navigate from main graph to nested graph by action using SafeArgs, you must also add an argument to the action:

<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/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.MainFragment">

        <action
            android:id="@+id/toNestedGraph"
            app:destination="@id/graph_nested">

            <argument
                android:name="arg_name"
                app:argType="string" />
        </action>
    </fragment>

</navigation>
Gravelly answered 18/2, 2021 at 11:33 Comment(2)
If you use a nested graph but keep it in the same file the answer of @Hawkie is sufficient and might be preferrable. However, if the nested graph is moved to its own file and included in the main graph using the "include" tag, the arguments must be copied to the action like in this answer. In the latter case, I didn't need to copy the arguments to the nested graph.Shieh
This one somehow works, but breaks navArgs().Did

© 2022 - 2024 — McMap. All rights reserved.