Why can we not pass a Serializable object to NavArguments? [duplicate]

Edit: Updated to Compose Navigation 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...

Then declare your composable like this:

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

Basically you can do the following:

// 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")

I've written a small extension for the NavController.

import android.os.Bundle
import androidx.core.net.toUri
import androidx.navigation.*

fun NavController.navigate(
    route: String,
    args: Bundle,
    navOptions: NavOptions? = null,
    navigatorExtras: Navigator.Extras? = null
) {
    val routeLink = NavDeepLinkRequest
        .Builder
        .fromUri(NavDestination.createRoute(route).toUri())
        .build()

    val deepLinkMatch = graph.matchDeepLink(routeLink)
    if (deepLinkMatch != null) {
        val destination = deepLinkMatch.destination
        val id = destination.id
        navigate(id, args, navOptions, navigatorExtras)
    } else {
        navigate(route, navOptions, navigatorExtras)
    }
}

As you can check there are at least 16 functions "navigate" with different parameters, so it's just a converter for use

public open fun navigate(@IdRes resId: Int, args: Bundle?) 

So using this extension you can use Compose Navigation without these terrible deep link parameters for arguments at routes.