I want to pass a parcelable object (BluetoothDevice) to a composable using compose navigation.

传递基元类型很容易:

composable(
  "profile/{userId}",
  arguments = listOf(navArgument("userId") { type = NavType.StringType })
) {...}
navController.navigate("profile/user1234")

But I can't pass a parcelable object in the route unless I can serialize it to a string.

composable(
  "deviceDetails/{device}",
  arguments = listOf(navArgument("device") { type = NavType.ParcelableType(BluetoothDevice::class.java) })
) {...}
val device: BluetoothDevice = ...
navController.navigate("deviceDetails/$device")

The code above obviously doesn't work because it just implicitly calls toString().

Is there a way to either serialize a Parcelable to a String so I can pass it in the route or pass the navigation argument as an object with a function other than navigate(route: String)?

推荐答案

Edit:更新为合成导航2.4.0-beta07

Seems like previous solution is not supported anymore. Now you need to create a custom NavType.

Let's say you have a class like:

@Parcelize
data class Device(val id: String, val name: String) : Parcelable

Then you need to define a NavType

class AssetParamType : NavType<Device>(isNullableAllowed = false) {
    override fun get(bundle: Bundle, key: String): Device? {
        return bundle.getParcelable(key)
    }

    override fun parseValue(value: String): Device {
        return Gson().fromJson(value, Device::class.java)
    }

    override fun put(bundle: Bundle, key: String, value: Device) {
        bundle.putParcelable(key, value)
    }
}

Notice that I'm using Gson to convert the object to a JSON string. But you can use the conversor that you prefer...

然后按如下方式声明您的可合成组件:

NavHost(...) {
    composable("home") {
        Home(
            onClick = {
                 val device = Device("1", "My device")
                 val json = Uri.encode(Gson().toJson(device))
                 navController.navigate("details/$json")
            }
        )
    }
    composable(
        "details/{device}",
        arguments = listOf(
            navArgument("device") {
                type = AssetParamType()
            }
        )
    ) {
        val device = it.arguments?.getParcelable<Device>("device")
        Details(device)
    }
}

Original answer

基本上,您可以执行以下操作:

// In the source screen...
navController.currentBackStackEntry?.arguments = 
    Bundle().apply {
        putParcelable("bt_device", device)
    }
navController.navigate("deviceDetails")

And in the details screen...

val device = navController.previousBackStackEntry
    ?.arguments?.getParcelable<BluetoothDevice>("bt_device")

Kotlin相关问答推荐

ktor中的json序列化

将Kotlin中的函数变量委托给通用类

如何使用Kotlinx.LocalDateTime获取重置时间为00:00的当前日期?

为什么 Kotlin 中没有 init 块的注释

在 Kotlin 中实现并输入 Either

为什么 Kotlin main 函数需要 @JVMStatic 注解?

为什么Kotlin不用static inner class来实现带有object关键字的单例呢?

Kotlin:调用 CoroutineScope.launch 与在协程内启动之间的区别

mutableStateOf 在 Jetpack Compose 中不记住 API 的情况下保持重组后的值

Jetpack Compose 中的连续重组

Jetpack BottomNavigation - java.lang.IllegalStateException:Already attached to lifecycleOwner

Kotlin JVM 和 Kotlin Native 有什么区别?

main函数和常规函数有什么区别?

Kotlin not nullable值可以为null吗?

Kotlin通过映射委托属性,如果映射中不存在,则抛出NoTouchElementException

如何从kotlin中的类实例化对象

Android Kotlin 创建类实现 Parcelable 在 writeToParcel 方法的 override中给出错误

在android java类中使用Kotlin扩展

如何根据ArrayList的对象属性值从中获取最小/最大值?

Failure delivering result on activity result