Navigation Component for Android Part 2: Graphs and Deep Links

In this tutorial you’ll use the Jetpack Navigation component to write an Android app utilizing graphs and deep links to navigate through different screens. By Meng Taing.

Leave a rating/review
Download materials
Save for later
Share

Navigation between Fragments using the FragmentManager is a nightmare most Android developers try to avoid. Usually, the Fragment lifecycle doesn’t play well with the Activity lifecycle.

Managing the Fragment back stack is another headache, especially when you add deep links to the mix. At Google I/O ’18, Jetpack Navigation component came to the rescue. The Navigation component takes the responsibility of the FragmentManager so developers can focus on building awesome features in their apps.

Some of the features in the Navigation component Version 1.0.0 were already covered in The Navigation Architecture Component Tutorial: Getting Started. In this tutorial, you’ll learn about these cool Jetpack Navigation component 2.1.0 features:

  • DialogFragment in navigation graph.
  • ViewModel per navigation graph.
  • Safe Args.
  • Nested graph.
  • Explicit and implicit deep links.

There’s also an awesome Jetpack Navigation Controller screen cast which quickly walks you through how to build a navigation graph. Be sure to check them out.

Note: This tutorial also includes Room and Data Binding. Even if you’re not familiar with those topics, you can still follow this tutorial. If you’re totally new to Jetpack, review the Android Jetpack Architecture Components: Getting Started tutorial first.

To embark on a romantic navigation journey, you’ll build a Love Letter app. As you might guess from the name, this app allows lets you send and receive love letters.

Final App Presentation

Getting Started

Some of the Navigation component features in this tutorial require Android Studio 3.4 or higher. Make sure to install the latest stable release.

First, download the materials for this tutorial using the Download materials button at the top or bottom of this tutorial. Open the project with Android Studio. You’ll be prompted with a dialog to choose Gradle wrapper:

Import Project From Gradle

Click OK. You’ll find the project structure looks like this:

Project Structure

Take a look at the fragments package:

  • InboxFragment: List of letters you received.
  • SentFragment: List of letters you sent.
  • CreateLetterFragment: Where you compose and send your letter.
  • PresentationFragment: Where you view the content of the letter.
  • EditProfileFragment: Dialog for inputting your name and email address.

There are two additional Fragments inside the agreement package:

  • PrivacyPolicyFragment: Long, boring text for the privacy policy nobody cares about.
  • TermsOfServiceFragment: More long, boring text for the terms of service nobody bothers to read.

Here are other important classes:

  • LetterAdapter: RecyclerView adapter for list of letters.
  • LetterPagerAdapter: ViewPager adapter for letter presentation.
  • Event: LiveData value wrapper that makes observer read its value only once.
  • KotlinExtension: Contains a few short-handed functions to make the code cleaner.
  • LetterRepository: Repository class for Letter CRUD operations.
  • LetterViewModel: The only ViewModel in this app for MainActivity and other Fragments.
  • MainActivity: The single Activity where navigation is setup.

To make sure everything is compatible and working, run the app. You should see an empty screen like this:

Empty Screen

The floating action button with the envelope icon is like the Compose button on Gmail app. It’ll bring you to the CreateLetterFragment.

Slide from the left edge to view the navigation drawer. All the drawer menu items are setup for you:

Navigation Drawer

Adding Dependencies

Open build.gradle under app module. Add the following code into dependencies braces below // TUTORIAL DEPENDENCIES:

def nav_version = "2.1.0"

implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

Run Gradle sync through Android Studio. If there aren’t any errors, you’re good to go.

Inflating Layout to Fragment in AndroidX

Because you added androidx.navigation:navigation-fragment-ktx, you’re now able to set the layout for the Fragment directly in its constructor. Open the following five files one at a time, and add the layout id to each constructor:

//InboxFragment.kt
class InboxFragment : Fragment(R.layout.fragment_inbox)

//SentFragment.kt
class SentFragment : Fragment(R.layout.fragment_sent)

//PresentationFragment.kt
class PresentationFragment : Fragment(R.layout.fragment_presentation)

//PrivacyPolicyFragment.kt
class PrivacyPolicyFragment : Fragment(R.layout.fragment_privacy_policy)

//TermsOfServiceFragment.kt
class TermsOfServiceFragment : Fragment(R.layout.fragment_terms_of_service)

The benefit of this approach and setting the layout in the Fragment constructor is that you don’t have to override onCreateView() and write boilerplate code to inflate the layout and return it.

Building Graphs With the Navigation Component

Right click the res folder in Project Explorer, then create a new Directory named navigation. Right click the navigation folder and create a new Navigation resource file named nav_graph. You’ll be brought to the Navigation Editor:

Navigation Editor

Add the following five fragments and the agreement graph to the editor. Connect them as in the screenshot below:

  • InboxFragment, set as home fragment.
  • SentFragment.
  • CreateLetterFragment.
  • PresentationFragment.
  • EditProfileFragment, shown as a dialog.

Navigation Graph

You only need to connect the action from InboxFragment and SentFragment to PresentationFragment. You’ll find out why in the Safe Args section below.

Switch the view mode from Design to Text. Fill in navigation id, action id and fragment label. Then, fill in layout as the code below, or simply copy and paste:

<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"
  android:id="@+id/nav_graph"
  app:startDestination="@id/inboxFragment">

  <fragment
    android:id="@+id/inboxFragment"
    android:name="com.raywenderlich.android.loveletter.fragment.InboxFragment"
    android:label="@string/menu_inbox"
    tools:layout="@layout/fragment_inbox">
    <action
      android:id="@+id/presentLetter"
      app:destination="@id/presentationFragment"/>
  </fragment>
  <fragment
    android:id="@+id/sentFragment"
    android:name="com.raywenderlich.android.loveletter.fragment.SentFragment"
    android:label="@string/menu_sent"
    tools:layout="@layout/fragment_sent">
    <action
      android:id="@+id/presentLetter"
      app:destination="@id/presentationFragment"/>
  </fragment>
  <fragment
    android:id="@+id/createLetterFragment"
    android:name="com.raywenderlich.android.loveletter.fragment.CreateLetterFragment"
    android:label="@string/create_letter"
    tools:layout="@layout/fragment_create_letter"/>
  <dialog
    android:id="@+id/editProfileFragment"
    android:name="com.raywenderlich.android.loveletter.fragment.EditProfileFragment"
    android:label="@string/edit_profile"
    tools:layout="@layout/fragment_edit_profile"/>
  <fragment
    android:id="@+id/presentationFragment"
    android:name="com.raywenderlich.android.loveletter.fragment.PresentationFragment"
    android:label="@string/presentation"
    tools:layout="@layout/fragment_presentation"/>
</navigation>
Note: The <dialog> tag is used instead of <fragment> for EditProfileFragment, otherwise the fragment would take up the full space on the screen.

If the fragments in Navigation Editor don’t render any layout, make sure that the tools:layout attributes are added. You could also simply rebuild the project.

You’ve finished building the navigation graphs. Now you’re ready to use nav_graph in content_main.xml.

Adding NavHostFragment With Navigation Component Graph

Open content_main.xml and add the following code inside the ConstraintLayout:

<fragment
  android:id="@+id/nav_host_fragment"
  android:name="androidx.navigation.fragment.NavHostFragment"
  android:layout_width="0dp"
  android:layout_height="0dp"
  app:defaultNavHost="true"
  app:layout_constraintBottom_toBottomOf="parent"
  app:layout_constraintLeft_toLeftOf="parent"
  app:layout_constraintRight_toRightOf="parent"
  app:layout_constraintTop_toTopOf="parent"
  app:navGraph="@navigation/nav_graph"/>

What you should notice here:

  • android:name="androidx.navigation.fragment.NavHostFragment": You’re using NavHostFragment from Androidx to host your navigation graph.
  • app:navGraph="@navigation/nav_graph": You’re supplying your navigation graph to the NavHostFragment.

Now it’s time to let your MainActivity know about the NavHostFragment and navigation graph.

Setting up Navigation Component in MainActivity

Open MainActivity.kt and examine the code. You’ll see some code for navigation drawer and data binding is already there. Data binding binds the profile name and email to the navigation drawer header.

There are a bunch of TODOs to help you know where to insert the code snippets.

Right below the MainActivity class declaration, add the following two private fields:

private val navController by lazy { findNavController(R.id.nav_host_fragment) } //1
private val appBarConfiguration by lazy {
  AppBarConfiguration(
    setOf(
      R.id.sentFragment,
      R.id.inboxFragment
    ), drawerLayout
  )
} //2

Here’s what you did:

  1. You’ll use navController to navigate from one fragment to another. Import findNavController from androidx.navigation.findNavController.
  2. appBarConfiguration defines which fragments are the top level fragments so the drawerLayout and hamburger icon can work properly. You’ll understand why when you run the app.