Android Navigation Component

ashita asati
8 min readMay 10, 2020

--

In this article, we’ll see what exactly is Navigation component and how to implement it.

What’s Navigation Component?

We all know that navigating between screens is one of the fundamental things in an Android application. Usually, we either achieve it through Intents or via Event Bus but what about the complex scenarios like navigation drawer or bottom navigation where you need to keep the selected tab highlighted in sync with the presented screen not to mention back-stack management? Isn’t it a bit complex to achieve this way?

Navigation component is the one-stop solution to all the problems for any type of navigation in an android app. The navigation component helps you to manage navigations, fragment transactions, back-stack management, animations, most importantly deep linking and many more. The JetPack Navigation component is a suite of libraries, tooling, and guidance that provides a robust navigation framework for in-app navigation.

The navigation component provides a new type of navigation in android development, where we have a navigation graph to see all the screens and the navigation between them.

Let’s talk about the key parts of the Navigation component. Navigation component consists of three key types

  • Navigation graph: An XML resource that contains all navigation-related information in one centralized location. This includes all of the individual content areas within your app, called destinations, as well as the possible paths that a user can take through your app.
  • NavHost: An empty container that displays destinations from your navigation graph.
  • NavController: An object that manages app navigation within a NavHost. The NavController orchestrates the swapping of destination content in the NavHost as users move throughout your app.

I’ll explain two more terminologies here as we are going to use them very often-

  • Destinations: Destinations are nothing but all content areas in your app like activities, fragments, dialogs, and so on.
  • Actions: Action is used to create a navigation that has an attribute called destination, where we can mention the end screen id.

When we navigate through the app using the NavController, the NavController shows the appropriate destination in the NavHost.

The Navigation component provides several other benefits, including the following:

  • Simplified setup for common navigation patterns
  • Handling fragment transactions.
  • Type safe when passing information while navigating
  • Handles transition animations
  • Centralizes and visualizes navigation
  • Handling Up and Back actions correctly by default.
  • Providing standardized resources for animations and transitions.
  • Implementing and handling deep linking.

Okay, so we are now familiar with the Navigation component and benefits. Now it’s time to get our hands dirty with implementing a basic navigation flow.

So what are we going to build?

We will build a basic application with three screens(Main Activity with three fragments with two buttons each) and establish the navigation between them.

Let’s get started…

Prerequisites:

  • Basic Kotlin knowledge
  • Android Studio 3.2 or higher
  • Emulator or device running API 14+

After creating the basic Android project, add the below dependency in the top-level build.gradle file.

def nav_version = "2.1.0"dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}

Now, add the below dependencies in your app-level build.gradle, and once done click on sync now!

apply plugin: "androidx.navigation.safeargs.kotlin"android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
def nav_version = "2.2.2"
dependencies {
implementation "androidx.navigation:navigation-fragment ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

Safe Arguments : Safe Args is a Gradle plugin used to pass data between destinations. The main advantage of using safe args is that some of the checks that happen at runtime will now be done at compile time.

You’re good to go now!!!

Firstly, we will create a Navigation Graph for our application.

The question arises here is how to create a navigation Graph???

As we learned earlier that a navigation graph is nothing but a resource file with all navigation related information, so we create a resource file under res directory with resource type as navigation.

Steps to creating a navigation graph:

  1. In the Project window, right-click on the res directory and select New > Android Resource File under navigation directory. If you haven’t created then create one.
  2. In the navigation directory, we’ll create a new navigation resource file. In this example, I’ve named it navigation but you can name it whatever you like.
  3. Select Navigation from the Resource type drop-down list, and then click OK.
Creating a navigation graph file
Navigation graph in the project panel

Now, we will modify out activity_main.xml and replace the default <TextView/> with the <fragment/> given below-

<fragment
android:id="@+id/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_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/navigation"
/>

Once we’ve done that, we should see a host (activity_main) added to the Navigation graph (in navigation.xml)

Navigation graph with a host

Now we can add a new destination by clicking on the ‘Click to add a destination’ option in the center of the navigation graph. Then select ‘create new destination’ options, this will open Android New Component Dialog where we can create a new Fragment.

For this example, we’ll have two fragments namely — SignupFragment and LoginFragment. After creating these Fragments, click on the fragment and attach it to the other one. The navigation graph should look something like this —

Navigation Graph with a single action

navigation.xml will look something like this for the above Navigation Graph-

<?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/navigation_graph"
app:startDestination="@id/signupFragment3"
>
<fragment
android:id="@+id/signupFragment3"
android:name="com.navigation.view.SignupFragment"
android:label="fragment_signup"
tools:layout="@layout/fragment_signup"
>
<action
android:id="@+id/action_signupFragment3_to_loginFragment3"
app:destination="@id/loginFragment3"
/>
</fragment>
<fragment
android:id="@+id/loginFragment3"
android:name="com.navigation.view.LoginFragment"
android:label="fragment_login"
tools:layout="@layout/fragment_login"
>
<action
android:id="@+id/action_loginFragment3_to_signupFragment3"
app:destination="@id/signupFragment3"
/>
</fragment>
</navigation>

Here the root tag will be navigation with startDestination attribute in which we mention the id of the fragment to be loaded initially followed by destinations, like fragments, dialogs and so on, with one of the attributes name, whose value will be the name of your destination.

Now we come to the navigation. We use a tag action under a specific destination — where we want to initiate the navigation. There’s also an attribute in which we mention the destination ID.

The SignupFragment layout will have two Buttons (with id’s as — signupBtn and gotoLoginBtn) and three EditText( with id — signupUsername, signupPassword, otherInfo).

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
gotoLoginBtn.setOnClickListener { onGotoLogin(it)
}private fun onGotoLogin(v: View) {
val action = SignupFragmentDirections.actionGoToLogin()
Navigation.findNavController(v).navigate(action)
}

Type safe dependency will generate class in the format of name_of_file_Directions (In our case source fragment SignupFragmentDirections) which consists the <action></action> in navigation file (navigation).

Now we will try to create a Navigation Graph with multiple actions. Let me show you how the navigation graph will look with three fragments and multiple actions.

Navigation graph with multiple actions

Now let’s take a look at the XML code in the navigation tab to create a graph like above:

<?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/navigation"
app:startDestination="@id/signupFragment"
>
<fragment
android:id="@+id/signupFragment"
android:name="com.navigation.view.SignupFragment"
android:label="fragment_signup"
tools:layout="@layout/fragment_signup"
>
<action
android:id="@+id/actionGoToMain"
app:destination="@id/mainFragment"
/>
<action
android:id="@+id/actionGoToLogin"
app:destination="@id/loginFragment"
/>
</fragment>
<fragment
android:id="@+id/loginFragment"
android:name="com.navigation.view.LoginFragment"
android:label="fragment_login"
tools:layout="@layout/fragment_login"
>
<action
android:id="@+id/actionGoToMain"
app:destination="@id/mainFragment"
/>
<action
android:id="@+id/actionGoToSignup"
app:destination="@id/signupFragment"
/>
</fragment>
<fragment
android:id="@+id/mainFragment"
android:name="com.navigation.view.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main"
>
<action
android:id="@+id/actionGoToSignup"
app:destination="@id/signupFragment"
/>
</fragment>
</navigation>

That’s it. Basic navigation is implemented in your app. You will be able to navigate to the LoginFragment with this.

Now as we have learned Basic implementation we will see how to implement Navigation With Arguments:

We can achieve this in two possible ways.

  1. Using bundles
  2. Using SafeArgs

Here I’ll implement it in both the ways and will also explain how passing data with SafeArgs is better than passing data through bundles.

Using Bundles: Pass data from the source fragment by getting the nav controller of view as

val bundle = bundleOf("username" to signupUsername.text.toString())
Navigation.findNavController(signupUsername).navigate(R.id.actionGoToMain, bundle)

bundleOf() is the extension method of androidx. For that, we need to add below dependency in the app-level build.gradle file:

implementation 'androidx.core:core-ktx:1.3.0-rc01'

Receive data in destination fragment as :

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
signoutBtn.setOnClickListener { onSignout() }
usernameTV.text = "Hello "+ arguments?.getString("username")
observeViewModel()
}

This is how we achieve passing data through a bundle from one fragment to another. However, I’m sure this question must be popping up in your mind that if we can easily pass data through Bundle then why do we need SafeArgs? Why should we use it?

I agree that we can pass data through Bundle but Sometimes what happens is we try to get data in a destination that has never passed in as extra params of a bundle which causes the null pointer exception in runtime. To avoid this problem, architecture components provide a type-safe way.

How to use Safe Arguments for passing data-

As we have safe args plugin active in our project, let’s see how to use it.

First, mention the arguments required for the destination in navigation.xml, 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/navigation"
app:startDestination="@id/signupFragment"
>
<fragment
android:id="@+id/signupFragment"
android:name="com.navigation.view.SignupFragment"
android:label="fragment_signup"
tools:layout="@layout/fragment_signup"
>
<action
android:id="@+id/actionGoToMain"
app:destination="@id/mainFragment"
/>
<action
android:id="@+id/actionGoToLogin"
app:destination="@id/loginFragment"
/>
</fragment>
<fragment
android:id="@+id/mainFragment"
android:name="com.navigation.view.MainFragment"
android:label="fragment_main"
tools:layout="@layout/fragment_main"
>
<argument
android:name="username"
app:argType="string"
android:defaultValue="default"
/>
<action
android:id="@+id/actionGoToSignup"
app:destination="@id/signupFragment"
/>
</fragment>
</navigation>

We send data as from source fragment as :

super.onViewCreated(view, savedInstanceState)
signupBtn.setOnClickListener { onSignup() }
}
private fun onSignUp(){
val action = SignupFragmentDirections.actionGoToMain(signupUsername.text.toString())
Navigation.findNavController(signupUsername).navigate(action)
}

Receive data as in destination fragment as:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
signoutBtn.setOnClickListener { onSignout() }
deleteUserBtn.setOnClickListener { onDelete() }

usernameTV.text = "Hello "+ MainFragmentArgs.fromBundle(requireArguments()).username
}

Type safe dependency will generate class in the format of name_of_file_Args (In our case destination fragment MainFragmentArgs) which consists the <arguments></arguments> in navigation file (navigation).

Yayyy!! And we are good to go.

You can also add Actions and Deep Links through the navigation graph.

Navigation With Deeplink:

In Android, a deep link is a link that takes you directly to a specific destination within an app. To add deep-link, click on the + icon in the Argument panel from the Deep Link section. In the Add deep link dialog, enter the URI.

That’s all folks!! Enjoy coding.

To see the complete project, do visit the GitHub link :

https://github.com/ashitaasati1608/NaviagtionComponent

--

--