Missing safe arguments generator for Compose Navigation

Overview

Safe Arguments Generator

Yet another attempt to add safe arguments to Compose Navigation.

Why

Since routes in Navigation Component don't support safe arguments out of the box as well as require a lot of boilerplate code, this library was meant to be made.

The main focus of the library is a simplified approach for declaring routes and arguments. What's more, this library doesn't force you to declare your screen composables in any particular way, which was a deal-breaker in several other existing safe-args projects.

Ok, show me the code

You declare all routes within a single interface like this:

@GenerateRoutes("Routes")
interface RouteActions {

    fun toMainScreen(): String
    fun toSecondScreen(id: Int): String
    // ...

}

The processor then generates a class with the name Routes as specified in the annotation, which can be used to declare your navigation within NavHost:

NavHost(navController, startDestination = Routes.toMainScreen()) {

    composable(Routes.MainScreen) {
        // your composable UI goes here
    }
    
    composableWithArgs(Routes.SecondScreen) { args ->
        // here you can use args.id
    }

    // ...
}

And in order to navigate between destinations, you just call:

navController.navigate(Routes.toSecondScreen(id = 123))

As simple as that.

There is of course a bunch of other useful features that will be explained further. But first...

Add the library to your project

First, add Kotlin Symbol Processing plugin to the module of your project where you are going to declare routes:

plugins {
    // ...
    id("com.google.devtools.ksp") version "1.5.31-1.0.1"
}

Then add dependencies:

dependencies {
    // ...
    ksp("dev.olshevski.safeargs:ksp:1.0.0")
    implementation("dev.olshevski.safeargs:api-compose:1.0.0")
}

In order for the project to discover newly generated files, add build/generated/ksp/... folders to the source sets like this:

android {
    // ...
    applicationVariants.all {
            val variantName = name
            sourceSets {
                named(variantName) {
                    kotlin.srcDir(file("build/generated/ksp/$variantName/kotlin"))
                }
            }
        }
    }
}

And that's it.

Alternative dependencies

If you for some reason want to use this library in a non-Compose application, or you just want to write your own custom NavGraphBuilder extensions you can use:

implementation("dev.olshevski.safeargs:api:1.0.0")

instead of api-compose. The api artifact contains only essential declarations without any Compose dependencies.

API

Declaring routes

You must declare routes inside an interface. The name of the interface is arbitrary. As it declares navigation actions, a good choice for the name may be RouteActions or similar one.

Inside the interface you declare methods starting with to prefix and returning String. Names of methods minus to prefix will become names of routes. For example, fun toMainScreen(): String will be interpreted as MainScreen route.

Every method may contain parameters of types Int, Long, Float, Boolean, String or String?. This is the limitation of Navigation Component, see Supported Argument Types.

Default values for parameters may be specified as well.

Then you annotate the interface with @GenerateRoutes specifying the name of the generated class, e.g. "Routes". This class will inherit the interface and provide implementations for every declared method. Every method will then be able to build a String representation of a route with arguments applied. Thus the required String return value.

For method toSecondScreen from the example above, the generated implementation will be:

override fun toSecondScreen(id: Int): String = "Routes_SecondScreen/$id"

Note that there is no limitation on the number of GenerateRoutes-annotated interfaces. Feel free to group and organize your routes however you want. The only requirement: no duplicate names for generated classes within the same namespace.

What else is generated

The route declarations themselves are constructed. They all inherit the base Route class and provide the pattern property and the list of NamedNavArguments which are both required to declare navigation routes within NavHost.

And of course, every generated route with parameters contains Args data class.

Let's see what a single declaration of fun toSecondScreen(id: Int): String generates:

object Second : Route<Second.Args>(
    "Routes_Second/{id}", listOf(
        navArgument(Args.Id) {
            type = NavType.IntType
        },
    )
) {
    override fun argsFrom(bundle: Bundle) = Args.from(bundle)

    override fun argsFrom(savedStateHandle: SavedStateHandle) =
        Args.from(savedStateHandle)

    data class Args(
        val id: Int
    ) {
        companion object {
            const val Id: String = "id"

            fun from(bundle: Bundle) = Args(
                bundle.getInt(Id),
            )

            fun from(savedStateHandle: SavedStateHandle) = Args(
                savedStateHandle[Id]!!,
            )
        }
    }
}

As you can see, a bunch of useful from methods are generated and constants for arguments. You may use them as you want. For example, argument constants may be useful for declaring deep-links.

Note: constructing arguments from SavedStateHandle is useful in ViewModels. For acquiring arguments from NavBackStackEntry use argsFrom(navBackStackEntry.arguments). navBackStackEntry.savedStateHandle may simply be null.

Nested navigation

This library was created with nested navigation in mind. To organize your routes hierarchically, you can simply declare more nested interfaces with @GenerateRoutes annotation:

@GenerateRoutes("Routes")
interface RouteActions {

    // ...

    @GenerateRoutes("Subroutes")
    interface SubrouteActions {

        fun toFirstScreen(): String
        fun toSecondScreen(): String

    }
    
}

This declaration will simply be treated as another group of routes, which are placed inside Routes.Subroutes object. In order for Routes.Subroutes to be a route itself, you need to add a navigation declaration with the same name:

@GenerateRoutes("Routes")
interface RouteActions {

    // ...

    fun toSubroutes(): String

    @GenerateRoutes("Subroutes")
    interface SubrouteActions {

        fun toFirstScreen(): String
        fun toSecondScreen(): String

    }
    
}

Now Routes.Subroutes is both a Route and a container for nested routes.

Extension methods

api artifacts adds a single extension method for now:

fun NavController.getBackStackEntry(route: Route<*>)

api-compose artifact adds convenient NavGraphBuilder extensions for using generated routes and easy acquiring of Args classes:

NavHost(navController, startDestination = Routes.toMainScreen()) {

    composable(Routes.MainScreen) {
        // you may get args manually here or in ViewModel
    }
    
    composableWithArgs(Routes.SecondScreen) { args ->
        // Args are acquired. 
        // Works well even if route doesn't have any parameters.
    }

    // same for Dialogs
    dialog(Routes.SomeDialog) { /* ... */ }
    dialogWithArgs(Routes.AnotherDialog) { args -> /* ... */ }

    // nested navigation
    navigation(Routes.Subroutes) { /* ... */ }

}

Sample

Please explore the sample module within the project for better understanding of capabilities of the library.

You might also like...
🚀 Android project template with Compose, MVVM, Hilt and Navigation
🚀 Android project template with Compose, MVVM, Hilt and Navigation

compose-android-template An Android project template with MVVM, Hilt, Navigation and Compose ✍️ Author 👤 theapache64 Twitter: @theapache64 Email: the

Small Android project demonstrating some navigation components for Jetpack Compose.
Small Android project demonstrating some navigation components for Jetpack Compose.

Small Android project demonstrating some navigation components for Jetpack Compose. Created this for presenting about this topic for a GDG meetup.

Odyssey it's a declarative multiplatform navigation library for Multiplatform Compose
Odyssey it's a declarative multiplatform navigation library for Multiplatform Compose

Odyssey Odyssey it's a declarative multiplatform navigation library for Multiplatform Compose 🚧 WARNING! It's an early preview, so you use it with yo

Kotlin, MVVM, Navigation Component, Hilt, Jetpack Compose, Retrofit2

What is this project? This course will replace my old java mvvm introduction: https://codingwithmitch.com/courses/rest-api-mvvm-retrofit2/. Watch the

[Tutorial] D-pad navigation in Jetpack Compose

dpad-compose D-pad navigation in Jetpack Compose The problem While Android is mostly used on touch devices, the operating system can also be used with

This library is a helper for the Android Compose Navigation routes

ComposableRoutes This library is a helper for the Android Compose Navigation routes: generates routes for the annotated screens provides helpers to re

Compose desktop navigation library

Navipose Compose desktop navigation library Features Now navipose supports basic screen navigation between few screens Examples At first you should cr

Android Sample Kotlin+ MVI + Jetpack compose + Coroutines + Retrofit + Hilt  + Room + Navigation component
Android Sample Kotlin+ MVI + Jetpack compose + Coroutines + Retrofit + Hilt + Room + Navigation component

MVIComposeSample Android Sample app to show user latest movies implementing MVI + Clean Architecture using kotlin & Jetpack compose following solid an

Android App made by Jetpack Compose Components with Kotlin, MVVM Pattern, Multi Module, Navigation, Hilt, Coroutines, Retrofit and cached data by Room
Android App made by Jetpack Compose Components with Kotlin, MVVM Pattern, Multi Module, Navigation, Hilt, Coroutines, Retrofit and cached data by Room

Android App made by Jetpack Compose Components with Kotlin, MVVM Pattern, Multi Module, Navigation, Hilt, Coroutines, Retrofit and cached data by Room

Releases(1.2.1)
Owner
Vitali Olshevski
10 years in Android Development, baby!
Vitali Olshevski
Lyricist - The missing I18N and I10N library for Jetpack Compose!

Jetpack Compose greatly improved the way we build UIs on Android, but not how we interact with strings. stringResource() works well, but doesn't benefit from the idiomatic Kotlin like Compose.

Adriel Café 269 Jan 2, 2023
This library will make it easier to pass arguments between screens in Jetpack Compose.

Compose Navigation This library will make it easier to pass arguments between screens in Jetpack Compose Setup allprojects { repositories { ...

Nguyen Van Tan 1 Oct 30, 2021
Small code generating library for safe Jetpack Compose navigation with no boilerplate.

Compose Destinations A KSP library to use alongside compose navigation. It reduces boilerplate code and is less error-prone since passing arguments be

Rafael Costa 1.9k Jan 5, 2023
Compose-navigation - Set of utils to help with integrating Jetpack Compose and Jetpack's Navigation

Jetpack Compose Navigation Set of utils to help with integrating Jetpack Compose

Adam Kobus 5 Apr 5, 2022
A project generator using Compose by JetBrains with a GUI

GradleProjectGenerator A project generator using Compose by JetBrains with a GUI

Francisco Solis 0 Dec 19, 2021
A Secure Password Generator designed with security precautions for the user's data

GenPass GenPass is a secure password generating application designed with a high level of security. It uses Java Security library to generate strong p

Joseph Olugbohunmi 1 Apr 29, 2022
Navigation-Compose - A sample to showcase Kotlin, MVVM, Hilt, Coroutines, StateFlow, Jetpack compose

Navigation-Compose A sample to showcase Kotlin, MVVM, Hilt, Coroutines, StateFlo

Mohammadali Rezaei 6 Jul 13, 2022
A Simple Blog App using Jetpack Compose, Flow, Navigation Compose, Room and Firebase

BlogCompose A Simple Blog App using Jetpack Compose, Flow, Navigation Compose, Room and Firebase Instructions Download your Firebase configuration fil

null 4 Oct 10, 2022
This repos one of the ways hows how to use Jetpack Compose Navigation along with Dagger 2

Dagger 2 and Jetpack Compose Integration This repository is about a way how to use Dagger 2 for projects which using Jetpack Compose. Here is an artic

Alexey Glukharev 10 Nov 16, 2022
Create Bottom Navigation Bar with Jetpack Compose

BottomNavigationBarComposeExample Create Bottom Navigation Bar with Jetpack Compose https://johncodeos.com/how-to-create-bottom-navigation-bar-with-je

JohnCodeos.com 31 Dec 24, 2022