Jetpack Compose: How to use bottom navigation

Introduction

Jetpack Compose is a modern UI toolkit for building native Android applications. One of its most exciting features is the ability to create and manage UI components declaratively, which makes designing UIs faster and more intuitive. In this article, we'll explore how to implement bottom navigation using Jetpack Compose in Android Kotlin, focusing on a simple example that demonstrates the interaction between a scaffold, bottom navigation, and floating action buttons.

Bottom navigation is a key component in mobile applications, providing easy access to core functionality in a user-friendly manner. Using Jetpack Compose, we can achieve a highly customizable and responsive bottom navigation layout, which can significantly enhance the overall user experience.

Breakdown of the Code: 1. Main Activity Setup

The entry point of the app is the MainActivity, which extends AppCompatActivity. Within the onCreate function, the content of the activity is set using setContent, a Jetpack Compose-specific method that takes a composable function as its parameter. In this case, the composable function GetScaffold is invoked, which sets up the primary structure of the UI, including a top app bar, main content, a bottom navigation bar, and a floating action button.

kotlin

setContent { GetScaffold() }

This method simplifies the process of creating the UI, as it eliminates the need for XML layouts and traditional view hierarchies.

2. The Scaffold Structure

At the core of this example is the Scaffold composable, which acts as a layout container for the top bar, bottom navigation, main content, and a floating action button. The scaffold provides a structure where different sections of the UI can be neatly arranged. In this example, it consists of a TopAppBar for displaying a title, a FloatingActionButton, and a BottomAppBar for navigation.

kotlin

Scaffold( topBar = { TopAppBar(title = { Text("Compose - Bottom Navigation") }) }, content = { MainContent(selectedItem) }, bottomBar = { BottomBarContent(selectedItem) }, floatingActionButton = { ... }, isFloatingActionButtonDocked = true )

The TopAppBar sets a static title for the app, while the BottomAppBar contains navigation items that allow users to switch between different states.

3. Bottom Navigation

The BottomBarContent composable function is responsible for creating the bottom navigation bar. Inside this function, the BottomAppBar houses three BottomNavigationItem components, each representing a different action: "Add," "ThumbUp," and "Delete."

kotlin

BottomNavigationItem( icon = { Icon(Icons.Filled.AddCircle, "") }, selected = selectedItem.value == "Add", onClick = { selectedItem.value = "Add" }, label = { Text("Add") } )

Each navigation item is visually represented by an icon and text label, and the current selection is tracked using selectedItem, which is a MutableState variable. When an item is clicked, it updates the selectedItem value, which is then displayed in the main content area.

4. Floating Action Button

The FloatingActionButton (FAB) adds another layer of interaction to the UI. In this example, the FAB is styled with a custom shape (CutCornerShape), which gives it a distinctive angular design. The icon inside the FAB is a heart (favorite) symbol, making it a prominent element of the screen.

kotlin

FloatingActionButton( onClick = { /* action */ }, shape = CutCornerShape(45.dp) ) { Icon(Icons.Filled.Favorite, contentDescription = "") }

The FAB is docked into the BottomAppBar using the isFloatingActionButtonDocked parameter, ensuring that it stays aligned with the navigation bar, enhancing the layout's visual appeal.

5. Main Content

The MainContent composable function dynamically displays the current selection made through the bottom navigation. It uses a simple Box layout, which centers the text on the screen, making it responsive to screen size changes.

kotlin

Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Text("Selected ${selectedItem.value}", fontWeight = FontWeight.Bold) }

The text updates in real-time based on the selected item from the bottom navigation, demonstrating how Compose's reactive system efficiently updates the UI when state changes.

Summary

This example illustrates the power and simplicity of Jetpack Compose when building an Android application with a bottom navigation layout. By using composables like Scaffold, BottomAppBar, and FloatingActionButton, we can create a clean, responsive interface with minimal code.

Jetpack Compose’s declarative approach allows for seamless UI updates and easy management of state, making it an excellent choice for building modern Android UIs. As demonstrated, the code is easy to read and maintain, providing developers with flexibility and control over their app's design and behavior.


MainActivity.kt

package com.cfsuman.jetpackcompose

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddCircle
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.ThumbUp
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            GetScaffold()
        }
    }


    @Composable
    fun GetScaffold(){
        val selectedItem = remember { mutableStateOf("Add")}

        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text(
                        text = "Compose - Bottom Navigation"
                    )},
                    backgroundColor = Color(0xFFC0E8D5),
                )
            },
            content = {MainContent(selectedItem)},
            backgroundColor = Color(0xFFEDEAE0),
            bottomBar = { BottomBarContent(selectedItem) },
            floatingActionButton = {
                FloatingActionButton(
                    onClick = {
                        // do something here
                    },
                    shape = CutCornerShape(45.dp)
                ) {
                    Icon(Icons.Filled.Favorite, contentDescription = "")
                }
            },
            isFloatingActionButtonDocked = true
        )
    }


    @Composable
    fun BottomBarContent(selectedItem: MutableState<String>){
        BottomAppBar(
            elevation = 4.dp,
            cutoutShape = CutCornerShape(45.dp)
        ) {
            BottomNavigation() {
                BottomNavigationItem(
                    icon = {Icon(Icons.Filled.AddCircle,"")},
                    selected = selectedItem.value == "Add",
                    onClick = { selectedItem.value = "Add" },
                    label = {Text(text = "Add")},
                    alwaysShowLabel = false
                )

                BottomNavigationItem(
                    icon = {Icon(Icons.Filled.ThumbUp,"")},
                    selected = selectedItem.value == "ThumbUp",
                    onClick = { selectedItem.value = "ThumbUp" },
                    label = {Text(text = "ThumbUp")},
                    alwaysShowLabel = false
                )

                BottomNavigationItem(
                    icon = {Icon(Icons.Filled.Delete,"")},
                    selected = selectedItem.value == "Delete",
                    onClick = { selectedItem.value = "Delete" },
                    label = {Text(text = "Delete")},
                    alwaysShowLabel = false
                )
            }
        }
    }


    @Composable
    fun MainContent(selectedItem: MutableState<String>){
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ){
            Text(
                text = "Selected ${selectedItem.value}",
                fontWeight = FontWeight.Bold
            )
        }
    }
}
More android jetpack compose tutorials