1

As I see in NavType docs, there is a sub-type called NavType.SerializableType which:

Is used for Serializable NavArguments.

The SerializableType's class constructor takes an argument of type Class<D?>? which should be a:

class that is a subtype of Serializable.

Now, I have the following class:

data class Product (
    var key: String? = null,
    //Other 15 different fields.
): Serializable 

Here is how the NavHost looks like:

NavHost(
    navController = navController,
    startDestination = "Products"
) {
    composable(
        route = "Products"
    ) {
        ProductsScreen(
            navController = navController
        )
    }
    composable(
        route = "Product/{product}",
        arguments = listOf(
            navArgument("product") {
                type = NavType.SerializableType(Product::class.java)
            }
        )
    ) {
        val product = navController.previousBackStackEntry?.arguments?.getSerializable("product") as Product
        ProductScreen(
            navController = navController,
            product = product
        )
    }

And I navigate from inside the start destination (Products screen) to (Product screen) using inside the :

navController.currentBackStackEntry?.arguments?.putSerializable("product", product)
navController.navigate("Product/${product}")

I get a crash with this errror:

Serializables don't support default values.

However, if I change the above by removing /{product}:

NavHost(
    navController = navController,
    startDestination = "Products"
) {
    composable(
        route = "Products"
    ) {
        ProductsScreen(
            navController = navController
        )
    }
    composable(
        route = "Product",
        arguments = listOf(
            navArgument("product") { 
                type = NavType.SerializableType(Product::class.java)
            }
        )
    ) {
        val product = navController.previousBackStackEntry?.arguments?.getSerializable("product") as Product
        ProductScreen(
            navController = navController,
            product = product
        )
    }

And navigate as explained here:

navController.currentBackStackEntry?.arguments?.putSerializable("product", product)
navController.navigate("Product") 

I get:

java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/Product } cannot be found in the navigation graph NavGraph(0x0) startDestination={Destination(0xb543811) route=Products}

How can I add a Serializable object of type Product to NavArguments and get back correctly? I don't want to transform the object to JSON String. Is this even possible?

Joan P.
  • 2,368
  • 6
  • 30
  • 63
  • 4
    the Jetpack Navigation team said that this is "just like the web", and therefore this is by design, and serialization to JSON (or similar format such as a byte array represented as base64) is unavoidable, and that you should URL-encode any strings because `&` or `/` or `$` breaks the nav args parser. You can define "custom parser strategy" but it will still rely on serialization to JSON (as is shown in their official docs and example). – EpicPandaForce Jan 12 '22 at 14:40
  • 1
    @EpicPandaForce Thanks for commenting. So if you say that transforming the `Product` object into JSON **cannot** be avoided, then I'll go ahead with that. It's not what I want, but perhaps, that's the way it is... – Joan P. Jan 12 '22 at 14:44
  • Yeah, it's unfortunate, there is also docs on it here: https://developer.android.com/guide/navigation/navigation-kotlin-dsl#custom-types – EpicPandaForce Jan 12 '22 at 14:48
  • 2
    @JoanP. I'd advice to avoid Jetpack Navigation alltogether. Use fragments as before, or if you really want to stay compose-only - use Voyager – Jakoss Jan 12 '22 at 15:39
  • You shouldn't be passing a `Product` as an argument in *any* case: you should be passing the ID of your product, just like you would if you were building a RESTful website (where putting your entire model object in the URL is just as odd of a use case). Where is your `ProductRepository` that both destinations use as an observable single source of truth? – ianhanniballake Jan 12 '22 at 17:05
  • @Jakoss What exactly is "Voyager"? Thanks for commenting. – Joan P. Jan 13 '22 at 09:02
  • @ianhanniballake I don't want to stop using Jetpack Navigation as Jakoss suggested. However, it's weird that I cannot send a Serializable object between screens. And yes, I can only send the product ID, but that means that I have to create another database call, in order to request that product. But that is actually not necessary, since I already have all the data inside the object that was clicked. That object I want to be passed to the next screen. It's also true I can send the value of each field separately, but there are some many of them. It's a pain to do that. – Joan P. Jan 13 '22 at 09:16
  • @ianhanniballake Is converting the `Product` object to `GSON` the only solution for this use-case? Thanks – Joan P. Jan 13 '22 at 09:16
  • 1
    @JoanP. Voyager is alternative navigation library, that makes use of kotlin types instead of relying on urls like Jetpack Navigation (https://github.com/adrielcafe/voyager). It's pragmatic, works like expected. As to sending objects - googlers seems to ignore the fact that we don't want to create a special table just to be able to pass object with 2-3 fields to another screen. We've tried that conversation, no luck there – Jakoss Jan 13 '22 at 09:30
  • my Android app is not a RESTful web page, so I don't see why such limitations would need to apply. At this rate, we might also "serialize JSON to session storage I mean shared preferences" for data sharing soon. Anyways, if you use the library `Compose-Destinations`, they generate code that converts serializable/parcelable to Base64 and back. https://github.com/raamcosta/compose-destinations/blob/95d6154f932751703ec424ab968491d9dee6b7ad/compose-destinations/src/main/java/com/ramcosta/composedestinations/navargs/serializable/DefaultSerializableNavTypeSerializer.kt#L17-L23 – EpicPandaForce Jan 13 '22 at 11:10
  • 1
    @EpicPandaForce After hours, and hours of research, I understand now why are you so angry on this type of navigation :| But hopefully, this will change, so we can have a way to simply pass a Serializable object to another screen. – Joan P. Jan 13 '22 at 11:17

0 Answers0