Type Safe arguments in Navigation Compose on Android
In this article we are going to see how Type Safe args in Navigation Compose eliminates the boilerplate, and how it achieves type safety and more.
Starting from new Navigation Compose release 2.8.0-alpha08 introduced a Type safe arguments for navigation, Previously we used routes (String) that define a unique id for the corresponding destination. This approach is error-prone and more boilerplate. We need to define its type, default value and nullable or not.
Although the new approach introduced the type safety but it doesn’t replace the core construction of the navigation, it always remains the same.
- NavHost: Host the current destination of the composable.
- NavController: Helps to navigate between the destinations, and manages the backstack.
- NavGraph: Displayes all possible navigation between destinations.
What’s the old approach?
For the basic argument to pass between screens, we need to write this tedious boilerplate code.
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "userScreen") {
composable(route = "userScreen") {
/* ... */
}
composable(
route = "userScreen?name={name}", // route (Error Prone)
arguments = listOf(
navArgument("name") {
type = NavType.StringType
defaultValue = "Mubarak"
nullable = true
}
)
) { backStackEntry ->
val userName = backStackEntry.arguments?.getString("name")
/* ... */
}
}
But starting from this 2.8.0-alpha08 release, we don’t need to write this error-prone code. So far, the current release is 2.8.0-beta02.
Checkout the latest release here releases.
Type safe manner
Let’s implement this with Type safe manner
- ) Declare necessary dependency in libs.versions.toml
[versions]
# ...
navigationCompose = "2.8.0-beta02" # currently is in beta
serializationVersion = "1.6.3"
# ...
[libraries]
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serializationVersion" }
[plugins]
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
2. ) build.gradle.kts(Module: app)
plugins {
/* ... */
alias(libs.plugins.kotlin.serialization)
/* ... */
}
dependencies {
// Compose Navigation
implementation(libs.androidx.navigation.compose)
// Kotlinx-Serialization
implementation(libs.kotlinx.serialization.json)
}
As, you might ask why kotlinx-serialization library, I will explain it in the moment
For this article i will develop a simple app that helps to demonstrate the new navigation compose.
This app contains two destinations MessageScreen and WishScreen MessageScreen contains single textfield and button when user enter their name when click go it displays the user entered text and append the text with Welcome string
In this app we passes Textfiled value to the WishScreen and we implement it with type safe manner.
Define NavHost
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Message) {
composable<Message> {
MessageScreen(text = text, onValueChange = { text = it }) {
navController.navigate(Greeting(name = text))
}
}
composable<Greeting> {
val greeting = it.toRoute<Greeting>()
WishScreen(greeting = greeting)
}
}
In this approach, instead of passing a route as a NavGraphBuilder.composable argument, we use kotlin data class and object
Note: Use object for. If composable that doesn’t require any arguments, if our composable needs an argument use data class and annotate with @Serializable
How it achieve type safety
As I mention previously instead of using route (String) we use data classes or object and annotate this with @Serializable
Serialization is a process of converting one type into another, so that it can be easily transferred.
Navigation compose use this library under the hood to make our nav arguments type safe.
@Serializable
object Message // for MessageScreen
@Serializable
data class Greeting( // for WishScreen
val name: String
// val age:Int? for optional argument we can use nullable type
)
How to get the passed argument value, We easily get that value by NavGraphBuilder.composable() function lambda returns a NavBackStackEntry, we use this to convert it into route by using toRoute() extension function.
composable<Greeting> { -> backStackEntry
val greeting = backStackEntry.toRoute<Greeting>()
WishScreen(greeting = greeting)
}
Here is the, the full code
As, you can see how easy and safe is to use new type safe navigation compose, now this currently is in beta, soonly it becomes in stable channel
Checkout the other code sample on my github repository:
This is all for this simple blog, I will hope you will like this blog
Signing off , Mubarak Native