如何在jetpack compose中传递导航中的对象?

Mah*_*hag 31 android-jetpack-compose jetpack-compose-navigation

文档中,我可以传递字符串、整数等。但是如何在导航上传递对象呢?

注意:如果我设置参数类型 Parcelable,则应用程序会崩溃并显示java.lang.UnsupportedOperationException: Parcelables don't support default values..

composable(
    "vendor/details/{vendor}",
        arguments = listOf(navArgument("vendor") {
            type = NavType.ParcelableType(Vendor::class.java)
        })
) {
// ...
}
Run Code Online (Sandbox Code Playgroud)

Mah*_*hag 28

根据导航文档

注意:通过参数传递复杂的数据结构被认为是反模式。每个目的地应负责根据最少的必要信息(例如项目 ID)加载 UI 数据。这简化了流程重新创建并避免了潜在的数据不一致。

因此,如果可能的话,请避免传递复杂的数据。更多官方详细信息请参见此处


以下解决方法基于navigation-compose版本2.7.5


我找到了两种传递对象的解决方法。

1. 将对象转换为JSON字符串

在这里,我们可以使用对象的 JSON 字符串表示形式来传递对象。

示例代码:

val ROUTE_USER_DETAILS = "user-details?user={user}"


// Pass data (I am using Moshi here)
val user = User(id = 1, name = "John Doe") // User is a data class.

val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(User::class.java).lenient()
val userJson = jsonAdapter.toJson(user)

navController.navigate(
    ROUTE_USER_DETAILS.replace("{user}", userJson)
)


// Receive Data
NavHost {
    composable(ROUTE_USER_DETAILS) { backStackEntry ->
        val userJson =  backStackEntry.arguments?.getString("user")
        val moshi = Moshi.Builder().build()
        val jsonAdapter = moshi.adapter(User::class.java).lenient()
        val userObject = jsonAdapter.fromJson(userJson)

        UserDetailsView(userObject) // Here UserDetailsView is a composable.
    }
}


// Composable function/view
@Composable
fun UserDetailsView(
    user: User?
){
    // ...
}

Run Code Online (Sandbox Code Playgroud)

重要提示:如果您的数据有任何 URL 或任何字符串&等,您可能必须分别使用URLEncoder.encode(jsonString, "utf-8")URLDecode.decode(jsonString, "utf-8")来传递和接收数据。但编码-解码也有一些副作用!就像如果你的字符串有任何+符号,它会用 aspace等替换它。

2. 使用传递对象NavBackStackEntry

在这里我们可以使用 传递数据navController.currentBackStackEntry并使用 接收数据navController.previousBackStackEntry

注意:从版本开始,1.6.0对 的任何更改都*BackStackEntry.arguments不会反映在对arguments. 所以,现在我们必须使用savedStateHandle. 版本变更详情请参见此处

示例代码:

val ROUTE_USER_DETAILS = "user-details"


// Pass data
val user = User(id = 1, name = "John Doe") // User is a parcelable data class.

// `arguments` will not work after version 1.6.0.
// navController.currentBackStackEntry?.arguments?.putParcelable("user", user) // old
snavController.currentBackStackEntry?.savedStateHandle?.set("user", user) // new
navController.navigate(ROUTE_USER_DETAILS)


// Receive data
NavHost {
    composable(ROUTE_USER_DETAILS) {
        // `arguments` will not work after version 1.6.0.
        // val userObject = navController.previousBackStackEntry?.arguments?.getParcelable<User>("user") // old
        val userObject: User? = navController.previousBackStackEntry?.savedStateHandle?.get("user") // new
        
        UserDetailsView(userObject) // Here UserDetailsView is a composable.
    }
}


// Composable function/view
@Composable
fun UserDetailsView(
    user: User?
){
    // ...
}
Run Code Online (Sandbox Code Playgroud)

重要提示:第​​二个解决方案不稳定,如果我们在导航时弹出后退堆栈,则该解决方案将不起作用。