Android 入门教程
Compose 导航
本教程共 100 篇 · 第 52 篇 · 更新于 2026-07-28 · 约 9 分钟阅读
AndroidAndroid 入门教程Jetpack Compose导航NavHostNavigation Compose底部导航
52. Compose 导航
本节目标:学会用 Navigation Compose 构建多页面应用,掌握 NavHost 定义路由、navigate 跳转、传参、底部导航栏集成。
核心概念
Navigation Compose 有三个核心角色:
- NavController:导航控制器,管理页面切换和返回栈。
- NavHost:导航宿主,显示当前页面的容器。
- 路由(Route):页面的唯一标识,通常用字符串。
打个比方:NavController 是导航员,NavHost 是舞台,路由是剧本里的场景名。导航员按场景名切换舞台上的内容。
添加依赖
dependencies {
val navVersion = "2.8.5"
implementation("androidx.navigation:navigation-compose:$navVersion")
}
基本导航
定义路由和 NavHost:
@Composable
fun NavExample() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onNavigateToDetail = { navController.navigate("detail") }
)
}
composable("detail") {
DetailScreen(
onBack = { navController.popBackStack() }
)
}
}
}
rememberNavController()创建并记住控制器。NavHost里用composable("路由名")定义每个页面。navController.navigate("detail")跳转到对应路由。navController.popBackStack()回退一页。
Note
- 路由是字符串,但要保持简洁唯一。推荐用页面名,如
"home"、"detail"、"settings"。
导航传参
方式一:URL 参数
NavHost(navController, startDestination = "home") {
// 声明参数
composable(
"detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.IntType })
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getInt("itemId") ?: 0
DetailScreen(itemId = itemId)
}
}
// 跳转时传参
navController.navigate("detail/42")
方式二:查询参数(可选)
composable(
"search?query={query}",
arguments = listOf(navArgument("query") {
type = NavType.StringType
defaultValue = ""
nullable = true
})
) { entry ->
val query = entry.arguments?.getString("query") ?: ""
SearchScreen(query)
}
// 跳转
navController.navigate("search?query=kotlin")
Tip
- URL 参数(
detail/{itemId})适合必传的参数;查询参数(search?query=)适合可选参数,能设默认值。
类型安全路由(推荐)
手动拼字符串容易出错。Navigation 2.8+ 支持用 Kotlin Serialization 做类型安全路由:
// 先加 Kotlin Serialization 插件
@Serializable
object Home
@Serializable
data class Detail(val itemId: Int)
// NavHost 用对象做路由
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(
onItemClick = { id ->
navController.navigate(Detail(id))
}
)
}
composable<Detail> { backStackEntry ->
val args: Detail = backStackEntry.toRoute()
DetailScreen(itemId = args.itemId)
}
}
Note
- 类型安全路由用数据类替代字符串,编译期就能检查参数类型,不用手动拼 URL。推荐新项目用这种方式。需要项目启用 Kotlin Serialization 插件。
导航事件处理
防止重复跳转
navController.navigate("detail") {
// 避免快速连点重复入栈
launchSingleTop = true
}
跳转时清栈
navController.navigate("home") {
// 清掉到 home 之间的所有页面
popUpTo("home") { inclusive = false }
// 或清到登录页并包含它
popUpTo("login") { inclusive = true }
}
登录流程示例
// 登录成功后跳转到首页,清掉登录页
navController.navigate("home") {
popUpTo("login") { inclusive = true }
}
Warning
- 登录成功后一定要
popUpTo("login") { inclusive = true },否则用户按返回键会回到登录页。
底部导航栏
底部导航是移动应用最常见模式。结合 Scaffold 和 NavigationBar:
@Composable
fun MainScreen() {
val navController = rememberNavController()
val screens = listOf(
BottomScreen("home", "首页", Icons.Default.Home),
BottomScreen("search", "搜索", Icons.Default.Search),
BottomScreen("profile", "我的", Icons.Default.Person)
)
Scaffold(
bottomBar = {
val currentRoute = navController.currentBackStackEntryAsState()
.value?.destination?.route
NavigationBar {
screens.forEach { screen ->
NavigationBarItem(
selected = currentRoute == screen.route,
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = { Icon(screen.icon, contentDescription = screen.label) },
label = { Text(screen.label) }
)
}
}
}
) { padding ->
NavHost(
navController = navController,
startDestination = "home",
modifier = Modifier.padding(padding)
) {
composable("home") { HomeScreen() }
composable("search") { SearchScreen() }
composable("profile") { ProfileScreen() }
}
}
}
Tip
- 底部导航的
popUpTo+saveState+restoreState组合是固定写法:切换 Tab 时保留各 Tab 的状态(比如滚动位置),不会每次都重建。findStartDestination()找到起始页,清到它但不包含它。
嵌套导航图
把相关页面分组:
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen() }
// 嵌套图
navigation(startDestination = "list", route = "feature") {
composable("list") { ListScreen() }
composable("detail/{id}") { entry ->
DetailScreen(entry.arguments?.getString("id"))
}
}
}
// 跳转到嵌套图的起始页
navController.navigate("feature")
监听导航结果
页面间传回结果:
// 详情页设置结果
navController.previousBackStackEntry
?.savedStateHandle
?.set("result", "选中了项目3")
navController.popBackStack()
// 列表页监听结果
navController.currentBackStackEntry
?.savedStateHandle
?.getStateFlow<String>("result", "")
?.collectAsStateWithLifecycle()
小结
Navigation Compose 用 NavHost 定义路由、navigate 跳转、popBackStack 回退。推荐用类型安全路由替代字符串拼接。底部导航用 Scaffold + NavigationBar + saveState/restoreState 组合。登录成功记得清栈。下一节学习手势处理。