androidxmlandroid-resourcesandroid-architecture-navigationandroid-bundle

How to pass argument name in android navigation component?


I'm using a navigation graph to navigate between fragments. Everything works fine, but I wonder how to pass argument name in an elegant way.

Here is my code snippet from navSheet.xml:

A source fragment action:

    <action
        android:id="@+id/action_go_to_B"
        app:destination="@id/id_fragment_b">
        <argument
            android:name="@string/fragment_b_arg_key_name"
            app:argType="integer"/>
    </action>

A destination fragment:

<fragment
    android:id="@+id/id_fragment_b"
    android:name="pl.asd.FragmentB"
    tools:layout="@layout/fragment_b">

    <argument
        android:name="@string/fragment_b_arg_key_name"
        app:argType="integer"/>
</fragment>

I store my argument key in string.xml:

<string name="fragment_b_arg_key_name" translatable="false">arg_fragment_b</string>

And at the end I try to get my argument in destination fragment:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            selectedId = it.getInt(getString(R.string.fragment_b_arg_key_name))
        }
    }

The problem is with android:name="@string/fragment_b_arg_key_name". I've seen on the debugger that there is an incorrect bundle key. It should be like:

Bundle[{arg_fragment_b=-730732511}]

but I get:

Bundle[{@string/fragment_b_arg_key_name=-730732511}]

Two questions:

  1. What's wrong with using a @string/fragment_b_arg_key_name in navSheet.xml so it not returns the content of @string/fragment_b_arg_key_name but raw reference.

  2. How to not hard-code arguments name that I use in Fragments, navSheet.xml and other classes? How to store such keys in one place?


Solution

  • What's wrong with using a @string/fragment_b_arg_key_name in navSheet.xml so it not returns the content of @string/fragment_b_arg_key_name but raw reference.

    Simply because Android Navigation component does not support that feature to parse the string. Every string put in android:name will become the raw id for the bundle. You can issue this in https://issuetracker.google.com/issues

    However, there is better solution for this. See below.

    How to not hard-code arguments name that I use in Fragments, navSheet.xml and other classes? How to store such keys in one place?

    Use safe args https://developer.android.com/jetpack/androidx/releases/navigation#safe_args

    You can change your codes like below

    FragmentA

    findNavController().navigate(R.id.id_fragment_b, FragmentBArgs(id).toBundle())
    

    FragmentB

    private val args : FragmentBArgs by navArgs()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        selectedId = args.arg_fragment_b
    }
    

    in navSheet.xml:

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

    So, no need to store the keys, because safeargs will generate the arguments for you, and by default it is non-nullable.

    To put the argument as Nullable, use app:nullable="true"