Understanding the Basics of Navigation Component

Alok Bharti
The Startup
Published in
4 min readAug 6, 2020

We all have used fragments in our app and wrote fragment transactions many times according to our needs. So, the navigation component is another way to handle transactions but it provides few other benefits too.

Some of them are:

  • Handling back action correctly by default
  • Default standard animations and transitions while switching between fragments
  • Passing data between fragments (this is also my favorite).

Okay, now let’s discuss how to integrate it into our app.

First, we’ve to add following dependencies in our app-level build.gradle file:

def nav_version = "2.3.0"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

The recommended way to navigate between destinations is to use the Safe Args Gradle plugin. This plugin generates simple object and builder classes that enable type-safe navigation and argument passing between destinations. To add Safe Args to our project, include the following classpath in our top-level build.gradle file:

buildscript {
repositories {
google()
}
dependencies {
def nav_version = "2.3.0"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}

Also, add this line to our app or module’s build.gradle file:

apply plugin: "androidx.navigation.safeargs"

There are three main components that we need to understand:

  1. Navigation Graph(nav_graph.xml): It contains all the navigation-related information like the start destination fragment, paths, etc.
  2. NavHost: A container that displays those destinations defined in nav_grap.xml file.
  3. NavController: It manages the swapping of fragments within a NavHost.

If you want to avoid implementing it from scratch, Android Studio can help you to start. Just select the navigation template while creating a project or adding a new activity.

Now to create a path between fragments, go to nav_graph.xml(it should be in res->navigation directory). Here, either you can add a path using the design editor or using code. Since I prefer using code, let’s discuss how to do that.

For our example, let’s consider we’ve one activity and two fragments A & B and we want to add a path between them. We can do so by defining like this:

<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/nav_graph"
app:startDestination="@id/fragment_a">
<fragment
android:id="@+id/fragment_a"
android:name="com.example.ui.FragmentA"
android:label="@string/fragment_name"
tools:layout="@layout/fragment_a">
<action
android:id="@+id/action_FragmentA_to_FragmentB"
app:destination="@id/fragment_b">
</action>
</fragment>

<fragment
android:id="@+id/fragment_b"
android:name="com.example.ui.FragmentB"
android:label="@string/fragment_name"
tools:layout="@layout/fragment_b" />
</navigation>

Here, the action tag represents the path. One thing that is important to mention is that the ids that you define for the fragments in nav_graph.xml should be the same in the menu.xml file(I spent almost 4 hours for debugging this).

Now we have to define host for displaying our fragments. The Navigation component contains a default NavHost implementation, NavHostFragment that displays fragment destinations. We can add so, like this:

<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">

<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bttm_nav"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />

<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bttm_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/nav_host_fragment"
app:layout_constraintBottom_toBottomOf="parent"
app:labelVisibilityMode="labeled"
app:menu="@menu/bottom_nav_menu"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Here we’ll use BottomNavigationView for our example. It contains the menu that will be used for navigating between fragments. Now, let’s add the controller part in MainActivity.kt file:

val navHostFragment: NavHostFragment =     supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragmentval bottomNavigationView: BottomNavigationView = findViewById(R.id.bttmNav)NavigationUI.setupWithNavController(bottomNavigationView, navHostFragment.navController)

Now, this is where the magic happens. NavigationUI contains static methods that manage navigation with the top app bar, navigation drawer, and bottom navigation. And since we are using the BottomNavigation view in our example, we’ll pass its id along with the NavController of the NavHostFragment defined in the layout.

So, when a user selects a menu item, the NavController calls onNavDestinationSelected() and voila, it automatically updates the selected item in the bottom navigation bar and the host will display the fragment.

Finally, the last thing that I want to discuss is how to pass data between fragments using the Navigation component. One way is to use FragmentResultListener but it involves a lot of boilerplate code. We can achieve this by defining an argument tag inside the action tag. Something like this:

<action
android:id="@+id/action_FragmentA_to_FragmentB"
app:destination="@id/fragment_a">
<argument
android:name="key"
app:argType="string"
android:defaultValue="@null"
app:nullable="true"/>
</action>

We can also make the variable nullable. Now in FragmentA.kt class, we can set the value:

val value = "set the value here"
val actions = FragmentADirections.actionFragmentAToFragmentB(value)
view.findNavController().navController.navigate(actions)

FragmentADirections class will be auto-generated. Now, in FragmentB we can get the value:

val value= arguments?.getString("key")

Conclusion

Handling fragment transitions has never been so easy before the navigation component introduced. Also, it is so easy to edit/add a path between fragments as we can see all the actions in a single file(nav_graph.xml). Check out the official android documentation for a better understanding of the Navigation Component.

Hope you learned something. Happy coding, Peace out! :)

--

--