一、依赖引入
Gradle (Module level build.gradle.kts)
kotlin
scss
dependencies {
// Compose Navigation
implementation("androidx.navigation:navigation-compose:2.7.7")
}
二、核心概念
- NavController:导航控制器,负责跳转、回退、获取当前路由
- NavHost:导航容器,承载所有页面,绑定 NavController
- route:路由字符串,唯一标识页面
- composable:页面路由条目,对应一个 Compose 页面
- 导航参数:支持必传参数、可选参数、类型转换
三、基础最简使用
1. 创建 NavController
kotlin
ini
val navController = rememberNavController()
2. NavHost 基础结构
kotlin
scss
NavHost(
navController = navController,
startDestination = "home" // 初始页面路由
) {
// 首页
composable(route = "home") {
HomeScreen(navController)
}
// 详情页
composable(route = "detail") {
DetailScreen(navController)
}
}
3. 页面内跳转
kotlin
arduino
// 跳转到详情页
navController.navigate("detail")
// 返回上一页
navController.popBackStack()
// 返回指定路由(清除中间所有页面)
navController.popBackStack("home", inclusive = false)
四、带参数导航(最常用)
方式 1:路径拼接传参
1. 定义带参路由
kotlin
ini
// {id} 占位符,必填参数
composable(route = "detail/{id}") { backStackEntry ->
// 取出参数
val id = backStackEntry.arguments?.getString("id") ?: "0"
DetailScreen(id = id)
}
2. 跳转时传参
kotlin
arduino
navController.navigate("detail/1001")
方式 2:可选参数(query 参数)
kotlin
ini
// ?name={name} 可选参数
composable(route = "detail/{id}?name={name}") { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")
val name = backStackEntry.arguments?.getString("name")
}
// 跳转
navController.navigate("detail/1001?name=test")
方式 3:类型安全路由(推荐,避免字符串写错)
1. 定义路由密封类
kotlin
kotlin
sealed class Screen(val route: String) {
object Home : Screen("home")
object Detail : Screen("detail/{id}") {
// 拼接完整路由
fun createRoute(id: Int) = "detail/$id"
}
}
2. NavHost 注册
kotlin
scss
NavHost(navController, startDestination = Screen.Home.route) {
composable(Screen.Home.route) { HomeScreen(navController) }
composable(Screen.Detail.route) { entry ->
val id = entry.arguments?.getString("id")
DetailScreen(id)
}
}
3. 跳转
kotlin
less
navController.navigate(Screen.Detail.createRoute(1001))
五、复杂对象传参(Parcelable)
字符串路由只能传基础类型,对象需要用 navArgument + Parcelable
- 数据类实现 Parcelable
kotlin
kotlin
@Parcelize
data class User(val name: String, val age: Int) : Parcelable
- 注册路由并声明参数
kotlin
typescript
composable(
route = "user",
arguments = listOf(
navArgument("user") {
type = NavType.ParcelableType(User::class.java)
}
)
) { entry ->
val user = entry.arguments?.getParcelable<User>("user")
UserScreen(user)
}
- 跳转携带对象
kotlin
javascript
val user = User("张三", 20)
navController.navigate("user") {
arguments?.putParcelable("user", user)
}
六、跳转常用进阶 API
1. 清除回退栈(登录→首页,不能返回登录)
kotlin
javascript
navController.navigate("main") {
popUpTo("login") { inclusive = true } // 把login页面从栈中删除
}
2. 单一实例(避免重复打开同一页面)
kotlin
javascript
navController.navigate("home") {
launchSingleTop = true
}
3. 组合使用
kotlin
ini
navController.navigate(Screen.Detail.createRoute(123)) {
popUpTo(Screen.Home.route)
launchSingleTop = true
restoreState = true // 恢复页面状态
}
七、获取当前路由 / 监听页面切换
kotlin
scss
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
// 监听路由变化
LaunchedEffect(navController) {
navController.currentBackStackEntryFlow.collect { entry ->
val route = entry.destination.route
println("当前页面:$route")
}
}
八、底部导航 + NavHost 完整示例
kotlin
ini
@Composable
fun MainNav() {
val navController = rememberNavController()
val items = listOf("home", "mine")
Scaffold(
bottomBar = {
NavigationBar {
items.forEach { route ->
NavigationBarItem(
selected = navController.currentDestination?.route == route,
onClick = { navController.navigate(route) { launchSingleTop = true } },
icon = { Icon(Icons.Default.Home, null) },
label = { Text(route) }
)
}
}
}
) { padding ->
NavHost(
navController = navController,
startDestination = "home",
modifier = Modifier.padding(padding)
) {
composable("home") { HomePage() }
composable("mine") { MinePage() }
}
}
}
九、常见问题
- 参数为空崩溃 :使用
?: 默认值兜底 - 重复创建页面 :跳转加
launchSingleTop = true - 返回栈错乱 :合理使用
popUpTo清理页面 - 页面状态丢失 :开启
restoreState = true,或用 ViewModel 保存数据 - 跨模块路由:统一用常量 / 密封类管理路由字符串
十、ViewModel 在导航中的使用
每个 composable 作用域内可以独立获取 ViewModel,参数通过 savedStateHandle 获取:
kotlin
scss
composable("detail/{id}") { entry ->
val vm: DetailViewModel = viewModel(
factory = DetailViewModelFactory(entry.savedStateHandle)
)
DetailPage(vm)
}
ViewModel:
kotlin
kotlin
class DetailViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
val id = savedStateHandle.get<String>("id")
}
十一、composable 完整作用详解
1. 本质是什么
composable() 是 NavHost 内部的路由注册函数 ,专门用来给 NavHost 注册一个页面路由规则。它是 NavGraphBuilder 的扩展函数,只能写在 NavHost { ... } 大括号内部。
2. 核心三大作用
① 绑定路由标识 route
route = "home":给这个页面定义唯一字符串标识,后续 navController.navigate("home") 跳转时,NavHost 靠这个 route 找到对应页面。
② 定义该路由对应的 Compose 页面内容
花括号 { HomeScreen(navController) } 就是这个路由要渲染的界面:
- 当导航跳转到
home路由时,NavHost 会执行这个 lambda,渲染里面的HomeScreen - 页面入栈、出栈、动画、生命周期全部由 NavHost 管理
③ 支持配置参数、页面过渡动画、参数类型转换
完整参数列表:
kotlin
javascript
composable(
route: String, // 路由唯一标识
arguments: List<NamedNavArgument> = emptyList(), // 页面接收的参数
deepLinks: List<NavDeepLink> = emptyList(), // 深度链接
enterTransition: (() -> EnterTransition)? = null, // 进入动画
exitTransition: (() -> ExitTransition)? = null, // 退出动画
popEnterTransition: (() -> EnterTransition)? = null,// 返回进入动画
popExitTransition: (() -> ExitTransition)? = null // 返回退出动画
) { backStackEntry ->
// 当前页面的UI代码
}
3. 和 NavHost 的关系
kotlin
scss
NavHost(navController, startDestination = "home") {
// 这里整个大括号是 NavGraphBuilder 构建导航图
// 每一个 composable 就是导航图里的一个节点(页面)
composable(route = "home") {
HomeScreen(navController)
}
composable(route = "detail") {
DetailScreen()
}
}
可以简单理解:
NavHost= 页面容器(存放所有页面)composable= 容器里单个页面的注册条目
4. backStackEntry 参数是什么
lambda 里默认携带 backStackEntry: NavBackStackEntry,用来获取当前页面传递过来的导航参数:
kotlin
scss
composable("detail/{id}") { backStackEntry ->
// 取出路由传递的 id
val id = backStackEntry.arguments?.getString("id")
DetailScreen(id)
}
5. 通俗比喻
把 NavHost 想象成一个手机多任务窗口管理器:
composable("home"):登记一个叫「home」的窗口,窗口内容是 HomeScreennavController.navigate("home"):打开登记好的 home 窗口popBackStack():关闭当前窗口,回到上一个登记的窗口
补充区分
@Composable注解:标记普通可组合函数(页面、控件)NavGraphBuilder.composable():导航专用注册函数,二者完全不是一个东西,只是名字都带 composable