Easy to use Navigation component…

Peter Nagy
5 min readDec 30, 2020

When we are developing an android application earlier navigation was tricky thing because there was no any best practice how can we do the navigation between activities and fragments. Earlier I have seen lot of NavigationUtils and similar implementations. Any of that solutions was good (because there was no any alternatives) however all of them has some insufficiency:
- there is no any view in the IDE where we can see from which screen can we navigate to other screen
- adding transition (animation) is not too simple
- deeplink handling difficult (how and where to navigate)

Let's see how can we solve these problems with Jetpack's Navigation Component.

I have created a github example project:

First we need lib dependencies:
Add this lines to your build.gradle file

implementation "androidx.navigation:navigation-fragment-ktx:2.3.2"
implementation "androidx.navigation:navigation-ui-ktx:2.3.2"

If you want to check and use type safe arguments at the case of navigation(when you pass objects into the intent(s) ) add this line of code to your main build.gradle file (put this line in buildscript -> dependencies part):

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.2"

and add a plugin to the build.gradle file:

id 'androidx.navigation.safeargs.kotlin'

Ok, now we have setup the library. Now we need a base component of the Navigation component: NavHostFragment

<androidx.fragment.app.FragmentContainerView
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_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/login_flow_graph" />

It is really simple we need add it to our layout xml. I have used a FragmentContainerView but you can use simple Fragment too. The important thing we need to set

  • android:name=”androidx.navigation.fragment.NavHostFragment”
  • app:defaultNavHost=”true”
  • app:navGraph=”@navigation/login_flow_graph”

Last line -> it is a new kind of xml: navigation. It will describe from which screen will the app navigate to which screen. We can image our application's screens are node of a graph and among them there are arrows. Users can go from a screen to a screen on an arrow.

<fragment
android:id="@+id/splashFragment"
android:name="com.petnagy.navigationdemo.pages.splash.SplashFragment"
android:label="SplashFragment">
<action
android:id="@+id/action_splashFragment_to_loginFragment"
app:destination="@id/loginFragment" />
<action
android:id="@+id/action_splashFragment_to_dashboardActivity"
app:destination="@id/dashboardActivity" />
</fragment>

<fragment> is a node and <action> is an arrow. Of course there are lot of settings what we can set them. I will write about them later.

There is a new view in the Android Studio we can see the screens from which screen where we can navigate! :) Here we can see the thumbnail of our screen based on our layout. It is really useful. Anyway we can choose the xml version of it (like above).

What is next ? We need setup our navigation host fragment:

val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController

Ok, now we are able to use navController. Thanks for ktx we are able to use findNavController() method in any Activity, Fragment and View. (Latest one could help us to navigate somewhere from an OnClickListener.)

findNavController().navigate({resId | deepLinkUri | navDirection})

Resource id could be any id from graph xml so it could be a node (fragment or activity) or it could be an action (arrow of the graph). And it is a great benefit of Navigation Component. We can set a lot of things like transition, or we can pop the screens (before last navigation) from the graph. A NavController has a backstack and we can remove item(s) from the backstack. When we want to handle back button we can use:

findNavController().navigateUp()

Let's see something more tricky

How can we remove some nodes from backstack ?

findNavController().navigate(
R.id.action_splashFragment_to_loginFragment, null,
NavOptions.Builder().setPopUpTo(R.id.splashFragment, true).build()
)

With NavOptions we can set a pop from the backstack. Anyway we can set this in the xml too, however I was not able to do same thing with the xml.

<fragment
android:id="@+id/splashFragment"
android:name="com.petnagy.navigationdemo.pages.splash.SplashFragment"
android:label="SplashFragment">
<action
android:id="@+id/action_splashFragment_to_loginFragment"
app:destination="@id/loginFragment"
app:popUpTo="@id/login_flow_graph"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_splashFragment_to_dashboardActivity"
app:destination="@id/dashboardActivity" />
</fragment>

Can we use Fragment and (or) Activity both in the graph ?

The answer is yes. We can add Activity and Fragment to the graph. And we are able to navigate from a fragment to a new activity. In this case we must be careful and start and setup a new NavController in the new Activity.
Originally when Google announced this component, there were lot of article about navigation component support single activity applications. It is true but we can do different. If our application has lot of screens then our navigation graph will be really difficult (we are able to use sub graph too). And I think it we can simplify our graph and it means we can add more activities and they will use separate graph xml.

Is it support BottomNavigationView ?

Yes, we can use Navigation Controller with BottomNavigationView.

val navHostFragment = supportFragmentManager.findFragmentById(R.id.dashboard_nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController

NavigationUI.setupWithNavController(bottomNavView, navHostFragment.navController)

The most important part: NavigationUI.setupWithNavController(bottomNavView, navHostFragment.navController)

And there is one trick, we need to use same id in the menu xml like we used in the graph xml. In this case NavController will select the proper Fragment when user select a menu item when press the BottomNavigationView.

Navigation Controller can handle only one graph, so it is really hard to put the BottomNavigationView's graph in another graph. It is not impossible but really hard. Because of this I have decided in my example project I will cut my graph into two graphs. There is a login flow (splash, login, registration) and there is a dashboard graph. Because there are two graph there are two activities too. (Based on this it will be easier to convert the app to instant app.)

Add transition animation

It is easy to add transition animation. The most easiest way: add animation to your action. On the right side in the design view there are option to add enter and exit animation (or popEnter animation and popExit animation).

<fragment
android:id="@+id/splashFragment"
android:name="com.petnagy.navigationdemo.pages.splash.SplashFragment"
android:label="SplashFragment">
<action
android:id="@+id/action_splashFragment_to_loginFragment"
app:destination="@id/loginFragment"
app:enterAnim="@anim/slide_in_left"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_left" />
<action
android:id="@+id/action_splashFragment_to_dashboardActivity"
app:destination="@id/dashboardActivity"
app:enterAnim="@anim/slide_in_left"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_left" />
</fragment>

If we are using pop from the code (in NavOptions) then we have to set the animation in the NavOptions.

findNavController().navigate(R.id.action_splashFragment_to_dashboardActivity, null, NavOptions.Builder()
.setEnterAnim(R.anim.slide_in_left)
.setExitAnim(R.anim.slide_out_left)
.setPopUpTo(R.id.loginFragment, true).build())

I think based on this Navigation Component is worth to use it. Easy to adopt and easy to use it.

--

--