DEV Community

RockAndNull
RockAndNull

Posted on • Originally published at paleblueapps.com on

Type-Safe Navigation in Jetpack Compose: Passing Custom Classes

Type-Safe Navigation in Jetpack Compose: Passing Custom Classes

Jetpack Compose's Navigation library has introduced long-awaited type safety, making navigation between destinations more robust, intuitive, and of-course safe.

But what happens when you need to pass custom classes as arguments? Luckily, the library supports this functionality - with some additional setup. Here's a complete concise example to guide you.

Consider a CheckoutFlow sealed interface with a Calendar destination accepting an Offer object as an argument:

fun NavGraphBuilder.checkoutGraph(navController: NavHostController) {
    navigation<CheckoutFlow.Start>(
        startDestination = CheckoutFlow.Calendar(offer = null),
    ) {
        composable<CheckoutFlow.Calendar>(
            typeMap = mapOf(
                typeMapOf<Offer?>(),
            ),
        ) {
            val route: CheckoutFlow.Calendar = it.toRoute()

            CheckoutCalendarComponent(
                offer = route.offer,
            )
        }
    }
}

sealed interface CheckoutFlow {
    @Serializable
    data object Start : CheckoutFlow

    @Serializable
    data class Calendar(
        val offer: Offer?,
    ) : CheckoutFlow
}

@Serializable
data class Offer(
    @SerialName("description")
    val description: String,
    @SerialName("id")
    val id: Int
)
Enter fullscreen mode Exit fullscreen mode

The Offer class, marked with @Serializable, requires additional work for navigation argument handling. Use the following custom NavType implementation for serialization and deserialization:

inline fun <reified T> serializableNavType(isNullableAllowed: Boolean = false) =
    object : NavType<T>(isNullableAllowed = isNullableAllowed) {
        override fun put(bundle: Bundle, key: String, value: T) {
            bundle.putString(key, serializeAsValue(value))
        }

        override fun get(bundle: Bundle, key: String): T? {
            return bundle.getString(key)?.let { parseValue(it) }
        }

        override fun serializeAsValue(value: T): String {
            return Uri.encode(Json.encodeToString(value))
        }

        override fun parseValue(value: String): T {
            return Json.decodeFromString(Uri.decode(value))
        }

        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (other !is NavType<*>) return false
            if (other::class.java != this::class.java) return false
            if (isNullableAllowed != other.isNullableAllowed) return false
            return true
        }
    }

inline fun <reified T> typeMapOf(): Pair<KType, NavType<T>> {
    val type = typeOf<T>()
    return type to serializableNavType<T>(isNullableAllowed = type.isMarkedNullable)
}

Enter fullscreen mode Exit fullscreen mode

Serialization and deserialization are handled by the serializableNavType, which converts your class to and from a string representation.

Additionally, extremely important is the equals method that plays a crucial role in ensuring arguments are compared accurately during navigation.

By implementing these steps, you can seamlessly pass custom classes in a type-safe manner, unlocking the full potential of Jetpack Compose's Navigation library.

Happy coding!

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Sentry mobile image

Improving mobile performance, from slow screens to app start time

Based on our experience working with thousands of mobile developer teams, we developed a mobile monitoring maturity curve.

Read more

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay