Blog Infos
Author
Published
Topics
, ,
Published
Is there even a problem with using Jetpack Compose Navigation?

Do not get me wrong — I don’t think Compose Navigation is a disaster, or even bad at all, or I wouldn’t be building something on top of it. But, I dothink that in terms of usability it can be greatly improved. It contains a lot of boilerplate and redundancy and the navigation arguments are not sent/received in a type-safe manner.
Let’s see an example.

@Composable
fun LoginScreen(
navigateToHome: () -> Unit
) {
/*...*/
Button (onClick = navigateToHome) {
/*...*/
}
@Composable
fun HomeScreen(
navigateToProfile: (String, Boolean) -> Unit,
navigateToSearch: (String?) -> Unit
) {
/*...*/
Button(onClick = { navigateToProfile("someId", true) }) {
/*...*/
Button(onClick = { navigateToSearch("query") }) {
/*...*/
}
@Composable
fun ProfileScreen(
id: String,
isEditable: Boolean = false
) { /*...*/ }
@Composable
fun SearchScreen(
query: String?
) { /*...*/ }
// Screen routes ____________________________
sealed class Screens(val route: String) {
object Login : Screens("login")
object Home : Screens("home")
object Profile : Screens("profile/{id}?isEditable={isEditable}")
object Search : Screens("search?query={query}")
}

3. You make the NavHost call

val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Screens.Login.route
) {
composable(route = Screens.Login.route) {
LoginScreen(
navigateToHome = {
navController.navigate(Screens.Home.route)
}
)
}
composable(route = Screens.Home.route) {
HomeScreen(
navigateToProfile = { id, isEditable ->
navController.navigate("profile/$id?isEditable=$isEditable")
},
navigateToSearch = { query ->
navController.navigate("search?query=$query")
}
)
}
composable(
route = Screens.Profile.route,
arguments = listOf(
navArgument("id") {
type = NavType.StringType
},
navArgument("isEditable") {
type = NavType.BoolType
defaultValue = false
}
)
) {
ProfileScreen(
id = it.arguments?.getString("id")!!,
isEditable = it.arguments?.getBoolean("isEditable")!!
)
}
composable(
route = Screens.Search.route,
arguments = listOf(
navArgument("query") {
type = NavType.StringType
nullable = true
}
)
) {
SearchScreen(query = it.arguments?.getString("query"))
}
}
Simple Compose Destinations usage

Compose Destinations takes advantage of annotation processing (using KSP) to improve the usability of Compose Navigation.
Let’s jump right into the same example as before, now with Compose Destinations:

@Destination(start = true)
@Composable
fun LoginScreen(
navigator: DestinationsNavigator
) {
/*...*/
Button (onClick = { navigator.navigate(HomeScreenDestination) }
/*...*/
}
@Destination
@Composable
fun HomeScreen(
navigator: DestinationsNavigator
) {
/*...*/
Button(
onClick = {
navigator.navigate(
ProfileScreenDestination(
id = "someId",
isEditable = true
)
)
}
) {
/*...*/
Button(onClick = { navigator.navigate(SearchScreenDestination(query = "query")) }) {
/*...*/
}
@Destination
@Composable
fun ProfileScreen(
id: String,
isEditable: Boolean = false
) { /*...*/ }
@Destination
@Composable
fun SearchScreen(
query: String?
) { /*...*/ }
// Navigation.kt file________________________
DestinationsNavHost(navGraph = NavGraphs.root)

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

,

Navigation superpowers at your fingertips

This talk will begin by the demonstration of a beautiful sample app built with Compose Mulitplatform and Appyx, complete with:
Watch Video

Navigation superpowers at your fingertips

Zsolt Kocsi
Principal Android engineer
Bumble

Navigation superpowers at your fingertips

Zsolt Kocsi
Principal Android en ...
Bumble

Navigation superpowers at your fingertips

Zsolt Kocsi
Principal Android enginee ...
Bumble

Jobs

Compose Destinations — FAQ

For this simple case, we clearly saw the advantages of using Compose Destinations. But for people that are used to Compose Navigation, some questions will surely pop up:

  • DestinationsNavigator
  • NavBackStackEntry
// Just as an example of something you might want to send to some destinations
val scaffoldState = rememberScaffoldState()
DestinationsNavHost(
navGraph = NavGraphs.root
) {
composable(SomeScreenDestination) { //this: DestinationScope<SomeScreenDestination.NavArgs>
SomeScreen(
arg1 = navArgs.arg1, // navArgs is a lazily evaluated `SomeScreenDestination.NavArgs` instance, field of `DestinationScope`
navigator = destinationsNavigator, // destinationsNavigator is a `DestinationsNavigator` (also lazily evaluated)
backStackEntry = navBackStackEntry, // navBackStackEntry is a `DestinationScope` field
scaffoldState = scaffoldState,
)
}
//All screens that don't need the scaffoldState don't need to be specified here
}
data class ProfileScreenNavArgs(
val id: Long,
val groupName: String?
)
//Then in the @Destination:
@Destination(
navArgsDelegate = ProfileScreenNavArgs::class
)
fun ProfileScreen() { /*...*/ }
But there’s more than meets the eye

Besides what we’ve seen so far, you get quite a few more goodies when deciding to use Compose Destinations.
To name a few:

  • Truly safe string navigation arguments.
    There are some edge cases to consider when you concatenate your routes with string arguments in the middle:
    – Empty strings;
    – Strings with characters like “&”, “/”, “?” and “%”.
    These can result in a badly formatted URI and crashes at runtime (or in receiving different strings from the ones sent) if you’re not really careful. Using this library means the strings you send will be the strings received on the other side, no need to worry about routes and their formatting.
Wrapping up

Compose Destinations is a library that I can now say with confidence will serve well from simple to more complex applications. It is a no-compromises choice when comparing it with “vanilla” Compose Navigation and the reason is simple: Compose Destinations builds on top of it and so you can do anything you could with it (and more) but just in a simpler and safer way.
I am absolutely certain that if you try it you will like it, and if you find a use case that can be improved or fixed you can submit an issue and I will be on top of it like a mad man 😄.
Who knows, maybe I can generate enough interest that someone will feel like contributing and helping me in this goal of making it easier for all developers navigating in Compose.
Either way, I will keep working on the library by providing updates, fixing issues, and adding features that make sense for its scope.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu