Navigation in Android: Navigation Architecture Component

Myric September
8 min readMay 27, 2019

--

In this tutorial we will take a look at Android’s Navigation architecture component. The purpose of this component is to simplify the implementation of navigation in our android apps. The navigation component forms part of a larger group of libraries called Jetpack.

Among other benefits such as Automatic handling of fragment transactions, Correctly handling up and back action by default, Default behaviours for animations and transitions and Type safety when passing information while navigating, this component also gives us the ability to visualise our navigation graph and see an overview of the flow of our app.

Let’s get started!

We will be building an app called CountryFact which stores statistical information of different countries in Africa. Download the starter project from my github using the below link:

Starter Project

Run the project. You will see a list of countries loaded from the CountryInfoProvider . When you click on a country you will be navigated to the detail screen containing all the information relating to the selected country. Play around with the app and loop through the starter code to familiarise yourself.

Add Gradle dependencies

To use the Navigation component we need to add some dependencies. You can get the latest dependency here. At the time of writing this article the below was latest so go ahead and add it to your Build.gradle as follows:

// Navigation Components

def navigationVersion = "1.0.0-rc02"
implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"
implementation 'android.arch.navigation:navigation-fragment:1.0.0'

Next we will create a navigation graph for our application. The navigation graph is used to define all the possible paths a user can take to navigate through your application. It enables us to visually see our app’s entire journey from a given destination to the next.

Right click on the res directory and select NewAndroid Resource File. In the New Resource File dialog, input countryfact_navigation_graph as the File name: and select Navigation in the Resource type: drop-down list. Click OK to create your graph.

Add a main entry point

Google advocates that we take the single activity, multi fragment approach when building applications. We will now set the activity_main.xml layout as our main entry point into our application. Open the activity_main.xml layout and update it as below:

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.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="com.myricseptember.countryfact.ui.MainActivity"
>

<fragment
android:id="@+id/navigationHostFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/countryfact_navigation_graph"
/>

</android.support.constraint.ConstraintLayout>

The navigationHostFragment will be the main access point for all our fragments.

Let’s take a look at some lines of code in our navigation host:

android:name="androidx.navigation.fragment.NavHostFragment"

The above is a widget you add to your layout which is used to display different destinations from your Navigation Graph.

app:navGraph="@navigation/countryfact_navigation_graph" />

The above line is used to specify our navigation graph. Remember our navigation graph can be used to find all possible paths a user can take to navigate through your application and all defined destinations.

Next you will modify MainActivity to make use of the Navigation Component to handle navigation in the app for us.

Update MainActivity

Replace all the code in your MainActivity with the below chunk of code:

private lateinit var navigationController: NavController

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

navigationController = findNavController(R.id.navigationHostFragment)
NavigationUI.setupActionBarWithNavController(this, navigationController)

}

override fun onSupportNavigateUp() = navigationController.navigateUp()

What are we doing here?

private lateinit var navigationController: NavController

In the above piece of code we are declaring an instance of our NavController specifically for this activity.

navigationController = findNavController(R.id.navigationHostFragment)

We then initialise our NavController using the findNavController() method and attach it to the navigationHostFragment that we created earlier in our layout.

NavigationUI.setupActionBarWithNavController(this, navigationController)

Next, to enable the ActionBar to show a back button whenever a child fragment is attached to this Activity. We tie the navigationController to the ActionBar for this activity using the NavigationUI helper class.

override fun onSupportNavigateUp()   = navigationController.navigateUp()

Finally, the above line of code is used to correctly handle up and back action by default .

When our application launches we need to tell our application what the default destination is. This means that we need to specify what must be the first screen to be displayed to the user when the application is first launched. Let’s see how we can achieve that.

Default destination

Open the countryfact_navigation_graph.xml.

The red rectangles show two places you can click to specify your default destination. A dialog will open from which you can either search the fragment that you want to set as the default destination or you can manually scroll down the given list.

Select CountryListFragment as your default destination.

Once you’ve selected CountryListFragment you will notice that it basically shows blank with some grey text indicating that the preview is unavailable. Let’s fix that so you can see the actual preview.

Click on the “Text” tab to open the XML and replace the countryListFragment with the below piece of code:

<fragment android:id="@+id/countryListFragment"
android:name="com.myricseptember.countryfact.ui.list.CountryListFragment"
android:label="CountryListFragment"
tools:layout="@layout/fragment_country_list"
/>

The only difference is that you added the below line of code in order for the preview to show. This specifies which layout to show for that particular destination.

tools:layout="@layout/fragment_country_list"

Cool! You’ve set your default destination.

Now that we have our default destination we want to navigate to the fragment that will show us the details relating to a particular country when selected by the user.

Navigate to next fragment

Click on the new destination button on the top left and select fragment_country_details from the dropdown. The CountryDetailsFragment fragment is now added as one of your destinations and will be the destination we will navigate to from our default destination.

When you select the CountryListFragment you will see a circle appear on the side. Drag the circle to the CountryDetailsFragment destination. Once done you will see an arrow connecting the two destinations.

Note: Just as with the countryListFragment destination remember to also add the below line in the countryDetailsFragment destination XML to see how the destination will look like.

tools:layout="@layout/fragment_country_details"

Next, open the CountryListFragment and replace all the code in the onItemClick() method with the below code:

view?.findNavController()?.navigate(
R.id.action_countryListFragment_to_countryDetailsFragment2)

Instead of using an intent for navigation we are now using the NavController to perform the navigation. You will notice we’re using an id that wasn’t created by you. The id action_countryListFragment_to_countryDetailsFragment2 was auto generated by Android studio when you connected the countryListFragment and the countryDetailsFragment destinations. With our new changes in place we no longer need the CountryDetailsActivity and the activity_country_details layout from the project so delete that.

Note: When the “Usages Detected” dialog pops up just click on Delete Anyway since its detecting usages that we will also delete.

Also remove the below from the manifest:

<activity
android:name=".ui.details.CountryDetailsActivity"
android:label="@string/country_details"
android:parentActivityName=".ui.MainActivity"
/>

Run the app and see the navigation component in action.

But wait! The information related to the specific country isn’t showing anymore? This is because we removed the functionality for the app to pass data between screens. We will fix this next by adding functionality for passing the data to the next screen.

Passing data to between Fragments

We will now be passing the country data based on the country that is selected from the list. Open the CountryListFragment fragment and replace the code in the onItemClick() method with the below:

val countryFactBundle = Bundle().apply {
putInt(getString(R.string.country_id), country.id)
}

view?.findNavController()?.navigate(
R.id.action_countryListFragment_to_countryDetailsFragment2,countryFactBundle)

Here, we create a bundle to store the id of the selected country which we will use to display the data based on the id in the next fragment. We then use our NavController again but this time with an additional parameter to pass the bundle.

Now, open the CountryDetailsFragment and replace :

val countryId = activity?.intent?.getIntExtra(getString(R.string.country_id),0)

with:

val countryId = arguments?.getInt(getString(R.string.country_id))

Run the app again to see if the selected country information is displaying.

The country information should now be displaying properly. But you’ll notice that the title is showing the fragment name which is definitely not what we want to show the user.

Fix the title

The title is auto-generated every time we add the fragment as a destination within countryfact_navigation_graph.xml. We can easily fix this by changing the title from within our navigation graph.

Open countryfact_navigation_graph.xml and select the Design tab if you aren’t already there. Select the CountryListFragment and change the Label on the right hand side to Country Fact. Next, we want the name of the selected country to be displayed as the title .Open the CountryDetailsFragment and add the bellow lines of code in populateCountryDetails() method:

(activity as AppCompatActivity).supportActionBar?.title = country?.name

Run the app again. You will now see that the title display correctly.

Great! Our app is now doing what we want it to do. But we can make it just a little better by animating the transition between screens. So let’s do that now.

Animate transitions between destinations

We are provided with the following animations to choose from:

· Entering a destination

· Exiting a destination

· Entering a destination via a pop action

· Exiting a destination via a pop action

Note: If you want to read more about the pop action follow this link.

To add some animations to actions do the following:

1. In the Navigation editor, click on the action(arrow) where the animation should occur.

2. On the right side in the Animations section of the Attributes panel, click the dropdown arrow next to the animation you’d like to add and choose from the animation options as discussed above.

Now run the app again. We now have some nice animation when navigating between the different screens.

Conclusion

Awesome! We created a simple app using the Navigation Architecture Component. There is still much more you can learn about when using the Navigation Architecture Components such as:

· Menu navigation, bottom navigation, and menu drawer navigation

· Deep links etc.

I look forward to your feedback and if, for some reason, you got stuck I would advise you to take a look at the repo containing the completed project.

Thank you for reading this article. Click the 👏 button as much you can, 🌟the github repo and follow me on Twitter and on Medium for future articles.

--

--

Myric September
Myric September

Written by Myric September

Senior Android Engineer @ Primotion; Johannesburg South Africa; Kotlin star; Speaker; Blogger

Responses (1)