Android Navigation Component#2: Pass Data Between Destinations

Shashank Mohabia
The Startup
Published in
6 min readApr 3, 2020

In the first blog of the series, we saw how to navigate between destinations using actions. Now let’s see how to pass data between the destinations.

Navigation allows you to attach data to a navigation operation by defining arguments for a destination. For example, a user profile destination might take a user ID argument to determine which user to display.

In general, you should strongly prefer passing only the minimal amount of data between destinations. For example, you should pass a key to retrieve an object rather than passing the object itself, as the total space for all saved states is limited on Android. If you need to pass large amounts of data, consider using a ViewModel as described in Share data between fragments (we will see this in a separate blog)

This will involve two steps:

  1. Defining destination arguments.
  2. Use Safe Args to pass data with type safety.

Define destination arguments

To pass data between destinations, first define the argument by adding it to the destination that receives it by following these steps:

  1. In the Navigation editor, click on the destination(resultFragment) that receives the argument.
  2. In the Attributes panel on the right, click Add (+) for the argument tab.
  3. In the Add Argument Link window that appears, enter the argument name, argument type, whether the argument is nullable, and a default value, if needed.
  4. Click Add. Notice that the argument now appears in the Arguments list in the Attributes panel.
  5. Next, click on the corresponding action that takes you to this destination. In the Attributes panel, you should now see your newly added argument in the Argument Default Values section.
  6. You can also see that the argument was added to XML. Click the Text tab to toggle to XML view, and notice that your argument was added to the destination that receives the argument. An example is shown below:
<fragment android:id="@+id/resultFragment" >
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="0" />
</fragment>

Supported argument types

The Navigation library supports the following argument types:

Note: References to resources are supported only in reference types. Using a resource reference in any other type results in an exception.

If an argument type supports null values, you can declare a default value of null by using android:defaultValue="@null".

When you choose one of the custom types, the Select Class dialog appears and prompts you to choose the corresponding class for that type. The Project tab lets you choose a class from your current project.

You can choose <inferred type> to have the Navigation library determine the type based on the provided value.

You can check Array to indicate that the argument should be an array of the selected Type value. Note that arrays of enums and arrays of resource references are not supported. Arrays are always nullable, regardless of the nullability of the underlying type. Arrays support a single default value, “@null” Arrays do not support any other default value.

Override a destination argument in an action

Destination-level arguments and default values are used by all actions that navigate to the destination. If needed, you can override the default value of an argument (or set one if it doesn’t already exist) by defining an argument at the action level. This argument must be of the same name and type as the argument declared in the destination.

The XML below declares an action with an argument that overrides the destination-level argument from the example above:

<action android:id="@+id/action_gameFragment_to_resultFragment"
app:destination="@+id/resultFragment">
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
</action>

Use Safe Args to pass data with type safety

The Navigation component has a Gradle plugin called Safe Args that generates simple object and builder classes for type-safe navigation and access to any associated arguments. Safe Args is strongly recommended for navigating and passing data because it ensures type-safety.

In some cases, for example, if you are not using Gradle, you can’t use the Safe Args plugin. In these cases, you can use Bundles to directly pass data.

To add Safe Args to your project, include the following classpath in your project level build.gradle file:

buildscript {
repositories {
google()
}
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}

You must also apply one of two available plugins.

To generate Java language code suitable for Java or mixed Java and Kotlin modules, add this line to your app or module’s build.gradle file:

apply plugin: "androidx.navigation.safeargs"

Alternatively, to generate Kotlin code suitable for Kotlin-only modules add:

apply plugin: "androidx.navigation.safeargs.kotlin"

You must have android.useAndroidX=true in your gradle.properties file as per Migrating to AndroidX.

After enabling Safe Args, your generated code contains the following type-safe classes and methods for each action as well as with each sending and receiving destination.

  • A class is created for each destination where an action originates. The name of this class is the name of the originating destination, appended with the word “Directions”. For example, if the originating destination is a fragment that is named ResultFragment, the generated class would be called ResultFragmentDirections. This class has a method for each action defined in the originating destination.
  • For each action used to pass the argument, an inner class is created whose name is based on the action. For example, if the action is called confirmationAction, the class is named ConfirmationAction. If your action contains arguments without adefaultValue, then you use the associated action class to set the value of the arguments.
  • A class is created for the receiving destination. The name of this class is the name of the destination, appended with the word “Args”. For example, if the destination fragment is named ConfirmationFragment, the generated class is called ConfirmationFragmentArgs. Use this class's fromBundle() method to retrieve the arguments.

The following example shows you how to use these methods to set an argument and pass it from the onClickListener:

navigate_button.setOnClickListener {
val action = GameFragmentDirections
.actionGameFragmentToResultFragment(1000 //random data)
NavHostFragment.findNavController(this).navigate(action)
}

In your receiving destination’s code, use the getArguments() method to retrieve the bundle and use its contents. When using the -ktx dependencies, Kotlin users can also use the by navArgs() property delegate to access arguments.

val args: ResultFragmentArgs by navArgs()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Toast.makeText(activity, args.myArg.toString(), Toast.LENGTH_LONG).show()
}

Use Safe Args with a global action

When using Safe Args with global action, you must provide an android:id value for your root <navigation> element, as shown in the following example:

<?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"
android:id="@+id/main_nav"
app:startDestination="@id/mainFragment">
...</navigation>

Navigation generates a Directions class for the <navigation> the element that is based on the android:id value. For example, if you have a <navigation> element with android:id=@+id/main_nav, the generated class is called MainNavDirections. All destinations within the <navigation> element has generated methods for accessing all associated global actions using the same methods as described in the previous section.

Pass data to the start destination

You can pass data to your app’s start destination. First, you must explicitly construct a Bundle that holds the data. Next, use one of the following methods to pass the Bundle to the start destination:

To retrieve the data in your start destination, call Fragment.getArguments().

For passing data between destinations, try to avoid using the Bundle approach and use safe args because it is not only easy but also type-safe.

In the next part of this series, we will see the way in which we can create a navigation drawer and toolbar using the navigation component.

Feel free to comment if there are doubts or if you feel anything needs to be corrected😀.

References:

Android Navigation Documentation

--

--