Jetpack Compose Navigation —— 页面跳转与返回栈管理
本文深入讲解 Compose Navigation 的全部用法,从基础跳转到深层链接、BottomNavigation 联动、动画转场。配合 Compose 从 0 到 1 阅读效果最佳。
Navigation 三大核心组件
Compose Navigation 由三个核心组件构成:
| 组件 | 作用 | 创建方式 |
|---|---|---|
| NavController | 管理返回栈,控制跳转 | rememberNavController() |
| NavHost | 路由表,把路由和界面绑定 | NavHost(navController, startDestination) { } |
| composable() | 注册一个路由对应的页面 | composable("route") { Screen() } |
1 |
|
⚠️
rememberNavController()必须在NavHost外部创建,如果在NavHost内部创建会导致每次重组都重新生成。
路由定义
字符串路由(基础)
1 | // 方式一:直接写字符串(适合简单场景) |
常量管理(推荐)
1 | object Routes { |
✅ 集中管理路由常量,方便跳转和避免拼写错误。
路由参数速查表
| 参数写法 | 含义 | 示例路由 |
|---|---|---|
{arg} | 必选参数 | detail/{id} → detail/123 |
{arg}={default} | 带默认值 | list/{page}=1 |
{arg}?argType=Int | 指定类型 | user/{id} → arguments.add(NavType) |
?arg={arg} | 可选查询参数 | search?q={query} |
参数传递
必选参数(路径参数)
1 | // 路由定义 |
可选参数(查询参数)
1 | // 路由定义 |
🔑 可选参数 =
?+=+defaultValue,三者缺一不可。
参数类型对照表
| NavType | Kotlin 类型 | 取值方法 |
|---|---|---|
StringType | String | getString("key") |
IntType | Int | getInt("key") |
LongType | Long | getLong("key") |
FloatType | Float | getFloat("key") |
BoolType | Boolean | getBoolean("key") |
StringArrayType | Array<String> | getStringArray("key") |
IntArrayType | IntArray | getIntArray("key") |
导航操作 —— 跳转、返回、清栈
navigate() 跳转
1 | // 基础跳转 |
popBackStack() 返回
1 | // 返回到上一个页面 |
navigateUp() 返回上一层
1 | // 等同于物理返回键 |
导航选项组合实战
1 | // 场景:登录成功后跳转主页,且按返回键不再回到登录页 |
返回栈管理
返回栈原理
1 | NavHost 启动 startDestination="home" |
获取当前路由
1 | val navBackStackEntry by navController.currentBackStackEntryAsState() |
监听导航事件
1 | // 监听路由变化(例如埋点统计) |
Deep Link —— 从外部打开指定页面
1 | composable( |
AndroidManifest.xml 配置:
1 | <activity |
BottomNavigation + Navigation 联动
标准实现
1 | data class BottomNavItem( |
⚠️ 关键:
popUpTo(findStartDestination) + saveState + restoreState这个三件套可防止切换 tab 时重复创建页面并保持滚动位置。
NavigationRail(平板/横屏)
1 | Scaffold( |
动画转场
composable 动画参数
1 | composable( |
常用动画速查表
| 动画方法 | 效果 |
|---|---|
slideInHorizontally { it } | 从右侧滑入 |
slideInHorizontally { -it } | 从左侧滑入 |
slideInVertically { it } | 从下方滑入 |
slideInVertically { -it } | 从上方滑入 |
fadeIn(initialAlpha = 0f) | 淡入 |
fadeOut(targetAlpha = 0f) | 淡出 |
scaleIn(initialScale = 0.8f) | 缩放进入 |
scaleOut(targetScale = 0.8f) | 缩放退出 |
slideInHorizontally { it } + fadeIn() | 组合动画(常见的推入+淡入) |
🔧
it代表完整宽度(水平)或完整高度(垂直),用{ it / 2 }可控制滑动距离。
AnimatedNavHost 全局动画(Material 3)
1 | // build.gradle.kts |
嵌套导航图
当模块较多时,可以把路由分组:
1 | NavHost(navController, startDestination = "main") { |
跳转到嵌套路由:navController.navigate("auth/register")
常见坑与最佳实践
| 坑 | 原因 | 解决 |
|---|---|---|
| 快速双击跳转多个相同页面 | navigate 默认允许重复 | launchSingleTop = true |
| 切换 tab 后页面状态丢失 | 没有 saveState/restoreState | BottomNavigation 三件套 |
| 参数获取为空 | 未声明 navArgument | 必须用 arguments = listOf(...) 声明 |
| 返回键直接退出应用 | 返回栈为空 | 判断 navController.previousBackStackEntry == null 时给提示 |
| 跳转后按返回又回到原始页 | 没 popUpTo | 登录成功跳转后 popUpTo("login") { inclusive = true } |
| 深层链接打不开 | AndroidManifest 没配 intent-filter | 两个 intent-filter 都要加 |
完整实战示例
1 | object Routes { |