Question #390MediumNative Android

How do you handle navigation with Jetpack Compose?

#jetpack-compose#navigation#android

Answer

Overview

Use Navigation Compose library for type-safe navigation.


Setup

kotlin
implementation("androidx.navigation:navigation-compose:2.7.0")

Basic Navigation

kotlin
@Composable
fun App() {
    val navController = rememberNavController()
    
    NavHost(navController, startDestination = "home") {
        composable("home") {
            HomeScreen(navController)
        }
        composable("details") {
            DetailsScreen(navController)
        }
    }
}

@Composable
fun HomeScreen(navController: NavController) {
    Button(onClick = { navController.navigate("details") }) {
        Text("Go to Details")
    }
}

@Composable
fun DetailsScreen(navController: NavController) {
    Button(onClick = { navController.popBackStack() }) {
        Text("Back")
    }
}

Passing Arguments

kotlin
NavHost(navController, startDestination = "home") {
    composable("home") {
        HomeScreen { userId ->
            navController.navigate("details/$userId")
        }
    }
    composable(
        route = "details/{userId}",
        arguments = listOf(
            navArgument("userId") { type = NavType.StringType }
        )
    ) { backStackEntry ->
        val userId = backStackEntry.arguments?.getString("userId")
        DetailsScreen(userId)
    }
}

Type-Safe Navigation (Recommended)

kotlin
@Serializable
sealed class Screen {
    @Serializable
    data object Home : Screen()
    
    @Serializable
    data class Details(val userId: String) : Screen()
}

NavHost(navController, startDestination = Screen.Home) {
    composable<Screen.Home> {
        HomeScreen {
            navController.navigate(Screen.Details("123"))
        }
    }
    composable<Screen.Details> { backStackEntry ->
        val details: Screen.Details = backStackEntry.toRoute()
        DetailsScreen(details.userId)
    }
}

Bottom Navigation

kotlin
@Composable
fun MainScreen() {
    val navController = rememberNavController()
    
    Scaffold(
        bottomBar = {
            BottomNavigation {
                BottomNavigationItem(
                    icon = { Icon(Icons.Default.Home, null) },
                    selected = currentRoute == "home",
                    onClick = { navController.navigate("home") }
                )
                BottomNavigationItem(
                    icon = { Icon(Icons.Default.Person, null) },
                    selected = currentRoute == "profile",
                    onClick = { navController.navigate("profile") }
                )
            }
        }
    ) {
        NavHost(navController, "home") {
            composable("home") { HomeScreen() }
            composable("profile") { ProfileScreen() }
        }
    }
}

Navigation Options

kotlin
// Pop up to destination
navController.navigate("details") {
    popUpTo("home") { inclusive = true }
}

// Single top
navController.navigate("details") {
    launchSingleTop = true
}

// Clear back stack
navController.navigate("login") {
    popUpTo(0) { inclusive = true }
}

Nested Navigation

kotlin
@Composable
fun AppNavigation() {
    NavHost(navController, "auth") {
        navigation(startDestination = "login", route = "auth") {
            composable("login") { LoginScreen() }
            composable("register") { RegisterScreen() }
        }
        navigation(startDestination = "home", route = "main") {
            composable("home") { HomeScreen() }
            composable("profile") { ProfileScreen() }
        }
    }
}

Best Practice: Use type-safe navigation with Kotlin Serialization for compile-time safety.