Skip to content

Latest commit

 

History

History
155 lines (122 loc) · 4.8 KB

06-navigation-in-compose.md

File metadata and controls

155 lines (122 loc) · 4.8 KB

Navigation in Compose

NavHost and Destinations

The Navigation Compose library manages screens and the back stack. You define routes in a NavHost, and each route maps to a composable.

// build.gradle.kts
// implementation("androidx.navigation:navigation-compose:2.9.0")

import androidx.navigation.compose.*

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

    NavHost(navController = navController, startDestination = "home") {
        composable("home") { HomeScreen(navController) }
        composable("detail") { DetailScreen(navController) }
        composable("settings") { SettingsScreen(navController) }
    }
}
  • rememberNavController() — holds and controls the back stack
  • NavHost — the container that shows the current destination
  • composable("home") — registers a route

Navigate Between Screens

Call navController.navigate("route") to move forward. Call navController.popBackStack() to go back.

@Composable
fun HomeScreen(navController: NavController) {
    Column {
        Text("Home")
        Button(onClick = { navController.navigate("detail") }) {
            Text("Go to Detail")
        }
    }
}

Pass Arguments

Add a placeholder to the route, then declare its type:

NavHost(navController = navController, startDestination = "home") {
    composable("home") { HomeScreen(navController) }
    composable(
        route = "detail/{itemId}",
        arguments = listOf(navArgument("itemId") { type = NavType.IntType })
    ) { backStackEntry ->
        val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
        DetailScreen(itemId = itemId, navController = navController)
    }
}

Navigate with the value in the path:

navController.navigate("detail/42")   // itemId becomes 42

Bottom Navigation Bar

Combine Scaffold with a NavigationBar to switch between top-level destinations.

Diagram: A three-tab app with Home, Search, and Profile.

flowchart TD
    App["App Scaffold"] --> NavHost
    App --> Bar["NavigationBar"]
    Bar --> Home["Home"]
    Bar --> Search["Search"]
    Bar --> Profile["Profile"]
Loading

Step 1 — define the tabs:

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*

data class TabItem(val route: String, val label: String, val icon: androidx.compose.ui.graphics.vector.ImageVector)

val tabs = listOf(
    TabItem("home", "Home", Icons.Filled.Home),
    TabItem("search", "Search", Icons.Filled.Search),
    TabItem("profile", "Profile", Icons.Filled.Person),
)

Step 2 — wire up Scaffold and NavHost:

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    Scaffold(
        bottomBar = {
            val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
            NavigationBar {
                tabs.forEach { tab ->
                    NavigationBarItem(
                        selected = currentRoute == tab.route,
                        onClick = {
                            navController.navigate(tab.route) {
                                popUpTo("home") { saveState = true }
                                launchSingleTop = true
                                restoreState = true
                            }
                        },
                        icon = { Icon(tab.icon, contentDescription = tab.label) },
                        label = { Text(tab.label) }
                    )
                }
            }
        }
    ) { padding ->
        NavHost(navController, startDestination = "home", Modifier.padding(padding)) {
            composable("home") { Text("Home", Modifier.padding(16.dp)) }
            composable("search") { Text("Search", Modifier.padding(16.dp)) }
            composable("profile") { Text("Profile", Modifier.padding(16.dp)) }
        }
    }
}

The navigation options keep state when switching tabs and avoid stacking duplicate screens:

  • popUpTo("home") { saveState = true } — pop back to the start, save state
  • launchSingleTop = true — do not create the same screen twice
  • restoreState = true — restore the tab's previous state

Nested Navigation

Group related routes into a graph. This is useful for a login flow or a checkout flow that you want to clear together:

navigation(startDestination = "login", route = "auth") {
    composable("login") { LoginScreen(navController) }
    composable("register") { RegisterScreen(navController) }
}

Navigate to the whole graph with navController.navigate("auth").

Practice Exercise

Build a 3-screen app: Home, Detail (takes an Int argument), and Settings. Add a bottom bar with two tabs. From Home, a button navigates to Detail with id 7. The Detail screen shows "Item #7" and has a Back button.