
Jetpack Compose provides 2 types of bottom navigation APIs:
In this article, we will learn how to implement them with the help of examples.
Prerequisites:
For this article, open MainActivity, create a MyUI() composable and call it from the onCreate() method.
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme(darkTheme = false) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
MyUI()
}
}
}
}
}
}
@Composable
fun MyUI() {
}
We will write our code in the MyUI().
1. Bottom Navigation in Jetpack Compose:
Bottom Navigation is used to display the destinations at the bottom of the screen. Each destination is represented by an icon and a label.
Example:

Jetpack Compose provides BottomNavigation composable. Here is how it looks:
@Composable
fun BottomNavigation(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = BottomNavigationDefaults.Elevation,
content: @Composable RowScope.() -> Unit
)
modifier – Optional modifier.
backgroundColor – The background color. Read more about colors in Jetpack Compose here.
contentColor – The color for this component’s children.
elevation – The shadow behind the component.
content – The content of the BottomNavigation. This is a RowScope. All the children (typically icons) are added horizontally.
Here is an example (without icons):
@Composable
fun MyUI() {
Box(modifier = Modifier.fillMaxSize()) {
BottomNavigation(
// place it at the bottom of the screen
modifier = Modifier.align(alignment = Alignment.BottomCenter)
) {
}
}
}
Output:

For icons, let’s create a data class with the icon’s name (label) and vector graphics.
data class BottomMenuItem(val label: String, val icon: ImageVector)
Create a method that prepares the list. To get the icons, you should include the material icons dependency in your Gradle file.
private fun prepareBottomMenu(): List<BottomMenuItem> {
val bottomMenuItemsList = arrayListOf<BottomMenuItem>()
// add menu items
bottomMenuItemsList.add(BottomMenuItem(label = "Home", icon = Icons.Filled.Home))
bottomMenuItemsList.add(BottomMenuItem(label = "Profile", icon = Icons.Filled.Person))
bottomMenuItemsList.add(BottomMenuItem(label = "Cart", icon = Icons.Filled.ShoppingCart))
bottomMenuItemsList.add(BottomMenuItem(label = "Settings", icon = Icons.Filled.Settings))
return bottomMenuItemsList
}
Inside our bottom navigation, add icons using BottomNavigationItem() composable.
@Composable
fun RowScope.BottomNavigationItem(
selected: Boolean,
onClick: () -> Unit,
icon: @Composable () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
label: @Composable (() -> Unit)? = null,
alwaysShowLabel: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
selectedContentColor: Color = LocalContentColor.current,
unselectedContentColor: Color = selectedContentColor.copy(alpha = ContentAlpha.medium)
)
selected – Whether this item is selected.
onClick – The onClick event on the item.
icon – The actual icon.
modifier – Optional modifier.
enabled – If the item is enabled or not. If it is false, this item will not be clickable and will appear disabled to accessibility services.
label – Optional text label for this item.
alwaysShowLabel – Whether to always show the label for this item. If false, the label will only be shown when this item is selected.
interactionSource – It is the MutableInteractionSource.
selectedContentColor – The color of the icon and the text, when it is selected.
unselectedContentColor – The color of the icon and the text, when it is not selected.
Use the Kotlin’s forEach loop to iterate through the list items. Our final code:
@Composable
fun MyUI() {
// items list
val bottomMenuItemsList = prepareBottomMenu()
val contextForToast = LocalContext.current.applicationContext
var selectedItem by remember {
mutableStateOf("Home")
}
Box(modifier = Modifier.fillMaxSize()) {
BottomNavigation(
modifier = Modifier.align(alignment = Alignment.BottomCenter)
) {
// this is a row scope
// all items are added horizontally
bottomMenuItemsList.forEach { menuItem ->
// adding each item
BottomNavigationItem(
selected = (selectedItem == menuItem.label),
onClick = {
selectedItem = menuItem.label
Toast.makeText(
contextForToast,
menuItem.label, Toast.LENGTH_SHORT
).show()
},
icon = {
Icon(
imageVector = menuItem.icon,
contentDescription = menuItem.label
)
},
label = {
Text(text = menuItem.label)
},
enabled = true
)
}
}
}
}
private fun prepareBottomMenu(): List<BottomMenuItem> {
val bottomMenuItemsList = arrayListOf<BottomMenuItem>()
// add menu items
bottomMenuItemsList.add(BottomMenuItem(label = "Home", icon = Icons.Filled.Home))
bottomMenuItemsList.add(BottomMenuItem(label = "Profile", icon = Icons.Filled.Person))
bottomMenuItemsList.add(BottomMenuItem(label = "Cart", icon = Icons.Filled.ShoppingCart))
bottomMenuItemsList.add(BottomMenuItem(label = "Settings", icon = Icons.Filled.Settings))
return bottomMenuItemsList
}
data class BottomMenuItem(val label: String, val icon: ImageVector)
Output:

2. Bottom App Bar in Jetpack Compose:
It displays navigation and key actions at the bottom of the mobile screen.
Example:

Let’s implement it. Jetpack Compose has a prebuilt BottomAppBar API:
@Composable
fun BottomAppBar(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
cutoutShape: Shape? = null,
elevation: Dp = AppBarDefaults.BottomAppBarElevation,
contentPadding: PaddingValues = AppBarDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
)
modifier – The optional modifier.
backgroundColor – The background color. Use Color.Transparent to have no color.
contentColor – The color of the children.
cutoutShape – The shape of the cutout that will be added to the BottomAppBar – this should typically be the same shape used inside the FloatingActionButton, when it is used with BottomAppBar in the Scaffold layout. This shape will be drawn with an offset around all sides. If null, there will be no cutout.
elevation – The shadow behind the bottom bar.
contentPadding – The padding applied to the content.
content – The content of this BottomAppBar. The default layout here is a Row, so content inside will be placed horizontally.
First, create the data class as we have done before. For now, we just need two items.
private fun prepareBottomMenu(): List<BottomMenuItem> {
val bottomMenuItemsList = arrayListOf<BottomMenuItem>()
// add menu items
bottomMenuItemsList.add(BottomMenuItem(label = "Home", icon = Icons.Filled.Home))
bottomMenuItemsList.add(BottomMenuItem(label = "Profile", icon = Icons.Filled.Person))
return bottomMenuItemsList
}
Let’s create a separate composable for the bottom app bar.
@Composable
fun MyBottomBar() {
// items list
val bottomMenuItemsList = prepareBottomMenu()
val contextForToast = LocalContext.current.applicationContext
var selectedItem by remember {
mutableStateOf("Home")
}
BottomAppBar(
cutoutShape = CircleShape
) {
bottomMenuItemsList.forEachIndexed { index, menuItem ->
if (index == 1) {
// add an empty space for FAB
BottomNavigationItem(
selected = false,
onClick = {},
icon = {},
enabled = false
)
}
BottomNavigationItem(
selected = (selectedItem == menuItem.label),
onClick = {
selectedItem = menuItem.label
Toast.makeText(
contextForToast,
menuItem.label, Toast.LENGTH_SHORT
).show()
},
icon = {
Icon(
imageVector = menuItem.icon,
contentDescription = menuItem.label
)
},
enabled = true
)
}
}
}
Use the Scaffold layout so that the FAB will overlap with the bottom bar. Our final code looks like this:
@Composable
fun MyUI() {
val scaffoldState = rememberScaffoldState()
Scaffold(
modifier = Modifier.fillMaxSize(),
bottomBar = { MyBottomBar() },
floatingActionButton = {
FloatingActionButton(onClick = {
// FAB onClick
}) {
Icon(imageVector = Icons.Default.Add, contentDescription = "Add")
}
},
scaffoldState = scaffoldState,
isFloatingActionButtonDocked = true,
floatingActionButtonPosition = FabPosition.Center
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues = it),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// rest of the app's UI
}
}
}
@Composable
fun MyBottomBar() {
// items list
val bottomMenuItemsList = prepareBottomMenu()
val contextForToast = LocalContext.current.applicationContext
var selectedItem by remember {
mutableStateOf("Home")
}
BottomAppBar(
cutoutShape = CircleShape
) {
bottomMenuItemsList.forEachIndexed { index, menuItem ->
if (index == 1) {
// add an empty space for FAB
BottomNavigationItem(
selected = false,
onClick = {},
icon = {},
enabled = false
)
}
BottomNavigationItem(
selected = (selectedItem == menuItem.label),
onClick = {
selectedItem = menuItem.label
Toast.makeText(
contextForToast,
menuItem.label, Toast.LENGTH_SHORT
).show()
},
icon = {
Icon(
imageVector = menuItem.icon,
contentDescription = menuItem.label
)
},
enabled = true
)
}
}
}
private fun prepareBottomMenu(): List<BottomMenuItem> {
val bottomMenuItemsList = arrayListOf<BottomMenuItem>()
// add menu items
bottomMenuItemsList.add(BottomMenuItem(label = "Home", icon = Icons.Filled.Home))
bottomMenuItemsList.add(BottomMenuItem(label = "Profile", icon = Icons.Filled.Person))
return bottomMenuItemsList
}
data class BottomMenuItem(val label: String, val icon: ImageVector)
Run the app. You will see the following output:

This is about the Bottom Navigation and Bottom App Bar in Jetpack Compose. If you have any doubts, leave a comment below.
Related:
- TextField in Jetpack Compose (with Examples)
- How to Integrate AppLovin MAX Mediation in Your Android App?
- Activity and Service Communication in Android (5 Ways)
- How to Animate Content Changes in Jetpack Compose?
References: