Difficulty: Beginner
Implementing Bottom navigation on android can be a tricky task, since there isn’t clear instructions on how it can be implemented properly, in this article we are going to implement the material design ‘bottom navigation view’ with multiple back stacks that preserve their state when switching between different screens.
Setup
To use bottom navigation view we will need the material dependency get the latest version here.
//build.gradle (module)
dependencies {
implementation 'com.google.android.material:material:1.8.0'
}
In this example im going to use androidx navigation and safe args but feel free to skip them if you want.
plugins {
id 'androidx.navigation.safeargs.kotlin'
}
dependencies {
def nav_version = "2.5.3"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}
buildscript {
repositories {
google()
}
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3"
}
}
Adding our fragments
In our project we want to display a bottom navigation bar with three options Home, Profile and Favorites so lets create these fragments by right clicking our app package name and select new > Fragment > Blank
These fragments might have a lot redundant code that we won’t need so lets delete it and replace each fragment with this (don’t forget to change names for each fragment)
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import com.example.bottomnavsample.databinding.FragmentHomeBinding
class HomeFragment : Fragment(R.layout.fragment_home) {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentHomeBinding.bind(view)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Creating a menu
To display our options in a bottom navigation view we will have to create a menu with the appropriate title, icon and id for each item. to create a menu right click on the res folder in android studio > New > Android Resource Directory, then from the resource type drop down choose menu and click OK.
Right click your newly created menu directory > New > Menu Resource File.
Add 3 items to your new menu with ids and icons, Notice the name of the ids for the menu items as they are important for navigation later.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/home_nav"
android:icon="@drawable/ic_home_black_24dp"
android:title="Home" />
<item
android:id="@+id/profile_nav"
android:icon="@drawable/baseline_person_24"
android:title="Profile" />
<item
android:id="@+id/favorite_nav"
android:icon="@drawable/baseline_star_24"
android:title="Favorite" />
</menu>
Creating the Navigation Graph
Right click the res folder > New > Android Resource Directory, from the window choose navigation as the resource type. Now create 4 new navigation graphs in this resource directory, the first one will be called main_graph the rest 3 will have the exact name as the ids from the menu items we just created (home_nav, profile_nav, favorite_nav)
Open up home_nav click the New Destination button and add your home fragment
Repeat the same for profile_nav and favorite_nav, and add the appropriate fragment for each one of them.
On main_graph click the New Destination button then add home_nav.xml, profile.xml and favorite.xml
Feel free to add more fragments and connect them with actions in each of your navigation graphs.
Connecting everything with bottom navigation view
In our activity_main.xml we want to add we will add a bottom navigation view and fragment container. Notice the attributes on fragment container name and navGraph.
navGraph points to the main graph that includes all of our other graph files, name value is set to NavHostFragment which is a class in androidx libraries that provides an area within your layout for self-contained navigation to occur. the NavHostFragment has a navController and a navigation state meaning the state of each of our options in the bottom navigation view will be saved and restored when switching items.
defaultNavHost gives us the expected behavior to navigate to the previous fragment when we press the back button.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/main_graph"
app:defaultNavHost="true"
tools:layout="@layout/fragment_home" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/nav_menu"/>
</LinearLayout>
And last but not least we’ll have to attach the navController to our bottom navigation view in MainActivity
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.setupWithNavController
import com.example.bottomnavsample.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
navController = navHostFragment.findNavController()
//Give the action bar the correct title for the current screen (this uses the fragment label)
appBarConfiguration = AppBarConfiguration(
setOf(R.id.homeFragment, R.id.profileFragment, R.id.favoriteFragment2)
)
//Connects the action bar with the nav controller
setupActionBarWithNavController(navController, appBarConfiguration)
binding.bottomNav.setupWithNavController(navController)
}
//gives functionality to the back button on the action bar
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration)
}
}
The sample project is available on Github.