关于 navigation3
这是一项使用 Navigation 3 在 Jetpack Compose 中实现状态驱动导航的技能,适用于 Android 开发者。内容涵盖返回栈、ViewModel 作用域、自适应布局、深链、状态恢复、动画、测试及从 Navigation Compose 迁移。
主要功能
- 01可序列化的导航键
- 02可变状态返回栈
- 03用于 ViewModel 的条目装饰器
- 04自适应布局与深链
- 05状态恢复与测试
适用场景
- 01将目标页面建模为应用状态
- 02为导航条目限定 ViewModel 作用域
- 03适配手机、平板和折叠设备布局
- 04测试返回栈变化与屏幕渲染
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ new-silvermoon/ awesome-android-agent-skills --skill navigation3安装后在 Claude Code 中输入/navigation3确认已加载。
详细介绍
Navigation 3
概览
使用 Navigation 3 在 Jetpack Compose 中实现状态驱动的导航。与 Navigation Compose 不同,Navigation 3 将导航建模为应用状态,而不是通过 NavController 实现。本指南涵盖导航键、返回栈管理、ViewModel 作用域、条目装饰器、自适应布局、深层链接、动画、状态恢复和测试。
设置
添加 Navigation 3 依赖项:
// build.gradle.kts
dependencies {
implementation("androidx.navigation3:navigation3-runtime:1.0.0-alpha08")
implementation("androidx.navigation3:navigation3-ui:1.0.0-alpha08")
// Lifecycle integration
implementation("androidx.lifecycle:lifecycle-viewmodel-navigation3:2.9.2")
// Serialization
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
}
// Enable serialization plugin
plugins {
kotlin("plugin.serialization") version "2.2.0"
}
核心概念
1. 定义导航键
导航目标由不可变且可序列化的对象表示。
import kotlinx.serialization.Serializable
@Serializable
data object Home
@Serializable
data class Profile(
val userId: String
)
@Serializable
data class Product(
val productId: String,
val showReviews: Boolean = false
)
@Serializable
data object Settings
键会成为你的导航状态。
2. 创建返回栈
Navigation 3 使用可变状态返回栈取代 NavController。
@Composable
fun MyApp() {
val backStack = remember {
mutableStateListOf<Any>(Home)
}
AppNavDisplay(backStack)
}
3. 创建 NavDisplay
@Composable
fun AppNavDisplay(
backStack: SnapshotStateList<Any>
) {
NavDisplay(
backStack = backStack,
onBack = {
if (backStack.size > 1) {
backStack.removeLast()
}
}
) { key ->
when (key) {
Home ->
HomeScreen(
onProfileClick = {
backStack += Profile(it)
}
)
is Profile ->
ProfileScreen(
userId = key.userId
)
is Product ->
ProductScreen(
productId = key.productId,
showReviews = key.showReviews
)
Settings ->
SettingsScreen()
}
}
}
导航模式
向前导航
backStack += Profile("user123")
返回
backStack.removeLast()
替换当前屏幕
backStack[backStack.lastIndex] = Home
清空返回栈
backStack.clear()
backStack += Home
返回根页面
while (backStack.size > 1) {
backStack.removeLast()
}
参数处理
获取参数
参数已包含在导航键中。
when (val key = currentKey) {
is Profile -> {
ProfileScreen(
userId = key.userId
)
}
is Product -> {
ProductScreen(
productId = key.productId
)
}
}
传递 ID,而非对象
// CORRECT
backStack += Profile(user.id)
// Fetch object inside ViewModel
class ProfileViewModel(
savedStateHandle: SavedStateHandle,
repository: UserRepository
) : ViewModel() {
val profile = savedStateHandle.toRoute<Profile>()
val user =
repository.getUser(profile.userId)
}
// INCORRECT
backStack += User(...)
backStack += ProductRepository(...)
backStack += ProductViewModel(...)
ViewModel 集成
Navigation 3 使用条目装饰器来限定 ViewModel 的作用域。
NavDisplay(
backStack = backStack,
entryDecorators = listOf(
rememberSceneSetupNavEntryDecorator(),
rememberSavedStateNavEntryDecorator(),
rememberViewModelStoreNavEntryDecorator()
)
) { key ->
// destinations
}
ViewModel 示例
class ProfileViewModel(
savedStateHandle: SavedStateHandle
) : ViewModel() {
val profile =
savedStateHandle.toRoute<Profile>()
}
条目装饰器
Navigation 3 使用装饰器来附加生命周期功能。
场景设置
rememberSceneSetupNavEntryDecorator()
为每个条目创建导航场景。
保存状态
rememberSavedStateNavEntryDecorator()
进程重建后自动恢复目标状态。
ViewModel 存储
rememberViewModelStoreNavEntryDecorator()
将 ViewModel 的作用域限定在各个导航条目中。
自适应导航
Navigation 3 可与 Material Adaptive 布局集成。
NavDisplay(
backStack = backStack,
sceneStrategy = rememberListDetailSceneStrategy()
)
使用自适应场景策略自动切换为:
- 单窗格(手机)
- 双窗格(平板电脑)
- 折叠设备
深层链接
深层链接应解析为导航键。
fun handleDeepLink(uri: Uri) {
val userId =
uri.lastPathSegment ?: return
backStack += Profile(userId)
}
避免手动构造路由字符串。
动画
导航过渡通过场景过渡定义。
NavDisplay(
backStack = backStack,
transitionSpec = {
fadeIn() togetherWith fadeOut()
}
)
Navigation 3 的动画 API 在 Alpha 阶段可能会有所变化。
状态恢复
导航键可序列化,并会自动恢复。
val backStack = rememberSaveable(
saver = navBackStackSaver()
) {
mutableStateListOf(Home)
}
始终确保键可序列化。
测试
导航由状态驱动,因此测试很简单。
示例
@Test
fun navigateToProfile() {
val backStack =
mutableStateListOf<Any>(Home)
backStack += Profile("123")
assertEquals(
Profile("123"),
backStack.last()
)
}
Compose UI 测试可以通过检查当前返回栈来验证屏幕渲染情况。
从 Navigation Compose 迁移
| Navigation Compose | Navigation 3 |
|---|---|
NavController | 可变返回栈 |
NavHost | NavDisplay |
navigate() | backStack += Key |
popBackStack() | removeLast() |
| 字符串路由 | 可序列化的键 |
composable() | when(key) |
| 导航图 | 状态驱动的目标 |
推荐的项目结构
navigation/
AppNavigation.kt
NavigationKeys.kt
NavigationDisplay.kt
feature/
home/
profile/
settings/
重要规则
应该
- 使用不可变且可序列化的键
- 将导航状态保留在 Compose 中
- 传递 ID,而不是复杂对象
- 使用条目装饰器限定 ViewModel 的作用域
- 使用
rememberSaveable恢复状态 - 将导航建模为可观察的应用状态
不应该
- 使用字符串路由
- 通过导航传递仓库或 ViewModel
- 修改导航键
- 在返回栈中存储业务对象
- 在重组时重新创建返回栈
- 混用 Navigation Compose API 和 Navigation 3 API
参考资料
- Android Navigation 3 文档
- Navigation 3 示例
- Lifecycle ViewModel Navigation 3 文档
- Material 3 Adaptive Navigation 文档