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"
}
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.
- Destinations are the screens (Fragments) of the app.
- 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>
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.
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" />
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)
}
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"
}
}
And apply the plugin:
apply plugin: "androidx.navigation.safeargs.kotlin"
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)
}
// DetailFragment.kt
private val args: DetailFragmentArgs by navArgs()
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>
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>
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>
Now we need to setup the NavController
in the UI component like this:
// MainActivity.kt
val navController = findNavController(R.id.navController)
bottomNavigationView.setupWithNavController(navController)
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)