Implement navigation with the Navigation Architecture Component

The Navigation Architecture Component simplifies the implementation of navigation between destinations in your app. A destination is a specific screen in an app. By default, the Navigation Architecture Component includes support fragments and Activities as destinations, but you can also add support for new types of destinations. A set of destinations compose an app’s “navigation graph.

In addition to destinations, a navigation graph has connections between destinations called “actions.” Figure 1 shows a visual representation of a navigation graph for a sample app containing 6 destinations connected by 5 actions.

Figure 1. A navigation graph

The Navigation Architecture Component is implemented based on the Principles of navigation.

Set up navigation in a project

Before you can create a navigation graph, you must set up the Navigation Architecture Component for your project. To set up your project in Android studio, perform the following steps.

  1. Add the following Navigation Architecture Component to your app or module's build.gradle file. For more information on adding Archtecture Components to build.gradle, refer to Adding components to your project.

  2. In the Project window, right-click on the res directory and select New > Android resource file. The New Resource dialog appears.

  3. Type a name in the File name field, such as "nav_graph".

  4. Select Navigation from the Resource type drop-down list.

  5. Click OK. The following occurs:

    1. A navigation resource directory is created within the res directory.
    2. A nav_graph.xml file is created within the navigation directory.
    3. The nav_graph.xml file opens in the Navigation Editor. This xml file contains your navigation graph.
  6. Click the Text tab to toggle to the XML text view. The XML for an empty navigation graph looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:android="http://schemas.android.com/apk/res/android">
    </navigation>
    
  7. Click Design to return to the Navigation Editor.

Tour the Navigation Editor

In the Navigation Editor, you can quickly build navigation graphs instead of building the graph's XML by hand. As shown in figure 2, the Navigation Editor has three sections:

Figure 2. The Navigation Editor

The Navigation Editor's sections are:

  1. The Destinations list - Lists all destinations currently in the Graph Editor.
  2. The Graph Editor - Contains a visual representation of your navigation graph.
  3. The Attributes Editor - Contains attributes associated with destinations and actions in your navigation graph.

Identify destinations

The first step in creating a navigation graph is to identify the destinations for your app. You can create blank destinations or create destinations from fragments and Activities in an existing project.

To identify destinations for your app, use the following steps.

  1. From the Graph Editor, click New Destination . The New Destination dialog appears.

  2. Click Create blank destination or click on a fragment or activity. The New Android Component dialog appears.

  3. Enter a name in the Fragment Name field. This name is the name of the fragment's class.

  4. Enter a name in the Fragment Layout Name field. This name is the name of the layout file for the fragment.

  5. Click Finish. A box representing the destination appears in the Graph Editor and in the list of Destinations. The following occurs:

    • If you created a blank destination, the Graph Editor displays the message “Hello blank fragment” in the destination. If you clicked on a fragment or activity, the Graph Editor displays a preview of the layout for that activity or fragment.

    • A Fragment subclass is created for your project. This class has the name you assigned in step 3.

    • A resource file is created for your project. This file has the name you assigned in step 4.

    Figure 3 shows a blank and an existing destination.

    Figure 3. New and existing destinations in the Graph Editor
  6. Click on the newly inserted destination to highlight the destination. The following attributes appear in the Attributes panel:

    • The Type field contains “Fragment” or “Activity” to indicate whether the destination is implemented as a fragment or activity in your source code.

    • The Label field contains the name of the destination’s XML layout file.

    • The ID field contains ID of the destination which will be used to refer to the destination in code.

    • The Class field contains the name of the class for the destination.

  7. Click the Text tab to toggle to the XML view. The XML now contains id, name (class name), label, and layout attributes based on names for the existing class and layout files:

    <?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"
        app:startDestination="@id/blankFragment">
        <fragment
            android:id="@+id/blankFragment"
            android:name="com.example.cashdog.cashdog.BlankFragment"
            android:label="Blank"
            tools:layout="@layout/fragment_blank" />
    </navigation>
    

Connect destinations

You must have more than one destination to connect destinations. Following is the XML for a navigation graph with two blank destinations:

<?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"
    app:startDestination="@id/blankFragment">
    <fragment
        android:id="@+id/blankFragment"
        android:name="com.example.cashdog.cashdog.BlankFragment"
        android:label="fragment_blank"
        tools:layout="@layout/fragment_blank" />
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.example.cashdog.cashdog.BlankFragment2"
        android:label="Blank2"
        tools:layout="@layout/fragment_blank_fragment2" />
</navigation>

Destinations are connected using actions. To connect two destinations:

  1. From the Graph Editor, hover over the right side of the destination that you want users to navigate from. A circle appears on the destination.

    Figure 4. Action connection circle
  2. Click and hold, drag your cursor over the destination you want users to navigate to, and release. A line is drawn to indicate navigation between the two destinations.

    Figure 5. Connected destinations
  3. Click on the arrow to highlight the action. The following attributes appear in the Attributes panel:

    • The Type field contains “Action.”
    • The ID field contains a system-assigned ID for the action.
    • The Destination field contains the ID for destination fragment or activity.
  4. Click the Text tab to toggle to the XML view. An action element has been added to the parent destination. The action has a system-assigned ID and destination attribute containing the ID of the next destination. For 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"
        app:startDestination="@id/blankFragment">
        <fragment
            android:id="@+id/blankFragment"
            android:name="com.example.cashdog.cashdog.BlankFragment"
            android:label="fragment_blank"
            tools:layout="@layout/fragment_blank" >
            <action
                android:id="@+id/action_blankFragment_to_blankFragment2"
                app:destination="@id/blankFragment2" />
        </fragment>
        <fragment
            android:id="@+id/blankFragment2"
            android:name="com.example.cashdog.cashdog.BlankFragment2"
            android:label="fragment_blank_fragment2"
            tools:layout="@layout/fragment_blank_fragment2" />
    </navigation>
    

    Designate a screen as the start destination

    The Graph Editor places an icon of a house next to the name of the first destination in your app. This icon indicates that this is the starting destination in the Navigation Graph. You can designate another destination as a starting destination using the following steps:

  5. From the Graph Editor, click on the destination. The destination is highlighted.

  6. Click Set Start Destination in the Attributes panel. The destination is now the start destination.

Modify an activity to host navigation

An activity hosts navigation for an app through the implementation of a NavHost interface which is added to your activity’s layout. The NavHost is an empty view whereupon destinations are swapped in and out as a user navigates through your app.

The Navigation Architecture Component’s default NavHost implementation is NavHostFragment.

After you have included a NavHost, you must associate your navigation graph with the NavHostFragment using the navGraph attribute. The following snippet shows how to include a NavHostFragment and associate a navigation graph with that NavHostFragment in an activity's layout file:

?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true"
        />

</android.support.constraint.ConstraintLayout>

The previous sample contains an app:defaultNavHost="true" attribute. This attribute ensures your NavHostFragment intercepts the system Back button. You will also overwrite AppCompatActivity.onSupportNavigateUp() and call NavController.navigateUp as follows:

Kotlin

override fun onSupportNavigateUp()
        = findNavController(R.id.nav_host_fragment).navigateUp()

Java

@Override
public boolean onSupportNavigateUp() {
    return Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp();
}

Tie destinations to UI widgets

Navigating to a destination is done using the NavController class. A NavController can be retrieved using one of the following static methods:

After you retrieve a NavController, use its navigate() method to navigate to a destination. The navigate() method accepts a resource ID. The ID can be the ID of a specific destination in the navigation graph or of an action. Using the ID of the action, instead of the resource ID of the destination, has advantages, such as associating transitions with your navigation. For more on transitions, refer to Create a transition between destinations.

The following code snippet shows how to navigate to the ViewTransactionsFragment:

Kotlin

viewTransactionsButton.setOnClickListener { view ->
   view.findNavController().navigate(R.id.viewTransactionsAction)
}

Java

viewTransactionsButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Navigation.findNavController(view).navigate(R.id.viewTransactionsAction);
    }
});

The Android system maintains a back stack containing the last visited destination. The first destination of your app is placed on the stack when the user opens the app. Each call to the navigate() method puts another destination on the top of the stack. Conversely, pressing the Up or Back button calls the NavController.navigateUp() and NavController.popBackStack() methods, respectively, to pop the top destination off of the stack.

For buttons, you can also use the Navigation class’s createNavigateOnClickListener() convenience method to navigate to a destination:

Kotlin

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null))

Java

button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.next_fragment, null));

Tie destinations to menu-driven UI components

You can tie destinations to the navigation drawer and overflow menu by using the id of the destination as the same id for the navigation drawer or overflow menu item in XML. The following snippet shows a details screen destination whose id is details_page_fragment:

<fragment android:id="@+id/details_page_fragment"
     android:label="@string/details"
     android:name="com.example.android.myapp.DetailsFragment" />

Using the same id for both a destination and a menu item automatically associates the destination with the menu item. The following XML shows how to associate the fragment destination with a menu item in a navigation drawer (for example, menu_nav_drawer.xml):

<item
    android:id="@id/details_page_fragment"
    android:icon="@drawable/ic_details"
    android:title="@string/details" />

The following XML shows how to tie a details destination to the overflow menu (e.g. menu_overflow.xml):

<item
    android:id="@id/details_page_fragment"
    android:icon="@drawable/ic_details"
    android:title="@string/details"
    android:menuCategory:"secondary" />

The Navigation Architecture Component includes a NavigationUI class. This class has several static methods you can use connect menu items with navigation destinations. For example, the following code shows how to use the setupWithNavController() method to connect items in the menu drawer to the NavigationView:

Kotlin

val navigationView = findViewById(R.id.nav_view)
navigationView.setupWithNavController(navController)

Java

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
NavigationUI.setupWithNavController(navigationView, navController);

It is necessary to setup your menu-driven navigation components using these NavigationUI methods so that the state of these UI elements stay in sync with changes to the NavController.

Pass data between destinations

You can pass data between destination in two ways: using Bundle objects or in a type-safe way using safeargs Gradle plugin. Use the following steps to pass data between destinations using Bundle objects. If you are using Gradle, consider following the instructions in [Pass data between destinations in a type-safe way]{#Arguments}.

  1. From the Graph Editor, click on the destination where the argument is received. The destination highlights.

  2. Click Add (+) in the Arguments section of the Attributes panel. Empty name and default value fields appear.

  3. Double-click on name and enter a name for the argument.

  4. Press Tab and enter a default value for the argument.

  5. Click on the action preceding this destination. The Argument Default Values should contain your newly added argument.

  6. Click the Text tab to toggle to the XML view. An argument element, with name and defaultValue attributes, has been added to the destination:

    <fragment
       android:id="@+id/confirmationFragment"
       android:name="com.example.cashdog.cashdog.ConfirmationFragment"
       android:label="fragment_confirmation"
       tools:layout="@layout/fragment_confirmation">
       <argument android:name="amount" android:defaultValue=”0” />
    
  7. In your code, create a bundle and pass it to the destination using the navigate() method:

    Kotlin

    var bundle = bundleOf("amount" to amount)
    view.findNavController().navigate(R.id.confirmationAction, bundle)
    

    Java

    Bundle bundle = new Bundle();
    bundle.putString("amount", amount);
    Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
    

    In your receiving destination’s code, use the getArguments() method to retrieve the bundle and use its contents:

    Kotlin

    val tv = view.findViewById(R.id.textViewAmount)
    tv.text = arguments.getString("amount")
    

    Java

    TextView tv = view.findViewById(R.id.textViewAmount);
    tv.setText(getArguments().getString("amount"));
    

Pass data between destinations in a type-safe way

The Navigation Architecture Component has a Gradle plugin, called safeargs, that generates simple object and builder classes for type-safe access to arguments specified for destinations and actions. Safe args is built on top of the Bundle approach, but requires a little extra code in exchange for more type safety. If you are using Gradle, you can use the safe args plugin. To add this plugin, add the 'androidx.navigation.safeargs' plugin to your build.gradle file. For example:

apply plugin: 'com.android.application'
apply plugin: 'androidx.navigation.safeargs'

android {
   //...
}

After you have the Gradle plugin configured, follow the steps below to use type-safe args.

  1. From the Graph Editor, click on the destination where the argument is received. The destination highlights.

  2. Click + in the Arguments section of the Attributes panel. Empty name and default value fields appear.

  3. Double-click on name and enter a name for the argument.

  4. Press Tab and select the type for the arugment from the drop-down list.

  5. Press Tab and enter a default value for the argument.

  6. Click on the action preceding this destination. The Argument Default Values should contain your newly added argument.

  7. Click the Text tab to toggle to the XML view. An argument element, with name and defaultValue attributes, has been added to the destination:

<fragment
    android:id="@+id/confirmationFragment"
    android:name="com.example.buybuddy.buybuddy.ConfirmationFragment"
    android:label="fragment_confirmation"
    tools:layout="@layout/fragment_confirmation">
    <argument android:name="amount" android:defaultValue="1" app:type="integer"/>
</fragment>

When you generate code using the safeargs plugin, simple object and builder classes are created for the action and sending and receiving destinations. These classes are:

  • A class for the destination where the action originates, appended with the word "Directions".

    So, if an origin fragment is titled SpecifyAmountFragment, the generated class is called SpecifyAmountFragmentDirections. This class has a method, named for the action used to pass the argument, to bundle the argument, such confirmationAction().

  • An inner class whose name is based on the action used to pass the argument. If the passing action is called confirmationAction, the class is named ConfirmationAction.

  • A class for the destination where the arguments are passed, appended with the word Args.

    So, if the destination fragment is titled ConfirmationFragment, the generated class is called ConfirmationFragmentArgs. Use this class's fromBundle() method to retrieve the arguments.

The following code shows you how to use these methods to set the argument and pass it to the navigate() method.

Kotlin

override fun onClick(v: View?) {
   val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
   val amount = amountTv.text.toString().toInt()
   val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
   action.amount = amount
   v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
   EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
   int amount = Integer.parseInt(amountTv.getText().toString());
   ConfirmationAction action =
           SpecifyAmountFragmentDirections.confirmationAction()
   action.setAmount(amount)
   Navigation.findNavController(view).navigate(action);
}

In your receiving destination’s code, use the getArguments() method to retrieve the bundle and use its contents:

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val tv: TextView = view.findViewById(R.id.textViewAmount)
    val amount = ConfirmationFragmentArgs.fromBundle(arguments).amount
    tv.text = amount.toString()
}

Java

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    TextView tv = view.findViewById(R.id.textViewAmount);
    int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
    tv.setText(amount + "")
}

Group destinations into a nested navigation graph

A series of destinations can be grouped into a sub-graph within a navigation graph. The sub-graph is called a “nested graph” while the containing graph is called the “root graph.” Nested graphs are useful to organize and reuse sections of your app’s UI, such as a separate login flow.

As with a root graph, a nested graph must have a destination identified as the starting destination. The nested graph encapsulates its destinations; destinations outside of the nested graph, such as those on the root graph, only accesses the nested graph through its starting destination. Figure 6 shows a navigation graph for a simple money transfer app. This graph has two flows: A flow allowing the user to send money and a flow allowing the user to view their balance.

Figure 6. Money transfer navigation graph

To group destinations into a nested graph:

  1. From the Graph Editor, press and hold shift and click on the destinations you want include in the nested graph. Each destination is highlighted.

  2. Open the context menu and select Move to Nested Graph > New Graph. The destinations are enclosed in a nested graph. Figure 7 shows a nested graph in the Graph Editor.

    Figure 4. Nested graph in the Graph Editor
  3. Click on the nested graph to highlight it. The following attributes appear in the Attributes panel:

    • The Type field contains “Nested Graph.”
    • The ID field contains a system-assigned ID for the nested graph. This ID is used to reference the nested graph within your code.
  4. Double-click on the nested graph. The destinations in the nested graph appear.

  5. In the list of Destinations, click Root to return to the root navigation graph.

  6. Click the Text tab to toggle to the XML view. A nested nagivation graph has been added to the graph. This navigation graph has its own open and close navigation elements. This nested graph has an ID of sendMoneyGraph and a startDestination attribute pointing to the first destination (chooseRecipient) in the nested graph:

    <?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"
       app:startDestination="@id/mainFragment">
       <fragment
           android:id="@+id/mainFragment"
           android:name="com.example.cashdog.cashdog.MainFragment"
           android:label="fragment_main"
           tools:layout="@layout/fragment_main" >
           <action
               android:id="@+id/action_mainFragment_to_chooseRecipient"
               app:destination="@id/sendMoneyGraph" />
           <action
               android:id="@+id/action_mainFragment_to_viewBalanceFragment"
               app:destination="@id/viewBalanceFragment" />
       </fragment>
       <fragment
           android:id="@+id/viewBalanceFragment"
           android:name="com.example.cashdog.cashdog.ViewBalanceFragment"
           android:label="fragment_view_balance"
           tools:layout="@layout/fragment_view_balance" />
       <navigation android:id="@+id/sendMoneyGraph" app:startDestination="@id/chooseRecipient">
           <fragment
               android:id="@+id/chooseRecipient"
               android:name="com.example.cashdog.cashdog.ChooseRecipient"
               android:label="fragment_choose_recipient"
               tools:layout="@layout/fragment_choose_recipient">
               <action
                   android:id="@+id/action_chooseRecipient_to_chooseAmountFragment"
                   app:destination="@id/chooseAmountFragment" />
           </fragment>
           <fragment
               android:id="@+id/chooseAmountFragment"
               android:name="com.example.cashdog.cashdog.ChooseAmountFragment"
               android:label="fragment_choose_amount"
               tools:layout="@layout/fragment_choose_amount" />
       </navigation>
    </navigation>
    
  7. In your code, pass the resource ID of the action connecting the root graph to the nested graph:

    Kotlin

    view.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph)
    

    Java

    Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
    

In Android, a deep link is a URI that points to a specific destination in an app. These URIs are useful when you want to send users to a specific destination to perform some task in your, such as a send money flow allowing the user to quickly send money to someone.

To assign a deep link to a destination in your navigation graph:

  1. From the Graph Editor, select the destination for the deep link.

  2. Click + in the Deep Links section of the Attributes panel. An Add Deep Link dialog box appears.

  3. Type a URI into the URI field, such as "www.cashdog.com/sendmoney" which represents the starting destination of a send money nested graph in an app.

    Note the following:

    • URIs without a scheme are assumed as http and https. For example, www.cashdog.com matchs http://www.cashdog.com and https://www.cashdog.com.

    • Placeholders in the form of {placeholder_name} matches 1 or more characters. The String value of the placeholder is available in the arguments Bundle with a key of the same name. For example, http://www.example.com/users/{id} matches http://www.example.com/users/4.

    • The .* wildcard can be used to match 0 or more characters.

  4. (optional) Check Auto Verify to require Google to verify that you are the owner of the URI. For more information, refer to Verify Android App Links.

  5. Click Add. A link icon appears above the selected destination to indicate that destination has a deep link.

  6. Click the Text tab to toggle to the XML view. A nested deep link element has been added to the destination:

<deepLink app:uri="https://cashdog.com/sendmoney"/>

When a user uses the Back button from a deep link destination, they navigate back up the navigation stack just as though they entered your app from the app’s entry point.

Add an intent filter for a deep link

You must make additions to your manifest.xml file to enable deep linking in your app:

  • For Android Studio 3.0 and 3.1, you must manually add an intent-filter element. For more information, refer to Create Deep Links to App Content.

  • For Android Studio 3.2+, you can add a nav-graph element to your activity elements:

<activity name=".MainActivity">
    <nav-graph android:value="@navigation/main_nav" />
</activity>

As part of the manifest merging build step, this element is replaced with the generated elements needed to match all of the deep links in the navigation graph.

Create a transition between destinations

The Navigation Architecture Component provides functionality to easily add transitions, such as fade-in and fade-out, between destinations. To add a transition:

  1. Create your animation resource files. The Navigation Architecture Component supports property and view animations. For further information, refer to Animation resources.

  2. From the Graph Editor, click on an action where the transition should occur.

  3. In the Transitions section of the Attributes panel, click the down arrow next to Enter. A list of transitions in your project appears.

  4. Select the transition to occur when a user enters the destination.

  5. In the Transitions section of the Attributes panel, click the down arrow next to Exit. A list of transitions in your project appears.

  6. Select the transition to occur when a user exits the destination.

  7. Click the Text tab to toggle to the XML text view. The XML for the transition appears in the action element for the action specified in step 2. The action is embedded within the XML for the destination that is active before the transition occurs. In the following example, specifyAmountFragment is the active destination and it contains the action with the transition animations:

    <fragment
        android:id="@+id/specifyAmountFragment"
        android:name="com.example.buybuddy.buybuddy.SpecifyAmountFragment"
        android:label="fragment_specify_amount"
        tools:layout="@layout/fragment_specify_amount">
        <action
            android:id="@+id/confirmationAction"
            app:destination="@id/confirmationFragment"
            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>
    

    In this example we have transitions that take place when moving to a destination (enterAnim and exitAnim and when exiting that destination (popEnterAnim and popExitAnim).

What's next

This document explained the fundamentals to implement navigation. The following additional navigation topics are available: