CPS251 Android Development by Scott Shaper

Navigation UI Components

Introduction to Navigation UI

Navigation UI components are the visual elements that help users move around your app. Think of them like signs and doors in a building - they show users where they can go and how to get there. In this lesson, we'll learn about two common navigation UI components:

Component Where It Goes What It's For When to Use
Top App Bar Top of the screen Show app title and important actions App-wide actions, context, or navigation. Note the or navigation part.
Bottom Navigation Bar Bottom of the screen Switch between main app sections 3-5 top-level destinations

What is Scaffold?

Scaffold is a layout in Jetpack Compose that gives your screen a standard structure: a place for a top bar, an optional bottom bar, and a large area in the middle for your main content. You don’t have to figure out where each part goes—Scaffold does that. You pass in the top bar (and bottom bar if you have one), and you put your main content in a block (a lambda). Scaffold also passes paddingValues into that block so you can add padding to your content and keep it from drawing underneath the bars.

In the example below, MainScreen uses Scaffold to show a top navigation bar and the main content. The main content is everything you put inside the content block. Here, that’s the NavHost. The NavHost is what actually shows your screens—HomeScreen, ProfileScreen, and SettingsScreen—depending on the current route. So in this code, the “main content” is the NavHost and the screen that’s currently visible inside it. Scaffold’s job is to put the top bar above that content and to give you paddingValues so the content doesn’t sit under the bar.

Here’s the same idea in terms of the code:

@Composable
fun MainScreen() {
    val navController = rememberNavController()

    Scaffold(
        topBar = { TopNavigationBar(navController) }
    ) { paddingValues ->
        NavHost(
            navController = navController,
            startDestination = NavigationRoutes.HOME,
            modifier = Modifier.padding(paddingValues)
        ) {
            composable(NavigationRoutes.HOME) { HomeScreen() }
            composable(NavigationRoutes.PROFILE) { ProfileScreen() }
            composable(NavigationRoutes.SETTINGS) { SettingsScreen() }
        }
    }
}

So: the main content is the NavHost and the screens it displays (HomeScreen, ProfileScreen, SettingsScreen). Scaffold places the top bar above that content and gives you paddingValues so the content stays visible below the bar.

Top App Bar

The Top App Bar (also called Action Bar) is like a header that stays at the top of your screen. It's great for showing the current screen title and navigation actions.

// NavigationRoutes.kt
    object NavigationRoutes {
        const val HOME = "home"
        const val PROFILE = "profile"
        const val SETTINGS = "settings"
    }
    
    // TopNavigation.kt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TopNavigationBar(navController: NavController) {
    val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route

    TopAppBar(
        title = { Text("Top Navigation Bar") },
        colors = TopAppBarDefaults.topAppBarColors(
            containerColor = MaterialTheme.colorScheme.primaryContainer,
            titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer
        ),
        actions = {
            IconButton(onClick = {
                navController.navigate(NavigationRoutes.HOME) { launchSingleTop = true }
            }) {
                Icon(
                    imageVector = Icons.Filled.Home,
                    contentDescription = "Home",
                    tint = if (currentRoute == NavigationRoutes.HOME) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onPrimaryContainer
                )
            }
            IconButton(onClick = {
                navController.navigate(NavigationRoutes.PROFILE) { launchSingleTop = true }
            }) {
                Icon(
                    imageVector = Icons.Filled.Person,
                    contentDescription = "Profile",
                    tint = if (currentRoute == NavigationRoutes.PROFILE) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onPrimaryContainer
                )
            }
            IconButton(onClick = {
                navController.navigate(NavigationRoutes.SETTINGS) { launchSingleTop = true }
            }) {
                Icon(
                    imageVector = Icons.Filled.Settings,
                    contentDescription = "Settings",
                    tint = if (currentRoute == NavigationRoutes.SETTINGS) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onPrimaryContainer
                )
            }
        }
    )
}
    
    // MainScreen.kt
    @Composable
    fun MainScreen() {
        val navController = rememberNavController()
    
        Scaffold(
            topBar = { TopNavigationBar(navController) }
        ) { paddingValues ->
            NavHost(
                navController = navController,
                startDestination = NavigationRoutes.HOME,
                modifier = Modifier.padding(paddingValues)
            ) {
                composable(NavigationRoutes.HOME) { HomeScreen() }
                composable(NavigationRoutes.PROFILE) { ProfileScreen() }
                composable(NavigationRoutes.SETTINGS) { SettingsScreen() }
            }
        }
    }

How the Top App Bar works:

  • The top app bar is like a header at the top of your screen that shows your app's name and navigation buttons
  • It's different from the bottom navigation bar because it's meant for app-level actions and showing where you are in the app
  • In the code, we use topBar in the Scaffold to put it at the top of the screen
  • It can show a back button when you're not on the home screen
  • While it can have navigation buttons, its main job is to show your app's title and important actions

How this example renders

Above is just a snippet of the code to view the full code, you need to go to my GitHub page and look at the chapter9 topNavBar.kt file.

Top Navigation Bar Example

Bottom Navigation Bar

The bottom navigation bar is perfect for switching between the main sections of your app. It's like having a row of buttons at the bottom of the screen that take you to different places.

/ NavigationRoutes.kt
    object NavigationRoutes {
        const val HOME = "home"
        const val PROFILE = "profile"
        const val SETTINGS = "settings"
    }
    
    // BottomNavigation.kt
    @Composable
    fun BottomNavigationBar(navController: NavController) {
        val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
    
        NavigationBar {
            NavigationBarItem(
                icon = { Icon(Icons.Filled.Home, contentDescription = "Home") },
                label = { Text("Home") },
                selected = currentRoute == NavigationRoutes.HOME,
                onClick = {
                    navController.navigate(NavigationRoutes.HOME) {
                        launchSingleTop = true
                    }
                }
            )
            NavigationBarItem(
                icon = { Icon(Icons.Filled.Person, contentDescription = "Profile") },
                label = { Text("Profile") },
                selected = currentRoute == NavigationRoutes.PROFILE,
                onClick = {
                    navController.navigate(NavigationRoutes.PROFILE) {
                        launchSingleTop = true
                    }
                }
            )
            NavigationBarItem(
                icon = { Icon(Icons.Filled.Settings, contentDescription = "Settings") },
                label = { Text("Settings") },
                selected = currentRoute == NavigationRoutes.SETTINGS,
                onClick = {
                    navController.navigate(NavigationRoutes.SETTINGS) {
                        launchSingleTop = true
                    }
                }
            )
        }
    }
    
    // MainScreen.kt
    @Composable
    fun MainScreen() {
        val navController = rememberNavController()
    
        Scaffold(
            bottomBar = { BottomNavigationBar(navController) }
        ) { paddingValues ->
            NavHost(
                navController = navController,
                startDestination = NavigationRoutes.HOME,
                modifier = Modifier.padding(paddingValues)
            ) {
                composable(NavigationRoutes.HOME) { HomeScreen() }
                composable(NavigationRoutes.PROFILE) { ProfileScreen() }
                composable(NavigationRoutes.SETTINGS) { SettingsScreen() }
            }
        }
    }

How Bottom Navigation Works:

How this example renders

Above is just a snippet of the code to view the full code, you need to go to my GitHub page and look at the chapter9 bottomNavBar.kt file.

Bottom Navigation Bar Example

Bottom Navigation vs Top App Bar: What's the Difference?

  • Where they go:
    • Bottom Navigation Bar goes at the bottom of your screen
    • Top App Bar goes at the top of your screen
  • What they're for:
    • Bottom Navigation Bar is for switching between the main parts of your app (like Home, Profile, Settings)
    • Top App Bar is for showing your app's name and important actions at the top
  • When to use each:
    • Use Bottom Navigation when you want users to easily switch between main sections of your app
    • Use Top App Bar when you want to show the app name and important actions at the top of the screen

Where Do the Icons Come From?

In our examples, we use icons from the Material Icons library. These are built into Android and easy to use. Here's how we get them:

// Import the icons we want to use
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Settings

// Then use them in our code
Icon(Icons.Filled.Home, contentDescription = "Home")
Icon(Icons.Filled.Person, contentDescription = "Profile")
Icon(Icons.Filled.Settings, contentDescription = "Settings")

Some things to know about Material Icons:

  • They're free to use and come with Android
  • There are many icons to choose from (like Home, Settings, Person, etc.)
  • You can find all available icons in Android Studio by typing Icons.Filled. and looking at the suggestions
  • Always include a contentDescription for accessibility

Tips for Success

  • Choose the right navigation component for your app's needs
  • Keep navigation consistent throughout your app
  • Use clear icons and labels
  • Test navigation on different screen sizes

Common Mistakes to Avoid

  • Using too many navigation components at once
  • Unclear icons or labels
  • Inconsistent navigation patterns
  • Not handling back navigation properly