Type safety in Kotlin DSL and Navigation Compose

You can use built-in type safe APIs to provide compile-time type safety for your navigation graph. These APIs are available when your app uses the Navigation Compose or Navigation Kotlin DSL. They are available as of Navigation 2.8.0.

These APIs are equivalent to what Safe Args provides to navigation graphs built using XML.

Define routes

To use type-safe routes in Compose, you first need to define serializable classes or objects that represent your routes.

To define serializable objects use @Serializable annotation provided by the Kotlin Serialization plugin. This plugin can be added to your project by adding these dependencies.

Use the following rules to decide what type to use for your route:

  • Object: Use an object for routes without arguments.
  • Class: Use a class or data class for routes with arguments.
  • KClass<T>: Use if you don't need to pass arguments, such as a class without parameters, or a class where all parameters have default values
    1. For example: Profile::class

In all cases the object or class must be serializable.

For example:

// Define a home route that doesn't take any arguments
@Serializable
object Home

// Define a profile route that takes an ID
@Serializable
data class Profile(val id: String)

Build your graph

Next, you need to define your navigation graph. Use the composable() function to define composables as destinations in your navigation graph.

NavHost(navController, startDestination = Home) {
     composable<Home> {
         HomeScreen(onNavigateToProfile = { id ->
             navController.navigate(Profile(id))
         })
     }
     composable<Profile> { backStackEntry ->
         val profile: Profile = backStackEntry.toRoute()
         ProfileScreen(profile.id)
     }
}

Observe the following in this example:

  • composable() takes a type parameter. That is, composable<Profile>.
  • Defining the destination type is a more robust approach than passing a route string as in composable("profile").
  • The route class defines the type of each navigation argument, as in val id: String, so there's no need for NavArgument.
  • For the profile route, the toRoute() extension method recreates the Profile object from the NavBackStackEntry and its arguments.

For more information on how to design your graph in general, see the Design your Navigation graph page.

Finally, you can navigate to your composable using the navigate() function by passing in the instance of the route:

navController.navigate(Profile(id = 123))

This navigates the user to the composable<Profile> destination in the navigation graph. Any navigation arguments, such as id, can be obtained by reconstructing Profile using NavBackStackEntry.toRoute and reading its properties.

Additional resources