Using the Navigation Component in a Modular World

Kurt Renzo Acosta
The Startup
Published in
6 min readAug 14, 2019

In this article, I’ll try to show you how to use the Navigation Component in a Modular Project.

The Navigation component is an Architecture Component and is part of Jetpack. It brings a lot to the table and hopefully, we’ll see this library grow and solve more of our problems with regards to navigation.

Why use the Navigation Component?

With the navigation component, you can see the user journey instantly through navigation graphs. Better than reading Intents and FragmentTransactions and looking at all possible destinations of that specific page

You can say goodbye to FragmentTransactions. Did you enjoy handling these? I didn’t. Now, the Navigation Component can handle this for you.

Correct Navigation between Up and Back navigation. No more worrying that the up button exits your app when coming from a deep link and speaking of deep links, they got that covered too.

Type-safety for arguments when using SafeArgs. No more need to have those safety checks and guards because it would enforce the types that you will define for navigation.

It also works with current Navigation UI Patterns like Bottom Navigation and Navigation Drawers with just a few configurations and it can handle transitions and animations between navigation.

Dynamic Feature ❌

Dynamic Feature Navigation is yet to be included in the library so if you’re using that, stick to your current navigation framework and be on the lookout for this feature.

Prerequisites

Of course, you can still read the article! But I think you would need the following to fully grasp the idea of it:

  • Navigation Architecture Component
  • Modular Android Projects

As of writing, the latest stable release for Navigation is 2.1.0-rc01

In a monolithic project, you can just put everything in that project and everything can see everything else in that project. The problem is not everyone needs everything else. Only a select few. This also applies to Navigation.

Let’s start with something simple.

Feature One

You have one feature that can go from A to B.

Easy enough. We just need a Navigation Graph with two destinations and one action to move from destination A to destination B. Here’s what your navigation graph would look like:

Feature One Navigation Graph Editor

featureone/nav_feature_one.xml

<navigation
android:id="@+id/nav_feature_one"
app:startDestination="@id/fragment_a">

<fragment
android:id="@+id/fragment_a"
android:name="com.kurt.example.featureone.FragmentA">

<action
android:id="@+id/action_go_to_b"
app:destination="@+id/fragment_b"
app:popUpTo="@+id/fragment_a" />
</fragment>
<fragment
android:id="@+id/fragment_b"
android:name="com.kurt.example.featureone.FragmentB"/>
</navigation>

This can handle the navigation for this feature.

Let’s make it bigger and have 2 more features of the same size and same kind of navigation.

Three Features

Okay. Easy. Just have two other modules with the same navigation graph as earlier

featuretwo/nav_feature_two.xml

<navigation
android:id="@+id/nav_feature_two"
app:startDestination="@id/fragment_c">

<fragment
android:id="@+id/fragment_c"
android:name="com.kurt.example.featuretwo.FragmentC">

<action
android:id="@+id/action_go_to_d"
app:destination="@+id/fragment_d"
app:popUpTo="@+id/fragment_c" />
</fragment>
<fragment
android:id="@+id/fragment_d"
android:name="com.kurt.example.featuretwo.FragmentD"/>
</navigation>

featurethree/nav_feature_three.xml

<navigation
android:id="@+id/nav_feature_three"
app:startDestination="@id/fragment_e">

<fragment
android:id="@+id/fragment_e"
android:name="com.kurt.example.featurethree.FragmentE">

<action
android:id="@+id/action_go_to_f"
app:destination="@+id/fragment_f"
app:popUpTo="@+id/fragment_e" />
</fragment>
<fragment
android:id="@+id/fragment_f"
android:name="com.kurt.example.featurethree.FragmentF"/>
</navigation>

Now, we have our navigation graphs for the three features!

But how do we move from one navigation graph to the other? I want to be able to move to feature two from feature one. Feature One to Feature Two and so on…

Let’s add an app module so we can switch between features!

Three Features with an App Module

The app module would serve as the controller that can switch between features depending on the user’s navigation.

For that to happen, we need to add the features as implementation details to our app module

app/build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
// ...
}

dependencies {
implementation project(":featureone")
implementation project(":featuretwo")
implementation project(":featurethree")

// ...
}

Now, the app module can see what those features have. We sure don’t want to redo the navigation graphs we did earlier so since we have access to them, let’s reuse them.

app/nav_app.xml

<?xml version="1.0" encoding="utf-8"?>
<navigation
app:startDestination="@id/nav_feature_one">

<include app:graph="@navigation/nav_feature_one"/>
<include app:graph="@navigation/nav_feature_two"/>
<include app:graph="@navigation/nav_feature_three"/>

</navigation>

Voila! With the use of the include tag, we “imported” the feature module’s navigation graphs into our app navigation graph.

Now you can start navigating to their destinations depending on how you want it.

Now, let’s add another use case. I want to be able to move from Feature 1 to Feature 2 without the app module. Specifically from Destination B to Destination D.

Destination B to Destination D

The first thing I did was add feature two as an implementation detail for feature one. Now, I can use feature two’s navigation graph in feature one and navigate as I want! But let’s add another use case to this. I want to be able to move from Destination B to Destination D and VICE VERSA.

Destination B to Destination D and Destination D to Destination B

Now, I can’t just add feature one as an implementation detail to feature two because that would cause a circular dependency. Feature one depending on Feature two, and Feature Two depending on Feature One. Now, it won’t know who needs who because both of them are circling on each other.

To achieve this, we need to use deep links. The library supports deep links for navigation destinations. So for this example, let’s modify their respective navigation graphs.

featureone/nav_feature_one.xml

<navigation
android:id="@+id/nav_feature_one"
app:startDestination="@id/fragment_a">

<fragment
android:id="@+id/fragment_a"
android:name="com.kurt.example.featureone.FragmentA">

<action
android:id="@+id/action_go_to_b"
app:destination="@+id/fragment_b"
app:popUpTo="@+id/fragment_a" />
</fragment>
<fragment
android:id="@+id/fragment_b"
android:name="com.kurt.example.featureone.FragmentB">
<deepLink app:uri="myApp://fragmentB"/> </fragment></navigation>

featuretwo/nav_feature_two.xml

<navigation
android:id="@+id/nav_feature_two"
app:startDestination="@id/fragment_c">

<fragment
android:id="@+id/fragment_c"
android:name="com.kurt.example.featureone.FragmentC">

<action
android:id="@+id/action_go_to_d"
app:destination="@+id/fragment_d"
app:popUpTo="@+id/fragment_c" />
</fragment>
<fragment
android:id="@+id/fragment_d"
android:name="com.kurt.example.featureone.FragmentD">
<deepLink app:uri="myApp://fragmentD"/> </fragment></navigation>

With the deepLink tag, we can specify the URI we can use to navigate to that specific destination

Now, from Feature One, without using Feature Two as an implementation detail, we can navigate using the deep link URI. For this example, let’s navigate on a view click.

featureone/FragmentB.kt

class FragmentB : Fragment() {
fun onViewCreated(...) {
...
...
view.setOnClickListener {
val uri = Uri.parse("myApp://fragmentD")
findNavController().navigate(uri)
}
}
}

And we can do the same from Feature Two!

featuretwo/FragmentD.kt

class FragmentD : Fragment() {
fun onViewCreated(...) {
...
...
view.setOnClickListener {
val uri = Uri.parse("myApp://fragmentB")
findNavController().navigate(uri)
}
}
}

And with that, you still keep your modules unaware of each other, yet able to move between one another. Moving from Fragment B to Fragment D, or moving from Fragment D to Fragment B without knowing about each other.

There you have it. Navigation in a Modular World.

Sample App

I created a sample app in trying this out. Feel free to check it out on an actual project and maybe give suggestions and comments.

And my victim from the Public APIs list is the Rick and Morty API so I created an app where we can:

  • View the list of characters
  • View the list of locations
  • View the list of episodes
  • View the details of a character
  • View the details of a location
  • View the details of an episode

Navigation Rules:

  • We can click on a character item from the list to move to the character details
  • We can click on an episode item from the list to move to the episode details
  • We can click on a location item from the list to move to the location details
  • A Character has episodes so we can view an episode’s details from the character detail’s episode list
  • An Episode has characters so we can view a character’s details from the episode detail’s character list
  • A Location has residents(characters) so we can view a character’s details from the location detail’s residents list

Here’s a visual representation of the navigation of the app:

Rick and Morty App Navigation

Thanks for reading and I hope you learned something from this article! Leave a clap and share this article if you liked it. Feel free to message me for comments, suggestions, corrections, and questions. I’d be happy to attend to them!

--

--