移动/游戏
在 Jetpack Compose 中用…
适用于 Android Jetpack Compose 项目,需要用可序列化导航键和可变回退栈替代 NavController 构建导航。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Jetpack Compose 中用特定
解决思路
- 添加 Navigation 3 runtime 依赖:`androidx.navigation3:navigation3-runtime:1.0.0-alpha08`。
- 添加 Navigation 3 UI 依赖:`androidx.navigation3:navigation3-ui:1.0.0-alpha08`。
- 添加 Lifecycle 集成依赖:`androidx.lifecycle:lifecycle-viewmodel-navigation3:2.9.2`。
- 添加 Serialization 依赖:`org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1`。
- 启用 Kotlin serialization 插件:`kotlin("plugin.serialization") version "2.2.0"`。
- 用 `@Serializable` 定义不可变导航键,如 `data object Home`、`data class Profile(val userId: String)`、`data class Product(val productId: String, val showReviews: Boolean = false)`、`data object Settings`。
- 把这些导航键作为导航状态使用。
- 在 `@Composable` 中用 `remember { mutableStateListOf<Any>(Home) }` 创建可变回退栈,替代 `NavController`。
- 创建 `AppNavDisplay` 等 `@Composable`,接收 `SnapshotStateList<Any> backStack`。
- 调用 `NavDisplay(backStack = backStack, onBack = { if (backStack.size > 1) backStack.removeLast() })`。
- 在 `NavDisplay` 的 lambda 中用 `when (key)` 根据 key 映射到对应 Screen,例如 `Home -> HomeScreen(...)`、`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)` 中把 `key.userId`、`key.productId` 等传给 Screen。
- 只传递 ID,不传递对象:使用 `backStack += Profile(user.id)`。
- 在 ViewModel 中取数据:使用 `savedStateHandle.toRoute<Profile>()` 取得参数,再调用 `repository.getUser(profile.userId)`。
- 不要把 `User`、`ProductRepository`、`ProductViewModel` 等复杂对象放进回退栈。
- 通过 entry decorators 为 Navigation 3 配置 ViewModel 作用域。
- 在 `NavDisplay` 中传入 `entryDecorators = listOf(rememberSceneSetupNavEntryDecorator(), rememberSavedStateNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator())`。
- 在 ViewModel 中注入 `SavedStateHandle`,并用 `savedStateHandle.toRoute<Profile>()` 读取导航键参数。
- 使用 `rememberSceneSetupNavEntryDecorator()` 为每个 entry 创建导航 scene。
- 使用 `rememberSavedStateNavEntryDecorator()` 在进程重建后自动恢复 destination 状态。
- 使用 `rememberViewModelStoreNavEntryDecorator()` 将 ViewModel 作用域限定到每个 navigation entry。
- 自适应布局:在 `NavDisplay` 中设置 `sceneStrategy = rememberListDetailSceneStrategy()`。
- 使用自适应 scene strategy 自动在单窗格(手机)、双窗格(平板)和折叠屏之间切换。
- 深链处理应解析为导航键:`fun handleDeepLink(uri: Uri) { val userId = uri.lastPathSegment ?: return; backStack += Profile(userId) }`。
- 避免手动构造 route 字符串。
- 动画:在 `NavDisplay` 中用 `transitionSpec = { fadeIn() togetherWith fadeOut() }` 定义场景过渡。
- 注意 Navigation 3 动画 API 在 alpha 阶段可能变化。
- 状态恢复:导航键可序列化并会自动恢复。
- 用 `rememberSaveable(saver = navBackStackSaver()) { mutableStateListOf(Home) }` 保存回退栈。
- 始终确保导航键可序列化。
- 测试:因为导航是状态驱动,可直接构造 `mutableStateListOf<Any>(Home)`,执行 `backStack += Profile("123")`,用 `assertEquals(Profile("123"), backStack.last())` 断言。
- Compose UI 测试可通过检查当前回退栈验证屏幕渲染。
- 从 Navigation Compose 迁移:将 `NavController` 替换为 mutable back stack。
- 从 Navigation Compose 迁移:将 `NavHost` 替换为 `NavDisplay`。
- 从 Navigation Compose 迁移:将 `navigate()` 替换为 `backStack += Key`。
- 从 Navigation Compose 迁移:将 `popBackStack()` 替换为 `removeLast()`。
- 从 Navigation Compose 迁移:将 String routes 替换为 Serializable keys。
- 从 Navigation Compose 迁移:将 `composable()` 替换为 `when(key)`。
- 从 Navigation Compose 迁移:将 Navigation graph 替换为状态驱动 destinations。
- 推荐项目结构:`navigation/` 下放 `AppNavigation.kt`、`NavigationKeys.kt`、`NavigationDisplay.kt`;`feature/` 下放 `home/`、`profile/`、`settings/`。
- DO:使用不可变可序列化键。
- DO:将导航状态保留在 Compose 内。
- DO:传递 ID 而不是复杂对象。
- DO:使用 entry decorators 限定 ViewModel 作用域。
- DO:使用 `rememberSaveable` 做状态恢复。
- DO:将导航建模为可观察应用状态。
- DON'T:不要使用 string routes。
- DON'T:不要通过导航传递 repositories 或 ViewModels。
- DON'T:不要修改导航键。
- DON'T:不要在回退栈中存储业务对象。
- DON'T:不要在重组时重建回退栈。
- DON'T:不要混用 Navigation Compose API 与 Navigation 3 API。
- 参考 Android Navigation 3 文档、Navigation 3 samples、Lifecycle ViewModel Navigation 3 文档、Material 3 Adaptive Navigation 文档。
适用边界 · 注意事项
- 项目不使用 Jetpack Compose,或只能依赖稳定版导航库时。
- 需要继续使用 Navigation Compose 的 `NavController`、`NavHost`、string routes 或混用两套 API 时。
- 无法启用 Kotlin serialization,或无法接受 Navigation 3 alpha 版本依赖时。