Why can't this parcelable be passed as an argument in Jetpack Compose Navigation?

74 views Asked by At

I have an Android app based on Jetpack Compose that uses the Google Play Services Barcode Scanner from the ML Kit to scan a QR Code.

On click of a FAB, the request to scan a new barcode is executed and the scanner window opens. When the scanning is finished (or cancelled or failed) a callback is invoked and I want to navigate the user to a new destination where the scanned code is used.

Passing the data to the new destination is the problem, however.

The data I need looks like this:

data class ScannerResult(
    val state: ScannerState,
    val displayValue: String?,
    val rawValue: String?,
    val exception: Exception? = null,
)

enum class ScannerState {
    SUCCEEDED,
    CANCELED,
    FAILED
}

That way I can handle all three possible outcomes with one object.

I know that you should not pass any complex data (Parcelable, Serializable) via navigation, but I do not want to introduce a ViewModel just to save the data there and retrieve it afterwards.

Currently my destination looks like this:

fun NavGraphBuilder.newAccountScreen(onNavigateUp: () -> Unit) {
    composable(
        "$navRouteNewAccount/{state}/{rawValue}?exception={exception}", arguments = listOf(
            navArgument("state") {
                type = NavType.EnumType(ScannerState::class.java)
                nullable = false
            },
            navArgument("rawValue") {
                type = NavType.StringType
                nullable = true
            },
            navArgument("exception") {
                type = NavType.SerializableType(Exception::class.java)
                nullable = true
            }
        )
    ) { backStackEntry ->
        val result = backStackEntry.arguments?.let { bundle ->
            ScannerResult(
                bundle.serializable("state")!!,
                bundle.getString("rawValue"),
                bundle.serializable("exception")
            )
        }
        if (result == null) {
            Log.e("NewAccountScreen", "No result object found, navigating up")
            onNavigateUp()
        } else {
            NewAccountScreen(
                result = result,
                onNavigateUp = onNavigateUp
            )
        }
    }
}

In here I take the ScannerResult apart and reconstruct it on the other side. That however does not work because it throws the following exception: java.lang.UnsupportedOperationException: Serializables don't support default values.

I have also tried implementing Parcelable with ScannerResult, but I couldn't find a way to pass the parcelable directly to NavController.navigate().

Does anybody have an Idea as to how to solve this?

0

There are 0 answers