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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Navigation Component for Android Part 2: Graphs and Deep Links
30 mins
- Getting Started
- Adding Dependencies
- Inflating Layout to Fragment in AndroidX
- Building Graphs With the Navigation Component
- Adding NavHostFragment With Navigation Component Graph
- Setting up Navigation Component in MainActivity
- Connecting Toolbar, Drawer and Navigation Controller
- Navigating From Drawer Menu Items
- Navigating From Floating Action Button
- Showing DialogFragment
- ViewModel Across Navigation Graph
- Getting ViewModel in Activity From Navigation Graph
- Getting ViewModel in Fragment From Navigation Graph
- Creating Your First Love Letter
- Building Notification With Explicit Deep Link
- Passing Letter With Safe Args
- Getting Letter From Safe Args
- Seeing What You’ve Sent
- Seeing What You’ve Received
- Nested Graph
- Creating Implicit Deep Link With URI
- Handling Web URL Deep Link
- Where to Go From Here?
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:
- Connect your
navController
withdrawerLayout
. - Connect your
toolbar
,navController
andappBarConfiguration
together. This makes yournavController
aware of top-level fragments defined inappBarConfiguration
. - 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.
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:
- 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. - Straight forward. This uses the navController to navigate to SentFragment.
- This is a deep link URI to PrivacyPolicyFragment. You’ll learn about this in a few sections.
- 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.
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.
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.
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.
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:
- 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 isR.id.nav_graph
. This ensures you’re using the same ViewModel across entire graph. - Get your LettersViewModel from
viewModelProvider
, which you created in the line above. - 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. - Load profile information from SharedPreferences.
- 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. :]