Make an Awesome Bottom Navigation using Jetpack Compose
Easily make bottom navigation in compose and Jetpack Navigation Library
Building bottom navigation using compose is so easy, it feels like a dream. With the release of the latest version of jetpack navigation, there are not many tutorials that adhere to best practices while implementing them.
This tutorial is meant for a detailed explanation on how to make awesome bottom navigation with jetpack compose and navigation framework using recommended best practices.
I will explain every line of code in detail so that even if you are a newbie in android you can follow along easily.
We will be building a basic navigation app for the sake of this tutorial
Getting the required libraries
Navigation in compose requires at least 3 libraries to work, of course you can add more check out this link for more information https://developer.android.com/jetpack/androidx/releases/navigation
You will need all these 3 libraries if you are using kotlin and jetpack compose.
Start Composing
Step 1: Define MainActivity
In the MainActivity file, we will call a composable function called AppMainNavigation()
in the setContext{}
( ). This function will hold the information needed to route to all our screens. Separating the navigation logic into a different file will help your MainActivity
stay decluttered.
If you have a web development background The AppMainNavigation() function is like router to all the screens in your pokemon viewer app.
Step 2: Define your Screens(Routes)
For the sake of easing the process of navigating between different screen, we will create a sealed class which will hold all the meta-data about our screens.
These screen objects hold three parameters:
- A String to represent a route.
- A string label for each of the bottom nav buttons.
- An ImageVector for the Icon.
These screens are actually objects that defines the meta data for each screen.
Step 3: Build a NavGraph using the Screens(Routes)
Now we are starting to deal with the Jetpack Navigation library. We are going to define the routes in our app within AppMainNavigation()
.
Let's understand what just happened:
val navController = rememberNavController()
The rememberNavController()
the method provided by the navigation library which is used for holding the state of your navigation (current screen, navigation back stack, etc).
As the name suggests we will use this navController
to navigate to different screens later and control which screen to render in response to click events in our bottom navigation. This works similar to what we are used to in reacting to click events in a webpage using a button.
composable(IconScreens.Home.route) {
HomeScreen(navController)
}
After that, we have defined 4 routes for our demo app namely — Home, Explore, Subscription, Library. These are defined using composable which take a route as a parameter and helps build the navigation graph. Navigation graph is built internally hench we don’t have to do much rather than passing a route as a parameter in composable()
and then defining a composable inside it.
Step4: Define your Application Skeleton using Scaffold
From here it may be hard to follow for some of you but it is an awesome way to modularize your screens.
For us to really modularize our app (One of the greatest strengths of compose) we will define a ScaffoldScreen()
composable and then call various screens(other composables) into it just like we do with fragments.
The ScaffoldScreen()
takes our Screens(1 of the 4 screens) Composables as the second parameter which you can relate with fragments in XML. (explained in step 6)
We are using a Scaffold composable just to properly place bottom navigation at the bottom of the app.
val bottomNavigationItems = listOf(
IconScreens.Home,
IconScreens.Explore,
IconScreens.Subscription,
IconScreens.Library
)
We have defined a list of Screens to be rendered at the bottom Navigation bar. The route data of each of these screens will be linked to icons in bottomNavBar
.
we will pass this bottomNavigationItems
list to BottomAppNavBar
along with navController
.
Scaffold(
topBar = {},
bottomBar ={BottomAppNavBar(navController,bottomNavigationItems)},
content = { screen() }
)
This screen
is used in the content parameter of Scaffold composable
.This makes it dynamic. We can add an endless number of screens without ever changing this composable.
As for the bottom navigation, we are passing another composable to bottomBar =
parameter of scaffold called BottomAppNavBar()
to which we pass the navController
.
Step 5: Define your bottom Navbar Composable
We will be using BottomAppBar(){}
which is highly customizable Material Themed for rendering the bottom navigation bar.
This composable iterates over each of the received bottomNavigationItems
list and then render a clickable IconButton for each of the routes.
we can get the current back stack entry from navController as a state linke so:
navController.currentBackStackEntryAsState()
Then we extract the current route string representation from its arguments property using the KEY_ROUTE constant:
navBackStackEntry?.arguments?.getString(KEY_ROUTE)
This will help us know which screen is currently been shown.
BottomNavigationItem(
icon = {Icon(imageVector = screen.icon, contentDescription = null)}, selected = currentRoute == screen.route,
onClick = {
navController.navigate(screen.route) {
popUpTo = navController.graph.startDestination
launchSingleTop = true
}
})
Using BottomNavigationItem we represent each route at the bottom navbar with an icon. Optionally you can add a label too.
onClick = {
navController.navigate(screen.route) {
popUpTo = navController.graph.startDestination
launchSingleTop = true
}
The onCLick parameter helps us define the logic for actions to take after clicking the icon. We use navController.navigate(screen.route)
to go to the respective state. The popUpTo = navController.graph.startDestination
pops all the backstackscreen back to the root screen (in our case Home Screen) whenever the pack button is pressed.
The launchSingleTop = true
launches this navigation graph as singleTop so that the same screen is not rerendered whenever we are clicking the same button more than once at a time.
Step 6: Define Screens to show after Clicking bottom navbar Icons (Fragments in XML terms)
Finally, We define our 4 different composable to display on clicking each of the buttons in the bottom bar.