Jetpack Compose Bottom Navigation (with Examples)

jetpack compose bottom navigation

Jetpack Compose provides 2 types of bottom navigation APIs:

  1. Bottom Navigation
  2. Bottom App Bar

In this article, we will learn how to implement them with the help of examples.

Prerequisites:

Bottom Navigation:

It is used to display the destinations at the bottom of the screen. Each destination is represented by an icon and a label.

Example:

Bottom Navigation Icons

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.

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:

Bottom navigation example

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 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(
            // place it at the bottom of the screen
            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:

Bottom Navigation Icons

Bottom App Bar:

It displays navigation and key actions at the bottom of the mobile screen.

Example:

Bottom App Bar

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.

cutoutShapeThe 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.

@Composable
fun MyUI() {

    val scaffoldState = rememberScaffoldState()

    Scaffold(
        bottomBar = { MyBottomBar() },
        floatingActionButton = {
            FloatingActionButton(onClick = {
                // FAB onClick
            }) {
                Icon(imageVector = Icons.Default.Add, contentDescription = "Add")
            }
        },
        scaffoldState = scaffoldState,
        isFloatingActionButtonDocked = true,
        floatingActionButtonPosition = FabPosition.Center
    ) {

    }
}

Run the app. You will see the following output:

Bottom App Bar

This is about the Bottom Navigation and Bottom App Bar in Jetpack Compose. If you have any doubts, leave a comment below.

Continue Exploring Jetpack Compose:

References:

Leave a Comment