Navigation trong Jetpack Compose
1. Setup
Dependencies
implementation("androidx.navigation:navigation-compose:2.7.7")2. NavController và NavHost
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onNavigateToDetail = { id ->
navController.navigate("detail/$id")
}
)
}
composable("detail/{id}") { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")
DetailScreen(id = id)
}
}
}3. Navigate với Arguments
Path Arguments
NavHost(...) {
composable("detail/{userId}") { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId")
DetailScreen(userId)
}
}
// Navigate
navController.navigate("detail/123")Type-safe Arguments
composable(
route = "detail/{id}",
arguments = listOf(
navArgument("id") {
type = NavType.IntType
}
)
) { backStackEntry ->
val id = backStackEntry.arguments?.getInt("id") ?: 0
DetailScreen(id)
}Optional Arguments
composable(
route = "search?query={query}",
arguments = listOf(
navArgument("query") {
type = NavType.StringType
defaultValue = ""
nullable = true
}
)
) { backStackEntry ->
val query = backStackEntry.arguments?.getString("query")
SearchScreen(query)
}
// Navigate
navController.navigate("search?query=kotlin")4. Navigation Back Stack
// Navigate và pop tất cả đến destination
navController.navigate("home") {
popUpTo("home") { inclusive = true }
}
// Navigate và clear back stack
navController.navigate("login") {
popUpTo(0) { inclusive = true }
}
// Pop back
navController.popBackStack()
// Navigate up (respect parent)
navController.navigateUp()5. Bottom Navigation
@Composable
fun MainScreen() {
val navController = rememberNavController()
Scaffold(
bottomBar = {
NavigationBar {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
listOf(
BottomNavItem("home", "Home", Icons.Default.Home),
BottomNavItem("search", "Search", Icons.Default.Search),
BottomNavItem("profile", "Profile", Icons.Default.Person)
).forEach { item ->
NavigationBarItem(
icon = { Icon(item.icon, item.label) },
label = { Text(item.label) },
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.startDestinationId) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
) { padding ->
NavHost(
navController,
startDestination = "home",
modifier = Modifier.padding(padding)
) {
composable("home") { HomeScreen() }
composable("search") { SearchScreen() }
composable("profile") { ProfileScreen() }
}
}
}6. Nested Navigation
NavHost(navController, startDestination = "main") {
// Main graph
navigation(startDestination = "home", route = "main") {
composable("home") { HomeScreen() }
composable("detail/{id}") { DetailScreen() }
}
// Auth graph
navigation(startDestination = "login", route = "auth") {
composable("login") { LoginScreen() }
composable("register") { RegisterScreen() }
}
}7. Deep Links
composable(
route = "detail/{id}",
deepLinks = listOf(
navDeepLink {
uriPattern = "myapp://detail/{id}"
},
navDeepLink {
uriPattern = "https://myapp.com/detail/{id}"
}
)
) { backStackEntry ->
DetailScreen(backStackEntry.arguments?.getString("id"))
}8. Passing Data Back
// Screen A: Set result listener
val result = navController.currentBackStackEntry
?.savedStateHandle
?.getLiveData<String>("key")
// Screen B: Return result
navController.previousBackStackEntry
?.savedStateHandle
?.set("key", "result value")
navController.popBackStack()9. Animation
composable(
route = "detail",
enterTransition = {
slideInHorizontally(initialOffsetX = { 1000 })
},
exitTransition = {
slideOutHorizontally(targetOffsetX = { -1000 })
},
popEnterTransition = {
slideInHorizontally(initialOffsetX = { -1000 })
},
popExitTransition = {
slideOutHorizontally(targetOffsetX = { 1000 })
}
) {
DetailScreen()
}10. Type-Safe Navigation (Kotlin Serialization)
@Serializable
data class Detail(val id: Int)
@Serializable
object Home
NavHost(navController, startDestination = Home) {
composable<Home> { HomeScreen() }
composable<Detail> { backStackEntry ->
val detail: Detail = backStackEntry.toRoute()
DetailScreen(detail.id)
}
}
// Navigate
navController.navigate(Detail(id = 123))📝 Tóm tắt
| Concept | Mô tả |
|---|---|
| NavController | Điều khiển navigation |
| NavHost | Container cho destinations |
| composable | Định nghĩa destination |
| navArgument | Truyền arguments |
| popUpTo | Pop back stack |
| Deep Links | External URLs |
| Nested Navigation | Graphs lồng nhau |
Last updated on