
In this article, we’ll learn how to implement navigation bar APIs in Material 3 Jetpack Compose.
Prerequisites:
Jetpack Compose provides two navigation bar APIs:
Let’s look at each one.
First, create an empty Compose project. Open the app-level gradle file and add the following dependency. We need it for the icons.
// this is the material icon dependency
// its size is large, so enable proguard in the production
implementation "androidx.compose.material:material-icons-extended"
Next, open the MainActivity.kt and create a composable called MyUI(). Call it from the onCreate().
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Image
import androidx.compose.material.icons.filled.Mic
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.ShoppingCart
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.BottomAppBarDefaults
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
YourProjectNameTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
modifier = Modifier.fillMaxSize()
) {
MyUI()
}
}
}
}
}
}
@Composable
fun MyUI() {
}
1. Navigation Bar:
The navigation bar is used to display the destination screens at the bottom of the screen. Each destination is represented by an icon and a label. It should only contain three to five destinations.
Example:

The API looks like this:
@Composable
fun NavigationBar(
modifier: Modifier = Modifier,
containerColor: Color = NavigationBarDefaults.containerColor,
contentColor: Color = MaterialTheme.colorScheme.contentColorFor(containerColor),
tonalElevation: Dp = NavigationBarDefaults.Elevation,
windowInsets: WindowInsets = NavigationBarDefaults.windowInsets,
content: @Composable RowScope.() -> Unit
)
modifier – The Modifier to be applied to this navigation bar.
containerColor – The background color. Use Color.Transparent to have no color.
contentColor – The preferred color for content.
tonalElevation – Elevation of the navigation bar.
windowInsets – A window insets of the navigation bar.
content – The content of this navigation bar.
In the content block, we add destinations (screens) using the NavigationBarItem(). Let’s look at it:
@Composable
fun RowScope.NavigationBarItem(
selected: Boolean,
onClick: () -> Unit,
icon: @Composable () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
label: @Composable (() -> Unit)? = null,
alwaysShowLabel: Boolean = true,
colors: NavigationBarItemColors = NavigationBarItemDefaults.colors(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
)
selected – Whether this item is selected.
onClick – Called when this item is clicked.
icon – Icon for this item, typically an Icon().
modifier – The Modifier to be applied to this item.
enabled – Controls the enabled state of this item. When false, this component will not respond to user input, and it will appear visually disabled and disabled to accessibility services.
label – Optional text label.
alwaysShowLabel – Whether to always show the label. If false, the label will only be shown when this item is selected.
colors – Colors of the item.
interactionSource – We can observe and customize the interactions. For example, we can disable the ripple effect.
Let’s see how to implement a simple navigation bar. First, create a data class for storing the item’s icon and text label.
data class NavigationBarData(val label: String, val icon: ImageVector)
Next, create a method that prepares the items (destinations) list.
private fun prepareNavigationBarItems(): List<NavigationBarData> {
val navigationBarItemsList = arrayListOf<NavigationBarData>()
// add items
navigationBarItemsList.add(NavigationBarData(label = "Home", icon = Icons.Filled.Home))
navigationBarItemsList.add(NavigationBarData(label = "Profile", icon = Icons.Filled.Person))
navigationBarItemsList.add(NavigationBarData(label = "Cart", icon = Icons.Filled.ShoppingCart))
navigationBarItemsList.add(NavigationBarData(label = "Settings", icon = Icons.Filled.Settings))
return navigationBarItemsList
}
Next, in the MyUI(), display the navigation bar.
@Composable
fun MyUI() {
// items list
val navigationBarItemList = prepareNavigationBarItems()
val contextForToast = LocalContext.current.applicationContext
var selectedItemIndex by remember {
mutableIntStateOf(0) // or use mutableStateOf(0)
}
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.padding(bottom = 76.dp) // add bottom margin for the navigation bar
.fillMaxSize()
) {
// add the app UI here
Text(
text = "Rest of the App UI"
)
}
NavigationBar(
modifier = Modifier.align(alignment = Alignment.BottomCenter) // place it at the bottom of the screen
) {
navigationBarItemList.forEachIndexed { index, item ->
NavigationBarItem(
icon = { Icon(imageVector = item.icon, contentDescription = null) },
label = { Text(text = item.label) },
selected = selectedItemIndex == index,
onClick = {
selectedItemIndex = index
Toast.makeText(
contextForToast,
navigationBarItemList[selectedItemIndex].label,
Toast.LENGTH_SHORT
).show()
}
)
}
}
}
}
We used a Box layout to position the navigation bar at the bottom of the screen using the align modifier. For the rest of the app UI, we used a Column layout. We added a margin of 76.dp to the Column to ensure that the content of the app is not hidden behind the navigation bar.
Here is the complete code:
@Composable
fun MyUI() {
// items list
val navigationBarItemList = prepareNavigationBarItems()
val contextForToast = LocalContext.current.applicationContext
var selectedItemIndex by remember {
mutableIntStateOf(0) // or use mutableStateOf(0)
}
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.padding(bottom = 76.dp) // add bottom margin for the navigation bar
.fillMaxSize()
) {
// add the app UI here
Text(
text = "Rest of the App UI"
)
}
NavigationBar(
modifier = Modifier.align(alignment = Alignment.BottomCenter) // place it at the bottom of the screen
) {
navigationBarItemList.forEachIndexed { index, item ->
NavigationBarItem(
icon = { Icon(imageVector = item.icon, contentDescription = null) },
label = { Text(text = item.label) },
selected = selectedItemIndex == index,
onClick = {
selectedItemIndex = index
Toast.makeText(
contextForToast,
navigationBarItemList[selectedItemIndex].label,
Toast.LENGTH_SHORT
).show()
}
)
}
}
}
}
private fun prepareNavigationBarItems(): List<NavigationBarData> {
val navigationBarItemsList = arrayListOf<NavigationBarData>()
// add items
navigationBarItemsList.add(NavigationBarData(label = "Home", icon = Icons.Filled.Home))
navigationBarItemsList.add(NavigationBarData(label = "Profile", icon = Icons.Filled.Person))
navigationBarItemsList.add(NavigationBarData(label = "Cart", icon = Icons.Filled.ShoppingCart))
navigationBarItemsList.add(NavigationBarData(label = "Settings", icon = Icons.Filled.Settings))
return navigationBarItemsList
}
data class NavigationBarData(val label: String, val icon: ImageVector)
Output:

Google has some recommendations for displaying the number of items inside the NavigationBar:
Three to four destinations: Display icons and text labels for all destinations.
Five destinations: Active destinations display an icon and text label. Inactive destinations use icons and use text labels if space permits.
A NavigationBarItem always shows text labels (if it exists) when selected. The visibility of text labels, when the item is not selected, is controlled by the alwaysShowLabel.
2. Bottom App Bar:
It displays navigation and key actions at the bottom of mobile screens.
Example:

Jetpack Compose provides BottomAppBar() method:
@Composable
fun BottomAppBar(
actions: @Composable RowScope.() -> Unit,
modifier: Modifier = Modifier,
floatingActionButton: @Composable (() -> Unit)? = null,
containerColor: Color = BottomAppBarDefaults.containerColor,
contentColor: Color = contentColorFor(containerColor),
tonalElevation: Dp = BottomAppBarDefaults.ContainerElevation,
contentPadding: PaddingValues = BottomAppBarDefaults.ContentPadding,
windowInsets: WindowInsets = BottomAppBarDefaults.windowInsets,
)
actions – The icons in the BottomAppBar. The default layout here is a Row, so all the icons will be placed horizontally.
modifier – The Modifier to be applied to this bottom app bar.
floatingActionButton – An optional floating action button at the end of the app bar.
containerColor – The background color. Use Color.Transparent to have no color.
contentColor – The preferred color for content.
tonalElevation – Elevation of the app bar.
contentPadding – The padding applied to the content.
windowInsets – A window insets that the app bar will respect.
Similar to the navigation bar, create a data class and a method to prepare the items list.
private fun prepareBottomAppBarItems(): List<BottomAppBarData> {
val bottomAppBarItemsList = arrayListOf<BottomAppBarData>()
// add menu items
bottomAppBarItemsList.add(BottomAppBarData(label = "Images", icon = Icons.Filled.Image))
bottomAppBarItemsList.add(BottomAppBarData(label = "Edit", icon = Icons.Filled.Edit))
bottomAppBarItemsList.add(BottomAppBarData(label = "Voice Record", icon = Icons.Filled.Mic))
return bottomAppBarItemsList
}
data class BottomAppBarData(val label: String, val icon: ImageVector)
Next, create a separate composable for the BottomAppBar.
@Composable
fun MyBottomAppBar() {
val bottomAppBarItemsList = prepareBottomAppBarItems()
val contextForToast = LocalContext.current.applicationContext
BottomAppBar(
actions = {
bottomAppBarItemsList.forEach { item ->
IconButton(
onClick = {
Toast.makeText(contextForToast, item.label, Toast.LENGTH_SHORT).show()
}
) {
Icon(imageVector = item.icon, contentDescription = item.label)
}
}
},
floatingActionButton = {
FloatingActionButton(
onClick = { Toast.makeText(contextForToast, "FAB", Toast.LENGTH_SHORT).show() },
containerColor = BottomAppBarDefaults.bottomAppBarFabColor,
elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation()
) {
Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
}
}
)
}
We added the icons using the IconButton() composable. Each icon is placed horizontally from the left side. The floating action button is placed on the right side.
Generally, we use the Scaffold layout to display the app bar. Let’s add the code inside the MyUI().
@Composable
fun MyUI() {
Scaffold(
modifier = Modifier.fillMaxSize(),
bottomBar = { MyBottomAppBar() }
) { paddingValues ->
// add rest of the app UI here
Column(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize()
) {
Text(text = "Rest of the app UI")
}
}
}
Here is the complete code:
@Composable
fun MyUI() {
Scaffold(
modifier = Modifier.fillMaxSize(),
bottomBar = { MyBottomAppBar() }
) { paddingValues ->
// add rest of the app UI here
Column(
modifier = Modifier
.padding(paddingValues)
.fillMaxSize()
) {
Text(text = "Rest of the app UI")
}
}
}
@Composable
fun MyBottomAppBar() {
val bottomAppBarItemsList = prepareBottomAppBarItems()
val contextForToast = LocalContext.current.applicationContext
BottomAppBar(
actions = {
bottomAppBarItemsList.forEach { item ->
IconButton(
onClick = {
Toast.makeText(contextForToast, item.label, Toast.LENGTH_SHORT).show()
}
) {
Icon(imageVector = item.icon, contentDescription = item.label)
}
}
},
floatingActionButton = {
FloatingActionButton(
onClick = { Toast.makeText(contextForToast, "FAB", Toast.LENGTH_SHORT).show() },
containerColor = BottomAppBarDefaults.bottomAppBarFabColor,
elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation()
) {
Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
}
}
)
}
private fun prepareBottomAppBarItems(): List<BottomAppBarData> {
val bottomAppBarItemsList = arrayListOf<BottomAppBarData>()
// add menu items
bottomAppBarItemsList.add(BottomAppBarData(label = "Images", icon = Icons.Filled.Image))
bottomAppBarItemsList.add(BottomAppBarData(label = "Edit", icon = Icons.Filled.Edit))
bottomAppBarItemsList.add(BottomAppBarData(label = "Voice Record", icon = Icons.Filled.Mic))
return bottomAppBarItemsList
}
data class BottomAppBarData(val label: String, val icon: ImageVector)
Output:

This is all about the Material 3 Navigation Bar APIs in Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.
Related Articles:
References: