The Navigation Architecture Component helps you easily implement common, but complex navigation requirements, while also helping you visualize your app's navigation flow. The library provides a number of benefits, including:

What you'll build

In this codelab, you will work on the sample app seen below:

You'll start with all the activities and fragments created for you and use the Navigation Component to connect them all. You will implement the following with the Navigation Component:

Prerequisites

Get the Code

Clone the navigation codelab from GitHub:

$ git clone https://github.com/googlecodelabs/android-navigation

Alternatively you can download the repository as a Zip file:

Download Zip

Get Android Studio 3.2 Canary 14

Make sure you are using Android Studio 3.2 Canary 14 or higher. This is required for the Android Studio navigation tooling.

If you need to download the newest version of Android Studio, you can do so here.

Destinations

The Navigation Component introduces the concept of a destination. A destination is somewhere the user can go. Programatically, and if you follow our recommendations, a destination is usually a fragment. Activities can also be destinations. Navigation supports both fragments and activities out of the box, but you can also make your own custom destination types if needed.

Navigation Graph

The different places that you can navigate to from a destination are represented visually in a navigation graph. This is a new resource type where you define all the possible paths a user could take through the app. Android Studio represents this visually for you using the new Navigation Editor. For example, here's part of the starting navigation graph for your app:

Exploring the Navigation Editor

Open res/navigation/mobile_navigation.xml.

Click Design to go into Design mode:

Here's what you should see (after a bit of rearranging):

The navigation graph shows the available destinations. The lines between the destinations are called actions. You'll learn about actions later.

You can click on a destination to see its properties. You can also change a destination's properties, which you'll do later.

Click on an arrow to see the properties of the action.

Anatomy of a navigation XML file

All of the changes you make in the graphical Navigation Editor changes the underlying XML; similar to how using the Layout Editor modifies the layout XML.

Select the Text tab:

You'll see some XML like this:

<navigation 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"
            app:startDestination="@+id/launcher_home">

    <!-- ...tags for fragments and activities here -->

</navigation>

Notice:

Let's take a look at a fragment destination:

    <fragment
        android:id="@+id/flow_step_one"
        android:name="com.example.android.codelabs.navigation.FlowStepOneFragment"
        tools:layout="@layout/flow_step_one_fragment">

        <argument
            .../>

        <action
            android:id="@+id/next_action"
            app:destination="@+id/flow_step_second"/>
    </fragment>

Notice:

If you take a look at other <fragment> tags, you'll see they contain other tags like <action>, <argument> and <deepLink>, all of which will be covering during this codelab.

Now you get to add your first destination to the graph! Each destination must be added to the navigation graph before you can navigate to it.

In res/navigation/mobile_navigation.xml file, switch to the Design tab.

Click the add destination icon and select "fragment_settings":

Note that you can also edit the XML file directly to add destinations:

mobile_navigation.xml

    <fragment
        android:id="@+id/settings_fragment"
        android:name="com.example.android.codelabs.navigation.SettingsFragment"
        android:label="fragment_settings"
        tools:layout="@layout/fragment_settings"/>

Either way, the result is a new destination, which renders a preview of the fragment's layout in the design view.

Right now you have this awesome navigation graph, but you're not actually using it.

Activities and Navigation

The Navigation component follows the guidance outlined in the Principles of Navigation. The Principles of Navigation encourages you to have activities as entry points for your app, which contains global navigation, such as the bottom nav.

In comparison, fragments will be the actual destination-specific layouts.

To get this all to work, you need to modify your activity layouts to contain a special widget called a NavHostFragment. A NavHostFragment is a widget that is meant to swap in and out different fragment destinations as you navigate through the navigation graph.

A simple layout supporting navigation similar to the picture above looks like this:

<LinearLayout
    .../>
    <android.support.v7.widget.Toolbar
        .../>
    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/mobile_navigation"
        app:defaultNavHost="true"
        />
    <android.support.design.widget.BottomNavigationView
        .../>
</LinearLayout>

Notice:

NavigationUI and navigation-ui-ktx, which we'll cover later, provides helper methods for global app navigation. This library allows the action bar, navigation drawer and bottom navigation bar to easily communicate with and swap out the contents of the NavHostFragment.

NavigationController

Finally, when a user does something like click a button, you actually need to trigger a navigate command. A special class, called the NavController, is what actually triggers all the fragment swaps in the NavHostFragment.

val navController = v.findNavController()
navController.navigate(R.id.settings)

Note that you pass in an ID to navigate. This is the ID defined in the navigation graph XML.

NavigationController is powerful because you'll call actions like navigate() or popBackStack() and it will translate that into the appropriate framework operations based on the type of destination you are navigating to or from. For example, when you navigate() to an activity destination, the NavController calls startActivity() on your behalf.

Navigate to a Destination

It's your turn to navigate using NavController. You'll hook up the Navigate To Destination button to navigate to the flow_step_one destination (which is a destination that is a FlowStepFragment).

Ways to Navigate with NavController

A NavController can be retrieved using any of the following static methods:

For click listeners, you can also use the convenience method Navigation.createNavigateOnClickListener(@IdRes destId: int, bundle: Bundle). This method will build an OnClickListener to navigate to the given destination with a bundle of arguments to be passed to the destination.

Navigate to a Destination with NavController

Assign the first button in MainFragment to a destination:

1. Open MainFragment

2. Hook up the navigate_dest_bt in onViewCreated:

MainFragment

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

The second parameter, currently null, is a Bundle of extras that can be passed to the destination.

3. Run the app and click the Navigate To Destination button. Note that the button navigates to the flow_step_one destination.

Each navigate call has a not very exciting default transition animation associated with it, as seen below:



The default transition animation, as well as other attributes associated with the call, can be overridden by including a set of NavOptions. NavOptions uses a Builder pattern which allows you to only override and set the options you need.

Update the code so that pressing the Navigate To Destination button shows a custom transition animation.

1. Open MainFragment

2. Construct a NavOptions and pass it into the navigate call to navigate_dest_bt

val options = NavOptions.Builder()
    .setEnterAnim(R.anim.slide_in_right)
    .setExitAnim(R.anim.slide_out_left)
    .setPopEnterAnim(R.anim.slide_in_left)
    .setPopExitAnim(R.anim.slide_out_right)
    .build()

view.findViewById<Button>(R.id.navigate_dest_bt)?.setOnClickListener {
    findNavController(it).navigate(R.id.flow_step_one, null, options)
}

3. Remove code added in step 5, if it's still there.

4. Verify that tapping the Navigate To Destination button causes the fragment to slide onto the screen and then pressing back causes it to slide off the screen.

Actions

The navigation system also allows you to navigate via actions. As previously mentioned, the lines shown in the navigation graph are visual representations of actions.

If you use actions for navigation, your navigation graph becomes a single centralized resource that shows all destinations in your app and the paths (actions) between them.

Another reason actions are useful is that they provide a level of abstraction. Instead of navigating to a specific destination, you navigate to an "action". The action can then point to different destinations depending on the context.

One example of this is that we can have an action called "action_next" which will navigate you to different screens depending on what the actual next destination is. This is what's shown above and the corresponding XML is below:

<fragment
    android:id="@+id/launcher_home"
    android:name="com.example.android.codelabs.navigation.MainFragment">
    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_one" />
</fragment>
<fragment
    android:id="@+id/flow_step_one"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">
    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_two"/>
</fragment>

<fragment
    android:id="@+id/flow_step_two"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">
    <action
        android:id="@+id/next_action"
        app:popUpTo="@id/launcher_home"/>
</fragment>

Notice:

Actions are scoped to the destination they are attached to. Therefore an action on a different destination can have different behavior. This allows you to build reusable code that refers to a generic action that is valid on multiple destinations.

Navigate with an Action

Time to hook up the Navigate with Action button so that it lives up to its name!

1. Open the mobile_navigation.xml file in Design mode

2. Drag an arrow from the launcher_home destination to flow_step_one:

3. With the action arrow selected (blue) change the properties of the action so that:

4. Click the Text tab.

5. Note the newly added next_action action under the launcher_home destination:

mobile_navigation.xml

<fragment android:id="@+id/launcher_home"...>
        
        <action android:id="@+id/next_action"
            app:destination="@+id/flow_step_one"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />

6. Open MainFragment.kt.

7. Add a click listener to the navigate_action_bt

MainFragment

        view.findViewById<Button>(R.id.navigate_action_bt)?.setOnClickListener(
                Navigation.createNavigateOnClickListener(R.id.next_action, null)
        )

8. Verify that tapping the Navigate To Action now navigates to the next screen.

NavigationUI and navigation-ui-ktx

The Navigation Components include a NavigationUI class and the navigation-ui-ktx kotlin extensions. NavigationUI has static methods that associate menu items with navigation destinations, and navigation-ui-ktx are extension functions that do the same. If NavigationUI finds a menu item with the same ID as a destination on the current graph, it configures the menu item to navigate to that destination.

Using NavigationUI

Let's use NavigationUI to configure four menus: the bottom navigation, the side navigation (if you're using a large enough screen), the action bar and the overflow menu.

In a previous step you added a "Settings" fragment that was never included in the app. Let's fix that!

1. Update your navigation drawer and overflow menus to include settings_fragment:

menu_nav_drawer.xml

<item
        android:id="@+id/settings_fragment"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings" />

menu_overflow.xml

<item
        android:id="@+id/settings_fragment"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        android:menuCategory="secondary" />

2. Verify that the settings fragment appears in the overflow menu on phones and the navigation drawer on tablets or in split screen on a phone.

Overflow menu on a phone

Drawer on a phone in

split-screen mode

Safe Args

The navigation component also has a Gradle plugin, called safe args, that generates simple object and builder classes for type-safe access to arguments specified for destinations and actions.

Safe args allows you to get rid of code like this when passing values between destinations:

val username = arguments?.getString("usernameKey")

And, instead, replace it with code that has generated setters and getters.

val username = args.username

Pass a value using safe args

1. Open the app/build.gradle file and review the applied plugin:

app/build.gradle

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

android { 
   //...
}

2. Open and review how arguments are defined in the flow_step_one destination in mobile_navigation.xml

mobile_navigation.xml

<fragment
    android:id="@+id/flow_step_one"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment"
    tools:layout="@layout/flow_step_one_fragment">
 <argument
     android:name="step"
     app:type="integer"
     android:defaultValue="1"/>
</fragment>

3. The <argument> tag generates a class called FlowStepFragmentArgs. Since the XML includes an argument called step, specified by android:name="step", the generated class FlowStepFragmentArgs will include a variable step with getters and setters.

4. Comment out this line of code in FlowStepFragment; it is the "old" way to get arguments that goes not use the step args:

FlowStepFragment.kt

// Comment out this line
// val step = arguments?.getInt("step")

5. Update FlowStepFragment to use the code generated class FlowStepFragmentArgs. This will get the FlowStepFragment arguments in a type-safe manner:

FlowStepFragment.kt

val step = arguments?.let {
    val safeArgs = FlowStepFragmentArgs.fromBundle(it)
    safeArgs.step
}

Deep Links and Navigation

Navigation components also include deep link support. Deep links are a way to jump into the middle of your app's navigation, whether that's from an actual url link or a pending intent from a notification.

One benefit of using the navigation library to handle deep links is that it ensures users start on the appropriate destination with the appropriate back stack from other entry points such as app widgets, notifications, or web links (covered in the next step).

Navigation provides a NavDeepLinkBuilder class to construct a PendingIntent that will take the user to a specific destination.

Add a Deep Link

We'll use the NavDeepLinkBuilder to hook up an app widget to a destination.

1. Open DeepLinkAppWidgetProvider

2. Add a PendingIntent constructed with NavDeepLinkBuilder:

DeepLinkAppWidgetProvider

Bundle args = new Bundle();
args.putString("myarg", "From Widget");
PendingIntent pendingIntent = new NavDeepLinkBuilder(context)
    .setGraph(R.navigation.mobile_navigation)
    .setDestination(R.id.android)
    .setArguments(args)
    .createPendingIntent();

remoteViews.setOnClickPendingIntent(R.id.deep_link, pendingIntent);

Notice:

3. Add the Deep Link widget to your home screen. Tap and hold on the home screen to see option to add widget.

Adding a widget

The Deep Link widget

4. Tap the widget, and verify that the Android destination opens with the correct argument (it should say "From Widget" at the top).

4. Verify that hitting the back button takes you to the launcher_home destination.

As a convenience, you can also call NavController's createDeepLink() method to use the Context and current navigation graph from the NavController.

<deepLink>

One of the most common uses of a deep link is to allow a web link to open an activity in your app. Traditionally you would use an intent-filter and associate a url with the activity you want to open.

The navigation library makes this extremely simple - and allows you to put the information mapping urls to destinations solely in your navigation graph.

<deepLink> is an element you can add to destination in your graph. Each <deepLink> element has a single required attribute: app:uri.

In addition to a direct Uri match, the following features are supported:

Add a Uri based Deep Link using <deepLink>

You'll add a deep link to www.iana.org/domains/example - the one link on www.example.com.

1. Open the mobile_navigation.xml file.

2. Add a <deepLink> element to the android destination.

mobile_navigation.xml

<fragment
    android:id="@+id/android"
    android:name="com.example.android.codelabs.navigation.DeepLinkFragment"
    android:label="@string/android"
    tools:layout="@layout/android_fragment">
    <argument
        android:name="myarg"
        android:defaultValue="Android!"/>
    <deepLink app:uri="www.example.com/{myarg}"/>
</fragment>

3. Open AndroidManifest.xml

4. Add the <nav-graph>:

AndroidManifest.xml

<activity
   android:name=".MainActivity">
   <intent-filter>
       <action android:name="android.intent.action.MAIN"/>
       <category android:name="android.intent.category.DEFAULT"/>
       <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
   <nav-graph android:value="@navigation/mobile_navigation"/>
  </activity>

5. With the browser of your choice, go to www.example.com/<any string>/. After selecting the code lab app, confirm that you are on the correct destination.

Opening www.example.com/hello/

triggers the intent chooser

The Deeplink fragment shows

the argument passed through

the deep link

There's one more part of the codelab app for your to experiment with, and that's the shopping cart button.

We'll use this as a recap of the various skills you've learned during this codelab. For this step, you will:

  1. Create a new fragment class
  2. Add the fragment as a destination to your navigation graph
  3. Have the shopping cart icon open up your new fragment class, using NavigationUI

Note that this step does not include comments - try this out on your own!

You're familiar with the basic concepts behind the navigation component! In this codelab you learned about:


Now you can start either using navigation in your own app, or continue explorations with this app.

There's a lot more to explore, including:

For more about the Navigation Component check out the documentation. If you're interested in learning about other Architecture Components, try out the following codelabs: