🧭 一、为什么需要 Navigation?
在 Compose 中,你不再用Activity 跳转,而是用“声明式导航”:
- 页面 =
Composable 函数 - 跳转 =
改变当前“目的地”(Destination) - 返回 =
从导航栈弹出
Jetpack Compose Navigation 提供了一套统一、安全、带生命周期管理的导航方案。
🛠 二、快速上手:3 步实现页面跳转
第 1 步:添加依赖(build.gradle)
implementation"androidx.navigation:navigation-compose:2.8.0"第 2 步:定义你的页面(Composable)
@ComposablefunHomeScreen(onNavigateToProfile:()->Unit){Column{Text("首页")Button(onClick=onNavigateToProfile){Text("去个人页")}}}@ComposablefunProfileScreen(){Text("个人中心")}第 3 步:搭建导航图(NavGraph)
@ComposablefunMyApp(){valnavController=rememberNavController()NavHost(navController=navController,startDestination="home"){composable("home"){HomeScreen{navController.navigate("profile")}}composable("profile"){ProfileScreen()}}}✅ 效果:点击按钮 → 跳转到个人页!
✅ 返回键自动生效(因为 NavHost 管理了返回栈)
🔑 三、核心概念解析
| 概念 | 说明 |
|---|---|
| NavController | 导航控制器,负责跳转、返回等操作 |
| NavHost | 导航容器,定义所有页面(目的地) |
| composable(route) | 声明一个页面,route 是唯一标识(如 “profile”) |
| 导航栈(Back Stack) | 记录用户访问路径,支持返回 |
💡 记住:
- rememberNavController():在 Composable 中获取 NavController
- 每个 composable 块对应一个页面
📦 四、页面传参(Arguments)
场景:从首页跳转到用户详情页,传userId
✅ 步骤 1:定义带参数的 route
// 使用 {id} 占位符composable("user/{userId}"){backStackEntry->// 从 backStackEntry 获取参数valuserId=backStackEntry.arguments?.getString("userId")?:"unknown"UserDetailScreen(userId=userId)}✅ 步骤 2:跳转时传值
// 在 HomeScreen 中Button(onClick={navController.navigate("user/123")})✅ 进阶:类型安全传参(推荐!)
使用 Navigation Safe Args(Compose 版) 或 自定义解析。
方法 A:手动解析(简单场景)
composable("user/{userId}"){entry->valuserId=entry.pathSegments[1]// ["user", "123"]UserDetailScreen(userId)}方法 B:使用 navArgs + Parcelable(更安全)
// 定义数据类@ParcelizedataclassUserArgs(valid:String,valname:String):Parcelable// 跳转valargs=UserArgs("123","Alice")navController.navigate("user?args=${Uri.encode(args.toString())}")// 不推荐,复杂// 更推荐:直接拼接基础类型navController.navigate("user/${userId}")📌 官方建议:
对于简单参数(String/Int),直接用 {param};
复杂对象优先通过 ViewModel 共享,而非导航传参。
🔙 五、返回并传递结果(类似 startActivityForResult)
Compose Navigation 原生不支持返回结果,但我们可以用 ViewModel + Shared State 实现。
✅ 推荐方案:共享 ViewModel
// 共享 ViewModel@HiltViewModelclassSharedViewModel@Injectconstructor():ViewModel(){privateval_selectedItem=MutableStateFlow<String?>("")valselectedItem=_selectedItem.asStateFlow()funselectItem(item:String){_selectedItem.value=item}}// 主页@ComposablefunSelectHomeScreen(viewModel:SharedViewModel,onNavigateToSelectItem:()->Unit){valselectedItembyviewModel.selectedItem.collectAsState()Column{Text("选中数据:${selectedItem}")Button(onClick=onNavigateToSelectItem){Text("选项选择")}}}// 选择页@ComposablefunPickerScreen(viewModel:SharedViewModel,onNavigateBackHome:()->Unit){Column{Button(onClick={viewModel.selectItem("选项A")onNavigateBackHome()}){Text("选项A")}}}✅ 优点:解耦、可测试、支持跨多层返回
💡 注:HiltViewModel 如果没有这个注解,需要引入依赖:
implementation("androidx.hilt:hilt-lifecycle-viewmodel:1.3.0")🌐 六、深度链接(Deep Link)
让外部链接(如网页、通知)直接打开 App 内页面。
步骤 1:在 composable 中注册 deep link
composable("profile",deepLinks=listOf(navDeepLink{uriPattern="myapp://profile"})){ProfileScreen()}步骤 2:在 AndroidManifest.xml 中声明
<activityandroid:name=".MainActivity"><intent-filter><actionandroid:name="android.intent.action.VIEW"/><categoryandroid:name="android.intent.category.DEFAULT"/><categoryandroid:name="android.intent.category.BROWSABLE"/><dataandroid:scheme="myapp"android:host="profile"/></intent-filter></activity>效果:点击 myapp://profile → 直接打开个人页!
🧪 七、测试导航
使用 createAndroidComposeRule + navigate() 模拟跳转。
@TestfuntestNavigateToProfile(){composeTestRule.setContent{MyApp()}// 点击按钮composeTestRule.onNodeWithText("去个人页").performClick()// 验证是否显示个人页内容composeTestRule.onNodeWithText("个人中心").assertExists()}⚙️ 八、高级技巧
1. 嵌套路由(Nested Graph)
//TODO main是外层的路由路径,home是嵌套内开始展示UI的路由路径navigation(startDestination="home",route="main"){composable("home"){HomeScreen()}composable("settings"){SettingsScreen()}}composable("login"){LoginScreen()}2. 自定义过渡动画
composable("profile",enterTransition={slideInHorizontally(initialOffsetX={it})},exitTransition={slideOutHorizontally(targetOffsetX={-it})}){...}3. 拦截返回键
BackHandler(enabled=canExit){navController.popBackStack()}✅ 十、最佳实践总结
- 导航图集中管理:所有 composable 定义在一个地方(如 NavGraph.kt)
- 参数尽量简单:用 ID 代替完整对象
- 返回结果用共享状态:避免 hack 式回调
- 深度链接提前规划:方便运营和分享
- 动画适度使用:提升体验但别过度
🎯 结语
Jetpack Compose Navigation 不只是“页面跳转”,而是一套完整的页面生命周期管理方案。
它帮你处理了:
- 返回栈
- 状态保存(配合 rememberSaveable)
- 深度链接
- 动画过渡
- 测试支持
只要掌握NavController+NavHost+composable三件套,你就能构建任意复杂的 App 导航结构!