Defining Destinations
The first step when using Compose Destinations is to mark the Composables you want to navigate to as "Destinations".
This will trigger the annotation processor to generate a Destination
object with all needed information to include the Composable in the navigation graph.
Destination annotation
To mark a Composable as a destination, use the @Destination
annotation:
@Destination
@Composable
fun WelcomeScreen() {
//...
}
As is, this will create an entry in the navigation graph with no navigation arguments (check how you declare those here), with route "welcome_screen".
There are a lot of ways to configure this destination. Lets start with an example where all those are used:
const val PROFILE_SCREEN_ROUTE = "profile/main"
const val PROFILE_NAV_GRAPH = "profile"
@Destination(
route = PROFILE_SCREEN_ROUTE,
start = true, // ❗️ DEPRECATED, read below!
navGraph = PROFILE_NAV_GRAPH, // ❗️ DEPRECATED, read below!
navArgsDelegate = ProfileScreenNavArgs::class,
deepLinks = [DeepLink(uriPattern = "https://destinationssample.com/$FULL_ROUTE_PLACEHOLDER")],
style = ProfileScreenTransitions::class
)
@Composable
fun ProfileScreen() {
//...
}
data class ProfileScreenNavArgs(
val arg1: Long,
val arg2: String
)
route
- This is a way to override the default route for this destination (which would be "profile_screen" in this case)- (DEPRECATED - read here)
start
- If true (default is false), marks this destination as the start of its navigation graph. In this case, since this would belong to the "profile" navigation graph (as defined in thenavGraph
) when navigating to that nested navigation graph, this screen would be shown. Each navigation graph needs one and only one start destination. A compile-time check is in place to ensure this. - (DEPRECATED - read here)
navGraph
- By default it will be "root". All destinations that do not specify other with this argument, will belong to the "root" navigation graph. In the case of the example, the destination would belong to the "profile" navigation graph which will be nested in the "root" one. All other destinations with the samenavGraph
argument would also belong to it. You can read more about nested navigation graphs here. navArgsDelegate
- a way to delegate the navigation arguments to some other data class. Read more heredeepLinks
- define deep links to this destination. Read more herestyle
- class that defines the style the destination is shown in or is animated when entering or leaving the screen. Read more here
Generated Destination object
Each annotated Composable will generate a Destination
object. These objects will automatically be used when DestinationsNavHost
is called.
Here is an example:
object ProfileScreenDestination : TypedDestination<ProfileScreenNavArgs> {
override operator fun invoke(navArgs: ProfileScreenNavArgs): Direction {
//...
}
operator fun invoke(
arg1: Long,
arg2: String,
): Direction {
//...
}
override val route: String = "profile_screen/{arg1}/{arg2}"
override val arguments get() = listOf(
navArgument("arg1") {
//...
},
navArgument("arg2") {
//...
}
)
override val deepLinks get() = listOf(
navDeepLink {
//...
}
)
override val style = //...
@Composable
override fun DestinationScope<ProfileScreenNavArgs>.Content(
dependenciesContainerBuilder: DependenciesContainerBuilder<ProfileScreenNavArgs>.() -> Unit
) {
//...
}
override fun argsFrom(navBackStackEntry: NavBackStackEntry): ProfileScreenNavArgs {
//...
}
override fun argsFrom(savedStateHandle: SavedStateHandle): ProfileScreenNavArgs {
//...
}
}
Some points about generated Destinations:
-
You can invoke these
Destinations
to create a validDirection
object you can then pass to the navigators. Read more about navigation here. -
Another reason to interact with
Destinations
is when using theargsFrom
methods which can be used to get the navigation arguments in the form of adata class
from either aNavBackStackEntry
or aSavedStateHandle
. -
If not using
navArgsDelegate
in the annotation, a generated class with nameNavArgs
will be nested in theDestination
. Either way, theargsFrom
method will return that data class containing your navigation arguments. -
The other fields/methods from
Destination
interface are used to build the navigation graph when callingDestinationsNavHost
. Usually, there are no reasons to use them in your code. If you feel like you need to use them it might be an indication that there are better ways to do it in the library. -
You can get the
Destination
correspondent to a certainNavBackStackEntry
with theappDestination
extensions which can be found inSingleModuleExtensions.kt
. Or, in case of multi module setup, justdestination()
. -
Besides, on single navigation module apps,
Destination
is a sealed interface, which opens possibilities for your logic to make sure is applied to all of them. A nice example of using it is to make some extension functions/properties, like this:
@get:StringRes
val Destination.title
get(): Int {
return when (this) {
GreetingScreenDestination -> R.string.greeting_screen
ProfileScreenDestination -> R.string.profile_screen
SettingsDestination -> R.string.settings_screen
FeedDestination -> R.string.feed_screen
ThemeSettingsDestination -> R.string.theme_settings_screen
}
}