指南:迁移到 Compose 和 Navigation 2 中的类型安全导航

本指南概述了将基于字符串的路线替换为可序列化的 Kotlin 类型的流程,以实现编译时安全性并消除因拼写错误或不正确的实参类型而导致的运行时崩溃。

前提条件

在开始迁移之前,请验证您的项目是否满足以下要求:

  1. Navigation 版本:更新到 Jetpack Navigation 2.8.0 或更高版本
  2. Kotlin 序列化插件
  3. 将插件添加到 libs.versions.toml
[libraries]
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }

[plugins]
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
  • 将依赖项添加到顶级 build.gradle.kts 和模块级 build.gradle.kts

第 1 步:确定目标平台

将您的常量路由字符串替换为 @Serializable 对象和类。

  • 对于没有实参的界面:使用 data object
  • 对于具有实参的界面:使用 data class

之前(基于字符串)

const val ROUTE_HOME = "home"
const val ROUTE_PROFILE = "profile/{userId}"

之后(类型安全)

import kotlinx.serialization.Serializable

@Serializable
object Home

@Serializable
data class Profile(val userId: String)

第 2 步:更新 NavHost 配置

更新 NavHost 以使用 composabledialog 函数中的新泛型类型。

之前:

NavHost(navController, startDestination = "home") {
    composable("home") { HomeScreen(...) }
    composable("profile/{userId}") { backStackEntry ->
        val userId = backStackEntry.arguments?.getString("userId")
        ProfileScreen(userId)
    }
}

之后:

NavHost(navController, startDestination = Home) {
    composable<Home> {
        HomeScreen(...)
    }
    composable<Profile> { backStackEntry ->
        // The library automatically handles argument extraction
        val profile: Profile = backStackEntry.toRoute()
        ProfileScreen(profile.userId)
    }
}

第 3 步:实现类型安全导航调用

将字符串插值导航调用替换为类实例。

之前:

navController.navigate("profile/user123")

之后:

navController.navigate(Profile(userId = "user123"))

第 4 步:在 ViewModel 中访问实参

如果您使用 ViewModel,现在可以直接从 SavedStateHandle 中提取路线对象。

实现

class ProfileViewModel(
    savedStateHandle: SavedStateHandle
) : ViewModel() {
    // Automatically parses arguments into the Profile class
    private val profile = savedStateHandle.toRoute<Profile>()
    val userId = profile.userId
}

第 5 步:(高级)处理自定义类型

如果您需要传递复杂的数据类(不仅仅是基元),则必须定义自定义 NavType

  1. 创建自定义类型: ```kotlin val SearchFilterType = object : NavType(isNullableAllowed = false) { override fun get(bundle: Bundle, key: String): SearchFilter? = Json.decodeFromString(bundle.getString(key) ?: return null)
override fun parseValue(value: String): SearchFilter =
    Json.decodeFromString(Uri.decode(value))

override fun put(bundle: Bundle, key: String, value: SearchFilter) {
    bundle.putString(key, Json.encodeToString(value))
}

}



2. **Register it in the Graph**:
```kotlin
composable<Search>(
    typeMap = mapOf(typeOf<SearchFilter>() to SearchFilterType)
) { ... }

最佳实践和提示

  • 密封层次结构:对于大型应用,请使用密封接口或类对路线进行分组,以保持导航结构的井然有序
  • 对象实例:对于没有参数的路由,请始终使用 object 而不是 class,以避免不必要的分配
  • 可为 null 的类型:新 API 支持可为 null 的类型(例如 data class Search(val query: String?)),并自动提供默认值
  • 测试:在界面测试期间,使用 navController.currentBackStackEntry?.hasRoute<T>() 以类型安全的方式检查当前目的地