DEV Community

Cover image for Understanding about Navigation Architecture Component
Douglas Fornaro
Douglas Fornaro

Posted on

Understanding about Navigation Architecture Component

Navigation in an Android app may present challenges and problems that require solutions, particularly when the app grows in complexity. We must carefully consider the Fragment transactions, handle Deep Links, pass parameters in a safe way, properly handle up/back navigation and back stack (especially for Fragments), as well as plan and execute app navigation tests. Google has been creating the Navigation Architecture Component based on consideration of these challenges as well as listening to the Android community.

The Navigation Architecture Component is part of Android JetPack. The Android JetPack is a package of libraries, tools, and guidance that help developers eliminate boilerplate code, simplify complex tasks, and use best practices to write high-quality apps. This way we can care about our business code.

The Navigation Architecture Component simplifies the implementation of navigation in an Android app. It also ensures a consistent and predictable user experience by adhering to an established set of principles.

Advantages

The concept behind the Navigation Architecture Component is to have the developer use a single Activity (Fragments only), to achieve various benefits. These include reduced development time, easy animations between Fragments, and increased app performance.

Navigation Architecture Component has been launched to solve a lot of Android app navigation problems. We can see them described below.

Implementation

Like Android JetPack, Android Navigation Component accelerates app development and is easy to be implemented. It entails not much more than a few concepts and a config file.

Fragment transactions

You may have tried Fragment transactions before. If so, you know that a lot of code is needed to achieve the result. Not anymore! We no longer need to care about adding, replacing and removing Fragments, because the framework does this for us.

Passing arguments in a safe way

Now there is a way to ensure that the data being passed from one Fragment will be received by another Fragment without cast.

Handling up/back button and back stack

Sometimes this gives us a headache. Now it’s only necessary to specify the app navigation within the config file.

Animations

Animations are also specified in a simple way, within the config file, making the code cleaner. Just beautiful.

Deep links

In Android, a deep link is a link that takes the user directly to a specific destination within an app. The framework lets you easily create deep links with the use of single line of code within the config file, without having to handle it manually.

Handling drawer navigation and bottom navigation

The framework already has support for these navigation components, keeping it concise and complete.

Tests

We all know the importance of testing things before launching an app. All of the things that are offered by the framework are already well tested. This way, the important test becomes the interactions between the Fragments.

How does it work?

Now that we understand more about the Navigation Architecture Component, let’s see how it works and how to implement it into our app.

The Navigation Architecture Component consists of three key parts:

  • Navigation graph: A XML file containing all navigation content such as destinations, actions and arguments. This is the navigation configuration file.
  • NavHost: A fragment container that displays all the destinations throughout the app. The app’s navigation will happen in this fragment container.
  • NavController: The object that handles app navigation.

We will discuss these in more detail below.

Set up the environment

To start using the Navigation Architecture Component make sure you are using Android Studio 3.3 or higher. The editor used to create the Navigation was introduced in this version. You can download the latest version of Android Studio at this link.

Add the following dependencies to your app’s build.gradle file:

dependencies {
    def nav_version = "2.3.2"

    // Navigation
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
Enter fullscreen mode Exit fullscreen mode

You can take a look at the most recently released version at this link.

Editor

The Navigation editor is used to create the navigation graph. The entire navigation configuration is done within this file.

This navigation graph shows previews of the six different destinations that are connected via five actions.<br>

  1. Destinations are the screens (Fragments) of the app.
  2. Actions are the possible connections between the destinations.

Navigation graph

All the navigation’s configuration is done within this XML file. This file is supposed to be inside res/navigation folder. Below is an example of the navigation graph file:

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    app:startDestination="@id/homeFragment">
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_detail_fragment"
            app:destination="@id/detailFragment" />
    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:name="com.example.DetailFragment"
        android:label="fragment_detail"
        tools:layout="@layout/fragment_detail" >
        <argument
            android:name="id"
            app:argType="long" />
    <fragment>
</navigation>
Enter fullscreen mode Exit fullscreen mode

The <navigation> element is the root element of the navigation graph. It contains <fragment> that are the destinations. A fragment may contains <action> and <argument>.

Arguments may support default values and nullable values as shown on the next image.

Default and nullable values supported by the arguments type.<br>

NavHost

The app navigation will happen in the fragment container. Below is an example how to add it in the Activity:

<androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/app_navigation" />
Enter fullscreen mode Exit fullscreen mode

android:name="androidx.navigation.fragment.NavHostFragment" identifies that the app navigation will happen here.

app:defaultNavHost="true" ensures that the NavHostFragment intercepts the system back button.

app:navGraph="@navigation/nav_graph" is the navigation graph XML file.

NavController

Now we just need to navigate from one destination to another. The following example shows how to do this on a button click:

button.setOnClickListener {
    findNavController().navigate(R.id.action_detail_fragment)
}
Enter fullscreen mode Exit fullscreen mode

findNavController() is an extension function that may be used for a Fragment, View or Activity.

Pass and receive data

To pass data in a safe way it’s needed to add the Safe Args plugin. To add the Safe Args to your project, include the following classpath in your top level build.gradle file:

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.3.2"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}
Enter fullscreen mode Exit fullscreen mode

And apply the plugin:

apply plugin: "androidx.navigation.safeargs.kotlin"
Enter fullscreen mode Exit fullscreen mode

The data passed from the fragments must be declared as <argument> on the navigation graph file. This is an example how to pass and receive arguments between destinations:

// HomeFragment.kt
button.setOnClickListener {
    val direction = HomeFragmentDirections.detailFragment(id)
    findNavController().navigate(direction)
}
Enter fullscreen mode Exit fullscreen mode
// DetailFragment.kt
private val args: DetailFragmentArgs by navArgs()
Enter fullscreen mode Exit fullscreen mode

HomeFragmentDirections and DetailFragmentArgs are generated classes by the framework where is possible to be used to pass and receive data.

Up/back button

Android maintains a back stack that contains the destinations visited by the user. The first destination of your app is placed on the stack when the user opens the app. Each call to the navigate() method puts another destination on top of the stack. Pressing Up or Back button calls the NavController.navigateUp() and NavController.popBackStack() methods, respectively, to remove (pop) the top destination off of the stack.

Pop additional destinations

After a login flow, for example, you should pop all of the login-related destinations off of the back stack so that the back button doesn’t take the users back into the login flow again.

This is possible by adding a app:popUpTo attribute to the associated <action> element. This way, the framework pops some destinations off of the back stack as part of the call to navigate(). The attribute value is the ID of the most recent destination that should remain on the stack.

You can also include app:popUpToInclusive="true" to indicate that the destination specified in app:popUpTo should also be removed from the back stack (which in most cases will be the same ID as the destination attribute).

Deep links

To start using deep links you may need to add a <deepLink> element inside the <fragment> on the navigation graph file like this:

<fragment
 android:id="@+id/secondFragment"
 android:name="com.example.SecondFragment"
 android:label="@string/second"
 tools:layout="@layout/fragment_second">
     <deepLink
        android:id="@+id/secondDeepLink"
        app:uri="navsample.com/second" />
</fragment>
Enter fullscreen mode Exit fullscreen mode

It means that when this URL is accessed from a browser or another app, it will open the SecondFragment and also, create the correct back stack. In that example the url is http://navsample.com/second.

Bottom Navigation and Drawer Layout

The framework has a NavigationUI component that connects navigation with certain material design components such as BottomNavigationView and DrawerLayout. Let’s see how it works with the BottomNavigationView.

This is an example of menu_bottom_navigation. In this case it’ll show three menu options on BottomNavigationView.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/homeFragment"
        android:icon="@drawable/ic_home"
        android:title="@string/home" />

    <item
        android:id="@+id/secondFragment"
        android:icon="@drawable/ic_second"
        android:title="@string/second" />

    <item
        android:id="@+id/thirdFragment"
        android:icon="@drawable/ic_third"
        android:title="@string/third" />
</menu>
Enter fullscreen mode Exit fullscreen mode

On the nav_graph file, we will need to use the same ID as the menu_bottom_navigation file as shown below:

<?xml version="1.0" encoding="utf-8"?>
<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/app_navigation"
 app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
android:name="br.com.fornaro.sample.navigation.HomeFragment"
        android:label="@string/home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/secondFragment"
android:name="br.com.fornaro.sample.navigation.SecondFragment"
        android:label="@string/second"
        tools:layout="@layout/fragment_second"  />

    <fragment
        android:id="@+id/thirdFragment"
android:name="br.com.fornaro.sample.navigation.ThirdFragment"
        android:label="@string/third"
        tools:layout="@layout/fragment_third" />
</navigation>
Enter fullscreen mode Exit fullscreen mode

Now we need to setup the NavController in the UI component like this:

// MainActivity.kt
val navController = findNavController(R.id.navController)
bottomNavigationView.setupWithNavController(navController)
Enter fullscreen mode Exit fullscreen mode

Once the IDs of the <fragments> are the same as <item> on menu_bottom_navigation, the framework will link them automatically and navigate to the specified Fragment.

The same concept applies to DrawerLayout by linking the IDs.

Tests

In the next section you’ll find a sample code using the Navigation Architecture Component with some tests implemented. There, it is possible to test the BottomNavigationView and the DeepLink.

Code

A sample code has been created to show how it might be implemented. In this example a BottomNavigationView was created to show how the navigation works in that component, how to use Safe Args to pass data in a safe way, how to use DeepLink with all the navigation with an animation. You can take a look at the code below.

Navigation Component - Github Sample

Conclusion

The Navigation Architecture Component has arriving to help developers implement an app’s navigation in a simple, easy, and consistent way while avoiding boilerplate code. Everything we need to do to implement how navigation will work on our app will be done within the navigation graph XML file, letting the framework do all the hard work for us.

Top comments (0)