You're reading for free via Jayant Kumar🇮🇳's Friend Link. Become a member to access the best of Medium.
Member-only story
Bottom Navigation Bar in Jetpack Compose.
In this article we will be talking about Bottom Navigation Bar in Jetpack Compose.
Basically we will have three screens …..
Splash → BottomBar (Home , Notification , Profile)-> Detail

First screen will be your splash screen
after 2 secs it will move to bottom bar screen
, when we will click on move to detail screen button
it will move to detail screen
and we will also see how to hide bottom Bar
when it’s not needed , Interesting!!
First let’s define all the required routes/tags
(unique keys) for all the composable functions.
BottomBar Routes
enum class BottomBarRoutes(
val id: Int,
@StringRes val title: Int,
val routes: String,
@DrawableRes val icon: Int
) {
HOME(1, R.string.home, "/home", R.drawable.home),
NOTIFICATION(
2,
R.string.notification, "/notification", R.drawable.notification
),
Profile(3, R.string.profile, "/profile", R.drawable.profile)
}
As you can see in the above code , we have created an enum class , which contains Bottom Bar’s routes
, title
& icon
.
Normal Screen Routes
sealed class ScreenRoutes(val route: String) {
data object Splash : ScreenRoutes("/splash")
data object BottomBar : ScreenRoutes("/bottombar")
data object Detail : ScreenRoutes("/detail")
}
As you can see above , we have created routes
for splash
, bottombar
& detail
screen. bottombar
object will be your nested navigation , which contains BottomBarRoutes
enum inside it (if you didn’t get don’t worry).
Okay so we defined all the routes now let’s create all the composable functions ( splash, bottombar (Home , Notification , Profile) , detail screens ).
Splash Screen
@Composable
fun SplashScreen(
navHostController: NavHostController
) {
LaunchedEffect(key1 = Unit){
delay(2000)
navHostController.navigate(ScreenRoutes.BottomBar.route)
}
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
Text(text = "Splash Screen", style = TextStyle(
color = Color.Black,
fontWeight = FontWeight.SemiBold,
fontSize = 30.sp
)
)
}
}
In the splash screen composable function , we are navigating to BottomBar screen
after 2 secs with help of LaunchEffect
composable function.
Home Screen
@Composable
fun HomeScreen(
navHostController: NavHostController,
context: Activity
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Button(onClick = {
navHostController.navigate(ScreenRoutes.Detail.route)
}) {
Text(text = stringResource(R.string.move_to_detail_screen))
}
}
BackHandler {
context.finish()
}
}
Notification Screen
@Composable
fun NotificationScreen(
navHostController: NavHostController
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
Text(text = "Notification Screen", style = TextStyle(
color = Color.Black,
fontWeight = FontWeight.SemiBold,
fontSize = 30.sp
)
)
}
BackHandler {
navHostController.navigate(BottomBarRoutes.HOME.routes){
popUpTo(BottomBarRoutes.HOME.routes){
inclusive = true
}
}
}
}
Here In the above code , we have BackHandler
composable function , which will handle back press ( like whenever we press back button in the bottombar screen , it will move to home screen
).
Profile Screen
@Composable
fun ProfileScreen(
navHostController: NavHostController
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
Text(text = "Profile Screen", style = TextStyle(
color = Color.Black,
fontWeight = FontWeight.SemiBold,
fontSize = 30.sp
)
)
}
BackHandler {
navHostController.navigate(BottomBarRoutes.HOME.routes){
popUpTo(BottomBarRoutes.HOME.routes){
inclusive = true
}
}
}
}
Detail Screen
@Composable
fun DetailScreen(
navHostController: NavHostController
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
Text(text = "Detail Screen", style = TextStyle(
color = Color.Black,
fontWeight = FontWeight.SemiBold,
fontSize = 30.sp
)
)
}
}
That’s it , Now it’s time to define Navigation graph
, where we will keep all the composable function for the navigation.
Navigation Screen
@Composable
fun BottomBarNavigation(
navHostController: NavHostController,
padding: PaddingValues,
context:Activity
) {
NavHost(
navController = navHostController, startDestination = ScreenRoutes.Splash.route,
modifier = Modifier.padding(padding)
) {
composable(ScreenRoutes.Splash.route) {
SplashScreen(navHostController = navHostController)
}
navigation(
route = ScreenRoutes.BottomBar.route,
startDestination = BottomBarRoutes.HOME.routes
) {
composable(BottomBarRoutes.HOME.routes) {
HomeScreen(navHostController = navHostController,context)
}
composable(BottomBarRoutes.NOTIFICATION.routes) {
NotificationScreen(navHostController = navHostController)
}
composable(BottomBarRoutes.Profile.routes) {
ProfileScreen(navHostController = navHostController)
}
}
composable(ScreenRoutes.Detail.route) {
DetailScreen(navHostController = navHostController)
}
}
}
In the above code , we have created NavHost
and inside it we passed all the composable functions. Splash
& Detail
screen will be in the composable
function but bottombar
will be in your navigation
function (Nested Navigation). and navigation
function also contains route
(through that we will identify the bottombar
screen) and startDestination
will be your first screen when we reached to bottombar screen. Hope you got it.
Now Create BottomBar
section
@Composable
fun BottomBarRow(
navHostController: NavHostController
) {
val tabList = listOf(
BottomBarRoutes.HOME,
BottomBarRoutes.NOTIFICATION,
BottomBarRoutes.Profile
)
val navStackBackEntry by navHostController.currentBackStackEntryAsState()
val currentDestination = navStackBackEntry?.destination
Row(
modifier = Modifier
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically
) {
tabList.forEach { tab ->
BottomBarItems(
tab = tab,
currentDestination = currentDestination,
navHostController = navHostController
)
}
}
}
@Composable
fun BottomBarItems(
tab: BottomBarRoutes,
currentDestination: NavDestination?,
navHostController: NavHostController
) {
val selected = currentDestination?.hierarchy?.any { it.route == tab.routes } == true
val contentColor =
if (selected) Color.Unspecified else MaterialTheme.colorScheme.onPrimary
IconButton(onClick = {
navHostController.navigate(tab.routes)
}) {
Icon(
painter = painterResource(id = tab.icon),
contentDescription = "",
tint = contentColor,
modifier = Modifier.size(30.dp)
)
}
}
In the above code we create three tabs for our bottombar.
Now comes into the main part , hide bottombar when it's not needed !!
we don’t need to show bottombar when we in the splash
& detail
screen. For that we have to create states, which will keep track the current destination of navigation graph.
@Composable
fun rememberAppState(
navHostController: NavHostController = rememberNavController()
) = remember(navHostController) {
AppState(navHostController)
}
@Stable
class AppState(
val navHostController: NavHostController
) {
private val routes = BottomBarRoutes.values().map { it.routes }
val shouldShowBottomBar: Boolean
@Composable get() =
navHostController.currentBackStackEntryAsState().value?.destination?.route in routes
}
Now in the above code , we have created a AppState
class which contains navHostController
parameter (this object will be the same for all the composable functions).
After that we get all the routes
of bottombar
(Home , Notification , Profile). and check if it’s present in the current destination or not through navHostController
(it will return a boolean value).
Now pass this AppState
class in the rememberAppState
composable function to remember all the things , like whenever screen destination change it will recompose.
Now pass navigation
, bottombar
& rememberAppState()
in the Scaffold
composable function , because through Scaffold
we can show bottombar.
Goto your MainActivity …..
MainActivity
@OptIn(ExperimentalMaterial3Api::class)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ArticlesRepositoryTheme {
val appState = rememberAppState()
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Scaffold(
bottomBar = {
if (appState.shouldShowBottomBar)
BottomAppBar(
containerColor = MaterialTheme.colorScheme.primary,
contentPadding = PaddingValues(horizontal = 20.dp),
modifier = Modifier
.height(70.dp)
.clip(
RoundedCornerShape(
topStart = 24.dp, topEnd = 24.dp
)
)
) {
BottomBarRow(
navHostController = appState.navHostController,
)
}
}
) { innerPadding ->
BottomBarNavigation(
navHostController = appState.navHostController,
padding = innerPadding,
this
)
}
}
}
}
}
}
As you can see in the above code , first we called rememberAppState()
composable function. and in the bottombar
section first we check if the appState.shouldShowBottombar
is true or not , if it’s true then show bottombar otherwise hide it.
And also passed BottomBarNavigation
composable function in the content
parameter of Scaffold
for the navigation.
SOURCE CODE
That’s all for today my friends , Hope you enjoyed and learnt lots of thing :)