Navigation Architecture Component- Passing argument data to the startDestination

42,055

Solution 1

OK, I found a solution to that problem thanks to Ian Lake from the Google team. Let say you have an activity A that will start activity B with some intent data and you want to get that data in the startDestination you have two options here if you using safe args which is my case you could do

StartFragmentArgs.fromBundle(requireActivity().intent?.extras)

to read the args from the Intent. If you don't use safe args you can extract the data from the bundle your self-using requireActivity().intent?.extras which will return a Bundle you can use instead of the fragment getArguments() method. That's it I try it and everything works fine.

Solution 2

TLDR: You have to manually inflate the graph, add the keys/values to the defaultArgs, and set the graph on the navController.

Step 1

The documentation tells you to set the graph in the <fragment> tag in your Activity's layout. Something like:

<fragment
    android:id="@+id/navFragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    app:graph="@navigation/nav_whatever"
    app:defaultNavHost="true"
    />

REMOVE the line setting the graph=.

Step 2

In the Activity that will be displaying your NavHostFragment, inflate the graph like so:

val navHostFragment = navFragment as NavHostFragment
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.nav_whatever)

Where navFragment is the id you gave your fragment in XML, as above.

Step 3 [Crucial!]

Create a bundle to hold the arguments you want to pass to your startDestination fragment and add it to the graph's default arguments:

val bundle = Bundle()
// ...add keys and values
graph.addDefaultArguments(bundle)

Step 4

Set the graph on the host's navController:

navHostFragment.navController.graph = graph

Solution 3

I think this has changed again with the 1.0.0 release. And Google has hidden this information very well in the official documentation. Or at least I struggled to find it, but stumbled upon it in the Migrate to the Navigation component guide. How to pass arguments to the start destination is mentioned here: Pass activity destination args to a start destination fragment

In short

  1. You have to set the navigation graph programatically:
findNavController(R.id.main_content)
    .setGraph(R.navigation.product_detail_graph, intent.extras)
  1. Don't set the graph in the NavHostFragment XML declaration.
  2. Read the extras from the receiver side:
val args by navArgs<ProductDetailsArgs>()  
val productId = args.productId

Update: Google has said that the official documentation for passing arguments to the initial navigation target is indeed missing. Hopefully this is added soon as part of the Navigation component documentation.

Solution 4

It had been fixed in 1.0.0-alpha07. See detail.

The solution is similar to Elliot Schrock's answer, but wrapping by official API.

We have to manually inflate NavHostFragment or graph

Use

NavHostFragment.create(R.navigation.graph, args)

Or

navController.setGraph(R.navigation.graph, args)

The args are the data we want to pass to start destination.

Solution 5

Following Pass data to the start destination section from official doc:

First, construct a Bundle that holds the data:

val bundle = Bundle().apply {
    putString(KEY, "value")
}

Next, use one of the following methods to pass the Bundle to the start destination:

  • If you're creating your NavHost programmatically

     NavHostFragment.create(R.navigation.graph, bundle)
    
  • Otherwise, you can set start destination arguments by calling one of the following overloads of NavController.setGraph():

     navHostFragment.navController.setGraph(R.navigation.graph, bundle)
    

Then you should use Fragment.getArguments() to retrieve the data in your start destination.

EDIT:

You can also use FragmentArgs instead of creating a bundle manually which makes it more convenient and type safe:

navHostFragment.navController.setGraph(R.navigation.graph, MyFragmentArgs(arg).toBundle())

Then in the fragment you can retrieve args as:

private val args: PodFragmentArgs by navArgs()

Make sure your fragment has argument element in the navigation.xml file:

<fragment
        android:id="@+id/myFragment"
        android:name="MyFragment"
        android:label="fragment_my"
        tools:layout="@layout/fragment_my">
        <argument
            android:name="argName"
            android:defaultValue="@null"
            app:argType="string"
            app:nullable="true" />
</fragment>
Share:
42,055
Ahmed Abdelmeged
Author by

Ahmed Abdelmeged

I'm an Android developer, blogger, Speaker, open-source contributor, Traveller and I’m well-regarded for writing efficient, clean, reusable and optimized code that prioritizes security. I am passionate about Kotlin, Android and helping others in the Community.

Updated on July 08, 2022

Comments

  • Ahmed Abdelmeged
    Ahmed Abdelmeged almost 2 years

    I have an activity A that start activity B passing to it some intent data. Activity B host a navigation graph from the new Navigation Architecture Component.I want to pass that intent data to the startDestination fragment as argument how to do that?

  • Ahmed Abdelmeged
    Ahmed Abdelmeged almost 6 years
    Your solution will work but quite complex i found a better way i will but in the other answer
  • sam33r
    sam33r over 5 years
    Where does StartFragmentArgs class comes from? Is it included in the navigation arch dependency?
  • Ahmed Abdelmeged
    Ahmed Abdelmeged over 5 years
    It's a generated class by the navigation. If you are using safe argument. For example the fragment here called StartFragment so the generated arg class will be StartFragmentArgs. If the fragment called FooFragment the generated class will be FooFragmentArgs and so on
  • Anmol
    Anmol over 5 years
    I tried using it and added this line in onCreate of Activity StartFragmentArgs.fromBundle(requireActivity().intent?.extra‌​s) but argument's sent to my startDestination are null
  • Ahmed Abdelmeged
    Ahmed Abdelmeged over 5 years
    When you start the activity that has the graph you are passing the data as intent and extract it in requireActivity().intent?.extras
  • Ahmed Abdelmeged
    Ahmed Abdelmeged over 5 years
    You need to use navigation safe args Gradle plugin
  • Dr. aNdRO
    Dr. aNdRO over 5 years
    Your answer is not complete. When you use SafeArgs to set the bundle it returns the class object itself setting all the bundle arguments. But you didn't told us how to get back that bundle in fragment. If we use fromBundle it ill return a new object always, so it won't have that value anymore when you try to get it and it will be null
  • Boonya Kitpitak
    Boonya Kitpitak over 5 years
    This solution is not working anymore in 1.0.0-alpha09. The addDefaultArgument function has been removed.
  • rupesh
    rupesh over 4 years
    Hi @Johan Paul Can you please tell me what is R.id.main_content in this?
  • Johan Paul
    Johan Paul over 4 years
    @rupesh It is the ID for the NavHostFragment where you would normally specify the navigation graph in the XML layout file.
  • wilmerlpr
    wilmerlpr over 4 years
    You are my hero!
  • Burak iren
    Burak iren over 4 years
    Thanks, saved my day :)
  • Praveen Singh
    Praveen Singh over 4 years
    addDefaultArgument has been removed from Navigation Architecture Component. Please have a look at my answer below to see the official recommended approach.
  • LiMuBei
    LiMuBei over 4 years
    This replaces the usual private val args: MyFragmentArgs by navArgs()
  • Pankaj Kumar
    Pankaj Kumar over 3 years
    Link broken. Update the link if your remember.
  • Nijat Ahmadli
    Nijat Ahmadli over 3 years
    @PankajKumar Link is updated, thanks for letting me know.
  • Adnan haider
    Adnan haider about 3 years
    can you give this solution in java?
  • Raghav Satyadev
    Raghav Satyadev about 2 years
    setGraph method is not working somehow