Android Navigation in a Multi-module Project with Bottom Navigation
Android Jetpack is a good set of libraries that allows us to simplify the development process of typical solutions. Despite these bonuses, even if you develop an application that is based on standard components and libraries from Google without sophisticated UI elements, you may encounter difficulties, the solution to which may not be the most obvious.
In this article, I’m going to describe how to add Jetpack Navigation Library in a multi-module application. One of the features of this example would be that the application will contain a Bottom Navigation.
Project structure
The general structure of modules in the app:
APP
App is the main module of the application. This module has links to the all feature modules and to the navigation module. Here we store the main activity, which is the host for our fragments.
<LinearLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"> <androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/app_nav_graph" />
</LinearLayout>
App module does not need to know about the design implementation of other modules. You should not use toolbars, bottom navigation, and other UI components which you may show or hide by some rules — do not use those hacks here. Main activity is a white paper for our ideas.
Navigation
This module has a navigation graph of our application, navigation animations, and other navigation-related resources.
In this XML file, we describe all fragments of the app, actions, and arguments. Navigation module doesn’t know about feature modules so Android Studio will mark all fragments red — do not worry about this. This tricky hack will work after compilation the app.
Feature
Each feature module it’s just a logical block of the app (like settings, user profile, etc.). It can be one or a group of screens — depends on your vision. Feature module has a link on Navigation module, thus you can use navigation controllers and do navigation across all the screens in the app.
Due to these little tricks, our multi-modal app has made great friends with Navigation Library without any extra complex components or approaches.
Now, let’s move to work with Bottom Navigation.
Bottom Navigation
Let’s imagine that one of our features needs bottom navigation element.
Step 1
First of all, we need to create navigation graphs for each item of bottom navigation. In this sample it will be:
- favorites_nav_graph.xml
- search_nav_graph.xml
- information_nav_graph.xml
For example, favorites_nav_graph.xml will be like this:
<navigation
...
android:id="@+id/favorites_nav_graph"
app:startDestination="@id/favorites_fragment">
<fragment
android:id="@+id/favorites_fragment"
android:name="com.sample.feature2.FavoritesFragment" />
</navigation>
Step 2
Next, we need to define a menu for the bottom navigation component. One of the important things: IDs of menu items should be the same as names of navigation graphs.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/favorites_nav_graph"
android:icon="@drawable/ic_favorites"
android:title="Favorites" />
<item
android:id="@+id/search_nav_graph"
android:icon="@drawable/ic_search"
android:title="Search" />
<item
android:id="@+id/infrormation_nav_graph"
android:icon="@drawable/ic_info"
android:title="Information" />
</menu>
Step 3
At this stage we need to get help from Google team: we will take an extension for bottom navigation from Android GitHub repo: NavigationExtensions
Step 4
This is the last step. Here, we need to integrate the NavigationExtensions with our feature starting fragment.
class FeatureFragment : BaseFragment()
{
override fun onViewCreated(view: View, savedState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (savedInstanceState == null) {
initBottomNavigation()
}
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState) initBottomNavigation()
}
private fun initBottomNavigation() {
val navController = bottomNavigation.setupWithNavController(
navGraphIds = listOf(
R.navigation.favorite_nav_graph,
R.navigation.search_nav_graph,
R.navigation.information_nav_graph
),
fragmentManager = childFragmentManager,
containerId = R.id.nav_host_container,
intent = requireActivity().intent
)
}
}
That’s all. If you need to add more screens, like navigation to details screen in a specific tab of bottom navigation — you just need to add this screen into the appropriate navigation graph.