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


    Android Developer

    Small and Modern GmbH
    Hamburg, Remote (Germany)
    • Full Time
    apply now

    Android Build Engineer

    Pinterest
    San Francisco, CA | Seattle, WA
    • Full Time
    apply now

    Senior Android Developer (Remote)

    Komoot
    Europe
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

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
I recently found a bug that would cause a crash in all the apps…
READ MORE
blog
Typically apps go from the navigation bar to the status bar. With the release…
READ MORE
blog

Edge-to-edge support for your Android app with Insetter

READ MORE
blog
Jetpack Compose is here to stay. There are tons of incentives and benefits in…
READ MORE

Leave a Reply

Your email address will not be published.

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

Menu