photo by burst.shopify.com

Android Navigation Components — Part 2

Dario Mungoi
Google Developer Experts
9 min readMay 25, 2018

--

Two weeks ago at the Google I/O, Google released a lot of great tools to help developers speed up their development process and build better apps.

Some of these announcements were the Android Navigation Components. The Navigation Components is a library part of the Android Jetpack set of libraries.

This library was created to help with the implementation of consistent navigation in android apps.

In my previous post, I talked about the pre-work I think you should to create or map out your app structure.

Understanding the app structure is the key to help you better start or migrate your app to use the Navigation components.

If you did not have a chance to read my previous post I would recommend you to do it so here before you continue.

In my previous post, I took as an example an app to find nearby events and allow the user to buy tickets.
At the end of the post, we ended up with the following use-case diagram:

If you take this diagram and rotate it 90 degrees clockwise, you will end up with something that looks like a tree. In this tree, you will notice that:

  1. The most important actions the user need to do are on the higher levels.
  2. The least important actions the user need to do are on the lower levels.
  3. To achieve some of the actions the user needs to follow a set of steps.
  4. There are some flows that depend on nested flows(Login is a nested flow).

Before starting the implementation of the Navigation Components, let’s take a look at the main concepts of the library:

Navigation Graph

The Navigation Graph is a graph that describes a group of Navigation destinations and their connections.

If you take a look at the use case diagram above, it can be represented using a single Navigation Graph or a set of multiple Navigation Graphs depending on the implementation you decide to go with(Single Activity or Multiple Activities application).

Navigation Destinations

A Navigation Destination can be a screen or any View in your app. In the use case diagram above, a Navigation Destination is equal to the different use cases the user can do.

Navigation Action

A Navigation Action is a link that connects 1 destination with another.
An action knows which destinations it is connecting and the kind of information that will flow between them.

Navigation Host

The Navigation Host is a component that knows about all the destinations and actions in the Navigation Graph.
It handles performing the navigation within the different destinations.

Implementing the Navigation Components in your project

In order to start implementing the Navigation Components in your app, you will need to first update the version of Android Studio you are working on to the latest 3.2 Canary build.

This will ensure you have all the latest tools such as the Navigation editor available to support you with implementing navigation. You can find the latest version of Android Studio here.

Add the Navigation Components dependencies

Create a Navigation Graph File

The first step to start using the Navigation Components is to create a Navigation Graph resource file.

As I mentioned earlier, this file is where we define the different destinations in the app and how they are connected.

An app can have more than 1 Navigation Graph and these Navigation Graphs can be nested together.

To create a Navigation Graph file, perform a right click from anywhere inside your app module, select the option New -> Android Resource File.

This will open a template to create a new Android resource file.

Fill the template by giving the name of your file and use the Resource Type drop-down to select the Navigation resource type.

When you are done, press ok and the Navigation Editor for this file will be opened as shown below.

The Navigation editor shown above is divided into 3 different sections. From left to right:

  1. A that section shows the list of all the destinations in the graph and where is the navigation graph hosted.
  2. The middle and big section is where you can see a visual representation of all the destinations in the graph and their connections.
    If you are using the tools namespace in your layouts, the data will be rendered here too and you can have a good view of your app and sample data.
  3. The third and rightmost section is the attributes section. This section content shows up only if you select a destination or an action. This section will show and allow the modification of the attributes of the selected item be it an Action or a Destination

Adding Destinations to the Graph

Start building your Navigation Graph by adding the different destinations in your app. I encourage you to start building your navigation graph from top to bottom.

I find it easier this way because the topmost levels of the use-case diagram will have a small number of destinations.

If we add the topmost destinations in the Event Finder app used as an example of this post, we will have something like this:

As you can see we have 3 destinations, that are the entry points to the app. These destinations are not connected to each other and so there will be no action linking them together.

I will show how to navigate to each one of these destinations later on.

For now, let’s add the fragments to buy tickets and have a flow of interconnected destinations.

To do that, you should first repeat the process of adding new destinations to the Navigation Graph.

To start making the connections, select the destination where you want to start and drag a line connecting the ending destination.

Doing it for the example of this post, we will now have the Navigation Graph look like this:

Hosting the Navigation Graph

As it is, this Navigation Graph does not do anything but to give a good visual representation of what is the user journey within the app.

This is because a Navigation Graph needs a special container called a Navigation Host.

This component takes a Navigation Graph and provide mechanisms to Navigate through the different destinations using a NavController.

The Navigation Host does all the heavy lifting for us such as doing the fragment transactions and all the other things needed to perform navigation .

If you are using the single Activity app type of structure that Google is suggesting now, your app will have one Activity with only one Navigation Host and one Navigation Graph.

If you are doing a migration, it is recommended to separate your flows into different Activities and provide a Navigation Graph and host for each one of the Activities during the transition stage.

It doesn’t matter if you are starting a new app or migrating an existing one, your Activity layout will likely be the same and will only contain the Navigation Host as shown below:

The two things to notice on the snippet above are the custom attributes:

  • app:navGraph : defines which Navigation Graph will be associated with the Navigation Host
  • app:defaultNavHost: A boolean that if set true will allow the Navigation Host to intercept when the system back button is pressed.

Now, If you launch the Activity that has the Navigation Host, it will show on screen the destination set as the starting point in the navigation graph.

pro-tip: If you want to visualize how your fragments look like in isolation, set the Fragment as the start destination in the navigation graph.

To do so, do a right click on top of the destination in the Navigation Editor and select the “Set as start destination”.

Navigating between destinations

Now that the Navigation Graph is hosted inside the NavFragmentHost, let us look at how you move from one destination to another.

In the example shown here the process of navigating to one destination is done after an event happens in the starting destination. For example, the navigation to the EventDetails Fragment happens when an event in the list is clicked.

To Navigate between destinations, the Navigation Host has a component called NavController .

The NavController component is responsible for managing the whole process of Navigation within a Navigation Host.

To perform the Navigation within destinations you need to first get the NavController from the Navigation Host.

Do this by using the static method of the NavHostFragment findNavController like this:

val navController = Navigation.findNavController(fragment)

After getting the NavController, the navigation to the Event Details destination can be done like this:

navController.navigate(R.id.event_details_fragment)

R.id.event_details_fragment is the destination id for the EventDetailsFragment.

To navigate back to the previous destination, the process is similar and you need to first get the NavController and then call the navigateUp function like this:

navController.navigateUp()

You will normally implement this method to handle the press of the Up Button in a destination. When using this method the NavController will figure out what does going up mean for your destination.

Passing data between destinations

Navigating between different destinations is the core thing but most of the time we are not just going to another destination but we need to send some kind of information in the process.

We still do this by using Bundles in 2 different ways:

  • A type-safe way by using the Navigation Components Safe args Gradle plugin.
  • A non type-safe way by constructing the Bundle ourselves as we always did.

I will talk about safe args in the next post alongside some other things I find cool but not necessary for now.

Using the second way, to pass information between destinations, I will first create a companion object in the destination with a function named bundleArgs that will take whatever arguments the destination needs and return a Bundle.

//EventDetailsFragment.kt companion object {
private const val ARG_EVENT = "event"

fun
bundleArgs(event: Event): Bundle {
return Bundle().apply {
this
.putParcelable(ARG_EVENT, event)
}
}
}

This approach helps me keep everything related to bundling the args and extracting them inside the destination Fragment.

To navigate to this destination and pass the bundle of information, you will need to use one of the overloads of the navigate method that takes the action and arguments:

val args = EventDetailsFragment.bundleArgs(event)
navController.navigate(R.id.event_details_fragment, args)

The way used to extract these arguments in the navigation destination remains the same.

Conclusion

After doing the pre-work I recommended in the previous post, implementing or migrating to the use of the Navigation Components is easy and straightforward.

This is because the api’s are clear and the tools to support them in the latest version of Android Studio are great.

By now, you are in a very good shape to start implementing the Navigation Components on your own projects and you can do that using the Single Activity application recommendation from Google if you are starting a new project or start by having multiple Activities with their own Navigation Graph if you are migrating a large app.

I look forward to seeing you in the next post to talk to you what is going on under the hood and some tips and tricks such as the extension functions provided to hook the BottomNav of the app directly with a NavController.

If you have any questions or comments, please feel free to drop them in the comments section below or send me a tweet.

See you next time!

Resources:

Android ADB Podcast — Navigation Episode

--

--

Dario Mungoi
Google Developer Experts

Senior Android Developer at @Shopify, Design Sprint Master, former Android GDE