androidkotlinmultiplatformcompose

How do I pass kotlinx @Serializable object as a back result in compose multiplatform


According to release notes here SavedState now supprots kotlix.serialization. So I'm trying to lavarage it for navigation. It works as expected for nav args but when I'm trying to pas it as a result to the previous BackStackEntry it either crashes or does nothing.

If I call

navController.previousBackStackEntry?.savedStateHandle?.set("RESULT", brand)

It crashes with

java.lang.IllegalArgumentException: Can't put value with type class com.....brands.data.dto.BrandDto into saved state
at androidx.lifecycle.SavedStateHandle.set(SavedStateHandle.android.kt:151)

If I'm using

private var result by resultHandle.saved(RESULT_BRAND_EDIT) { brand }
...
result = brand
navController.popBackStack()

and

private val brand by savedStateHandle.saved(RESULT_BRAND_EDIT) { BrandDto(name = "") }
...
product.brand = brand

When I read this value (when the screen onResume is triggered after navigation back), it returns the default value for brand, not the one I put in the code above.

So I'm wondering if is a problem with the framework or am I doing something wrong? Is there any example of working code for such scenario?


Solution

  • When you use by savedStateHandle.saved, you aren't storing the value itself inside the SavedStateHandle (@Serializable classes by themselves aren't saveable), but instead you are using the SavedState 1.3 Serialization Support:

    KotlinX Serialization Support

    • SavedState now includes KotlinX Serialization support. You can convert a class annotated with @Serializable to a SavedState using the methods encodeToSavedState and decodeFromSavedState. The returned SavedState is a regular Bundle on Android and can be used by any API that accepts a Bundle.

    The by savedStateHandle.saved is doing that encodeToSavedState and decodeToSavedState for you.

    When you manually call set, you aren't using the property delegate, so you need to manually call encodeToSavedState:

    navController.previousBackStackEntry?.savedStateHandle?.set(
      "RESULT",
      encodeToSavedState(brand) // Manually encode it as something SavedStateHandle can...handle
    )