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
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Connecting Toolbar, Drawer and Navigation Controller

Here’s the most important code in this tutorial. While you’re still in MainActivity, add the following code inside setupNavigation:

NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)  //1

NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)  //2

navController.addOnDestinationChangedListener { _, destination, _ ->  //3
  if (destination.id in arrayOf(
      R.id.createLetterFragment,
      R.id.presentationFragment,
      R.id.editProfileFragment
    )
  ) {
    fab.hide()
  } else {
    fab.show()
  }

  if (destination.id == R.id.presentationFragment) {
    toolbar.visibility = View.GONE
  } else {
    toolbar.visibility = View.VISIBLE
  }
}

Here’s what this code does:

  1. Connect your navController with drawerLayout.
  2. Connect your toolbar, navController and appBarConfiguration together. This makes your navController aware of top-level fragments defined in appBarConfiguration.
  3. Customize your floating action button and toolbar based on the destination fragment. Here you want to hide your floating action button when you’re on CreateLetterFragment, PresentationFragment and EditProfileFragment. On PresentationFragment, hide the toolbar so that you can read your love letters without distraction.

Now, run the app. You should now see the hamburger icon. It wasn’t there when you first ran the app. That’s because of the second line of the code above.

The title at tool bar is shows the label of InboxFragment, which is the current Fragment.

Inbox Screen

Click the hamburger icon or slide from the left side of the screen. You’ll see the drawer is open. But if you click on any menu item, you still can’t navigate to any fragment yet.

Navigating From Drawer Menu Items

Inside onNavigationItemSelected, you’ll find the when block for each menu item. Replace the
content inside the when block with the following code:

R.id.nav_inbox -> {
  navController.popBackStack(R.id.inboxFragment, false)  //1
}

R.id.nav_sent -> {
  navController.navigate(R.id.sentFragment)  //2
}

R.id.nav_privacy_policy -> {
  navController.navigate(Uri.parse("loveletter://agreement/privacy-policy"))  //3
}

R.id.nav_terms_of_service -> {
  navController.navigate(Uri.parse("loveletter://agreement/terms-of-service"))  //4
}

Here’s what you added:

  1. Since InboxFragment is the home fragment, you use popBackStack to remove other fragments from the navigation stack. The second parameter indicates whether InboxFragment should also pop out of the stack.
  2. Straight forward. This uses the navController to navigate to SentFragment.
  3. This is a deep link URI to PrivacyPolicyFragment. You’ll learn about this in a few sections.
  4. This is also a deep link URI, but for TermsOfServiceFragment.

Now, run the app. You should be able to navigate between InboxFragment and SentFragment.

That’s it. You no longer have to get SupportFragmentManger or commit a fragment transaction in order to swap fragments.

Open Sent

Note: Currently, if you click on Privacy Policy or Terms of Service the app will crash. This is because no app is handling the URI you defined in the code snippet above.

Navigating From Floating Action Button

In MainActivity.kt, locate setupViews() and add the following code inside
fab.setOnClickListener:

navController.navigate(R.id.createLetterFragment)

Similar to how you navigate to SentFragment, you use navController to navigate to
CreateLetterFragment by its id.

Run the app and click the floating action button. You’ll see the app navigates to CreateLetterFragment.

The loading progress bar keeps on spinning. It’s an annoyance, but you can ignore it for now.

Create Letter Screen

Next, you’ll wire the edit profile dialog.

Showing DialogFragment

If you’ve tried showing DialogFragment in Navigation component 1.0.0 or followed The Navigation Architecture Component Tutorial: Getting Started tutorial, you have extended Navigator class and implemented your own show dialog logic.

This isn’t required with Navigation component 2.1.0. Take a look at the code in nav_graph.xml. You’ll see the tag dialog is automatically used for fragments extending DialogFragment.

To show the dialog fragment, you have to add the following line inside the click listener of headerBinding.ivEdit in MainActivity:

navController.navigate(R.id.editProfileFragment)

Once again, we’re using the navController to navigate to another fragment. In this case the Edit Profile Fragment. Run the app and click the Edit Profile button.

Yay! The dialog pops up with only a few lines of code.

Open Edit Profile Dialog

Next, you’ll set the ViewModel so you can save and show your profile information.

ViewModel Across Navigation Graph

ViewModel is a design pattern introduced to separate the business logic and life cycle events from your Activity and Fragment. Since your fragments live under an Activity, it’s common to use one shared ViewModel for the Activity and its associated fragments.

In the Navigation component, you can initialize a ViewModel with a navigation graph scope. This means all the fragments in the same navigation graph and their parent Activity share the same ViewModel.

Note: If you’ve never used ViewModel or data binding, check out the MVVM and DataBinding: Android Design Patterns and other related tutorials.

Getting ViewModel in Activity From Navigation Graph

While you’re still in MainActivity.kt, add the following code to setupViewModel():

try {
  val viewModelProvider = ViewModelProvider(
    navController.getViewModelStoreOwner(R.id.nav_graph),
    ViewModelProvider.AndroidViewModelFactory(application)
  )  //1
  lettersViewModel = viewModelProvider.get(LettersViewModel::class.java)  //2
  headerBinding.viewModel = lettersViewModel  //3
  lettersViewModel?.loadProfile()  //4
} catch (e: IllegalArgumentException) {  //5
  e.printStackTrace()
}

Here’s a breakdown of the above code:

  1. Instead of the usual way of getting ViewModelProviders from Activity or Fragment like ViewModelProviders.of(this), here you get ViewModelProvider from navigation graph id, which is R.id.nav_graph. This ensures you’re using the same ViewModel across entire graph.
  2. Get your LettersViewModel from viewModelProvider, which you created in the line above.
  3. This is the data binding logic for the header in the navigation drawer. If you’re curious, check nav_header_main.xml. The header binding requires the viewModel instance of LettersViewModel class.
  4. Load profile information from SharedPreferences.
  5. An exception happens when the app is launched via a deep link. Navigation component can’t handle the initialization of a ViewModel scoped to Navigation graph. The temporary solution is to catch the exception.

The details of ViewModel implementation aren’t discussed here since it’s not part of the Navigation component. Spend some times reading the code in LettersViewModel to understand how the ViewModel, Data Binding and Room operate behind the scenes.

Now your drawer header is ready to show profile information from shared preferences. But nothing is saved to shared preferences yet! You need to do something in the EditProfileFragment.

Getting ViewModel in Fragment From Navigation Graph

Open EditProfileFragment.kt and replace the declaration of lettersViewModel with the following line:

private val lettersViewModel: LettersViewModel by navGraphViewModels(R.id.nav_graph)

This is much cleaner than how you got your ViewModel in MainActivity earlier. Thanks to navGraphViewModels(...), the extended inline function for Fragment, you can get the instance of LettersViewModel with one line of code.

Launch the app and edit your profile information in the dialog. Click the Save button and check your profile in the drawer again.

Voila! Your name and email are there! If you open the EditProfileFragment dialog again, your name and email are filled by default.

That’s the magic of data binding. :]

Fill In Edit Profile Dialog