Skip to main content
Version: 1.x

Migrating to v2

Follow these steps to migrate to v2.

Please, if you find you had to do something which is not covered in this page or it's not clear, open an issue on here, here or write a message on kotlin slack #compose-destinations channel.
You can also quickly open a PR to improve documentation by using "Edit this page" button at the end of each page.

The ones that start with "If", you can skip if you don't meet that criteria. Otherwise expand it and go through the steps explained.

1. RootNavGraph annotation was renamed to RootGraph

Going forward, the convention for navigation graph annotations is to call them "SomethingGraph". This will generate "SomethingNavGraph" classes so we end up with different names. Classes in your code can be used for any number of things, so making their name the more specific one makes sense. While nav graph annotations will always be used in the same context, so just "Graph" suffix is enough.

2. Destination annotation now requires a navigation graph type argument.

If you're not using any custom navigation graphs, then you just need to add <RootGraph> to all @Destination usages and delete @RootNavGraph annotations. If you do have custom navigation graph annotations, then use the annotation name instead of "RootGraph". Example:

@Destination
@Composable
fun MyScreen() { /*...*/ }

@RootNavGraph(start = true)
@Destination
@Composable
fun MyStartScreen() { /*...*/ }

@MyGraph(start = true)
@Destination
@Composable
fun AnotherScreen() { /*...*/ }

3. Small package name and name changes

  • SingleModuleExtensions.kt file, no longer generated, so:

    • appDestination() -> destination()
    • appCurrentDestinationFlow -> currentDestinationFlow
    • appCurrentDestinationAsState() -> currentDestinationAsState()
    • startAppDestination -> startDestination
    • NavGraph -> NavGraphSpec
  • Destination.kt file is no longer generated, so: (note that replacements are not sealed)

    • sealed Destination -> DestinationSpec
    • sealed DirectionDestination -> DirectionDestinationSpec
    • sealed TypedDestination<T> -> TypedDestinationSpec<T>
  • rememberAnimatedNavHostEngine -> rememberNavHostEngine

  • navArgsDelegate parameter of @Destination renamed to navArgs

  • DestinationSpec<T> -> TypedDestinationSpec<T>

  • DestinationSpec<*> -> DestinationSpec

  • Some core APIs changed package name. If you have some red imports, try deleting the import and importing it again.

  • Generated code is now on com.ramcosta.composedestinations.generated package by default, instead of getting the common part of all destination's package names. If you use "moduleName", then that is suffixed to the above package name. So you will have to either set the current package name used on v1 by the library as your "codeGenPackageName" or, you'll need to re-import all Destinations.

To set the "codeGenPackageName" do this in your build.gradle(.kts) file

    ksp {
arg("compose-destinations.codeGenPackageName", "your.preferred.package.name") // replace package name!
}

4. Gradle setup: If using animations-core

Expand

animations-core is now bottom-sheet that you add alongside the core, not instead of.

So the core dependency should now always be:

ksp("io.github.raamcosta.compose-destinations:ksp:<version>")
implementation("io.github.raamcosta.compose-destinations:core:<version>")

And when using bottom sheet destinations, add also this one:

implementation("io.github.raamcosta.compose-destinations:bottom-sheet:<version>")

5. If using your own NavGraph annotations

Expand

Nav graph annotations now requires a parent navigation graph type argument.

Same as with @Destination<MyGraph>, custom navigation graph annotations now need to identify their parent nav graph in its type argument rather than annotating it.

@RootNavGraph
@NavGraph
annotation class MyGraph(
val start: Boolean = false
)

@MyGraph(start = true)
@NavGraph
annotation class AnotherGraph(
val start: Boolean = false
)
info

In the above example (both before and after), we're making a "MyGraph" that is nested in "Root" and a "AnotherGraph" that is nested in "MyGraph" and is its start route.

Navigation graphs with no parent (used to pass to DestinationsNavHost) should now be annotated with @NavHostGraph

This will generate a special NavHostGraph object which has slightly different characteristics (also the annotation has different parameters) then a normal navigation graph.
Also, default NavGraph parameter (which was usually used in these graphs) no longer exists, so you do have to be explicit in all @Destination and add the graph you want it to belong to (such as @Destination<MyGraph>).

@NavGraph(default = true)
annotation class MyMainGraph(
val start: Boolean = false
)

@MyMainGraph(start = true)
@Destination
@Composable
fun MyStartScreen() { /*...*/ }

@Destination //because default = true on MyMainGraph, when absent, Destination would belong to that graph
@Composable
fun AnotherScreen() { /*...*/ }

6. If you have set "compose-destinations.mode" on gradle ksp config

Expand

There's no "mode" anymore. Let's talk about each mode we had on v1:

"destinations"

You can set "generateNavGraphs" to false to have a similar output for that module

ksp {
arg("compose-destinations.generateNavGraphs", "false")
}

The output is slightly different, on v2 there's an object that contains the list of destinations instead of the field list being top level. You can import all destinations to certain nav graph defined in another module by using @ExternalModuleDestinations<ModuleDestinationsOutputObject> in companion object of that nav graph annotation class.

"navgraphs" & "singlemodule"

These existed to control generation of certain files that are no longer generated, so if you had these, you should be safe to just delete it.

7. If you have set "compose-destinations.useComposableVisibility" on gradle ksp config

Expand

The approach on v1 was not great because it wouldn't allow you to have internal Composable with public generated Destination, which is what may make sense on some multi module projects.

On v2, you can have any combination though since the annotation lets you control the visibility of the generated Destination. Example:

@Destination(
visibility = CodeGenVisibility.INTERNAL // or PUBLIC
)
@Composable
internal fun MyScreen() { /*...*/ }
note

You can create your own Destination annotations if you want to simplify this for the whole module, like:

@Destination<AnyGraph>(visibility = CodeGenVisibility.INTERNAL)
annotation class InternalDestination<T: Annotation>(
//copy all fields from Destination annotation you want usages of this one to be able to use, like
val route: String = COMPOSABLE_NAME,
val start: Boolean = false,
val navArgs: KClass<*> = Nothing::class,
val deepLinks: Array<DeepLink> = [],
val style: KClass<out DestinationStyle> = DestinationStyle.Default::class,
val wrappers: Array<KClass<out DestinationWrapper>> = []
)

8. If using route param Destination annotation

Expand

Generated destinations class name is now based of the route and not the Composable name. By default, routes are also set based on the composable name, so, by default, there should be no difference. However, if you're setting any manual routes, then the generated Destination object will likely have a different name. Given this, for those cases, you'll need to change the usages to the new name.

9. If using DestinationStyle.Runtime

Expand

This was removed. Depending on the reason for using it, we have new and better ways to solve the same issue:
If using it,

  • for changing style when using destinations from different modules
    • There's now a way to do that with annotation @ExternalDestination<MyDestinationFromAnotherModule>(style = MyStyle::class) Example:
// navigation module
@NavHostGraph // or @NavGraph<ParentGraph>
annotation class MyGraph {

@ExternalDestination<MyDestinationFromAnotherModule>(style = MyStyle::class)
companion object Includes
}
  • for other reasons
    • You can use MyDestination animateWith MyAnimation or passing in lambdas (as in the official lib) on manualComposableCallsBuilder param of DestinationsNavHost

Example:

DestinationsNavHost(
//...
) {
// works for default transitions on NavGraphs as well!
ProfileScreenDestination animateWith MyAnimatedStyle
// OR
ProfileScreenDestination.animateWith(
enterTransition = { /*...*/},
exitTransition = { /*...*/},
popEnterTransition = { /*...*/},
popExitTransition = { /*...*/}
)
}

10. If have any custom implementation of a DestinationStyle type

Expand

DestinationStyle is now an abstract class instead of an interface, so extending it requires "()"
Example:

object ProfileTransitions : DestinationStyle.Animated() {
//...
}

Besides, specifically for DestinationStyle.Animated, it has changed to have getters of lambdas rather than functions. This is because this way it results in more one to one with official APIs and it lets us keep some of them as null (instead of returning null). Example:

object ProfileTransitions : DestinationStyle.Animated {

override fun AnimatedContentTransitionScope<NavBackStackEntry>.enterTransition(): EnterTransition? {

return when (initialState.destination()) {
GreetingScreenDestination ->
slideInHorizontally(
initialOffsetX = { 1000 },
animationSpec = tween(700)
)
else -> null
}
}

override fun AnimatedContentTransitionScope<NavBackStackEntry>.exitTransition(): ExitTransition? {

return when (targetState.destination()) {
GreetingScreenDestination ->
slideOutHorizontally(
targetOffsetX = { -1000 },
animationSpec = tween(700)
)
else -> null
}
}
}

11. If you are defining Nav graph level animations on rememberAnimatedNavHostEngine or rememberNavHostEngine call

Expand

On v2 rememberNavHostEngine doesn't have these options. Instead, you can define default animations for navigation graphs at the annotation level or when importing a navigation graph from another module. Example:

@NavGraph<RootGraph>(
defaultTransitions = MyAnimatedDestinationStyle::class
)
annotation class MyGraph {

// or when importing
@ExternalNavGraph<AnotherModuleGraph>(
defaultTransitions = MyAnimatedDestinationStyle::class
)
companion object Includes

}

OR

If you need to have logic on your animations based on any runtime state, you can do so in the manualComposableCallsBuilder.
Example:

DestinationsNavHost(
//...
) {
MyGraph.animateWith(
enterTransition = { /*...*/ },
exitTransition = { /*...*/ },
popEnterTransition = { /*...*/ },
popExitTransition = { /*...*/ },
)
}

12. If you are still using the deprecated "navGraph" param of Destination annotation

Expand

Firstly, create an annotation class for each navigation graph you need, defining RootGraph as its parent, like this:

@NavGraph<RootGraph>
annotation class MyGraph

@NavGraph<RootGraph>
annotation class AnotherGraph

//...

Then, use those in your destinations.

@Destination<MyGraph>
@Composable
fun MyScreen() { /*...*/ }

@Destination<AnotherGraph>
@Composable
fun AnotherScreen() { /*...*/ }

13. If you are passing dependencies via dependenciesContainerBuilder param of DestinationsNavHost call

Expand
DestinationsNavHost(
//...
dependenciesContainerBuilder = {
dependency(aDependencyForEveryDestination)

dependency(ProfileScreenDestination) { anotherDependency }
dependency(ProfileScreenDestination) { someOtherDependency }

dependency(NavGraphs.settings) {
val parentEntry = remember(navBackStackEntry) {
navController.getBackStackEntry(NavGraphs.settings.route)
}
viewModel<SettingsViewModel>(parentEntry)
}

dependency(NavGraphs.settings) { anotherDependencyForSettingsGraph }
}
)
Expand

These APIs were removed on v2. There are new and better ways to achieve the same thing.

To import destinations from another module while setting different deep links

@NavGraph<RootGraph>
annotation class MyGraph {

@ExternalDestination<AnotherModuleDestination>(
deepLinks = [
DeepLink(uriPattern = "..."),
DeepLink(uriPattern = "...")
]
)
companion object Includes
}

To have a destination be part of multiple graphs

You can use multiple @Destination<Graph> in your Composable. Compose Destinations will generate a Destination for each Destination annotations, in this case, preffixing the Destination class name with the name of the graph.

@Destination<GraphOne>
@Destination<GraphTwo>
@Composable
fun MyScreen() { /*...*/ }

This would generate a GraphOneMyScreenDestination and a GraphTwoMyScreenDestination.

Multi module case

Note that a Destination generated on module A cannot be imported to multiple nav graphs of module B. If you find yourself wanting to do this, consider removing Compose Destinations from module A and just exposing a normal Composable. Then on module B you can create a Composable annotated with multiple Destination (like the example above) and just call module A's Composable.

15. If implementing NavGraphSpec in a multi module setup to aggregate graphs and destinations from other modules

Expand

There's no longer a need to do that, in fact, you shouldn't. Because doing it that way, won't let Compose Destinations know at compile time how the navigation graphs look like, and so, it cannot be as helpful.

So, on v2, if you want to include destinations or navigation graphs from other modules in a graph called "MainNavGraph", you should do:

@NavHostGraph
annotation class MainGraph {

@ExternalNavGraph<FeatureXNavGraph>
@ExternalModuleDestinations<SomeModuleDestinations>
@ExternalDestination<AnotherModuleDestination>
companion object Includes
}
Above example
  • Assumes you want to pass MainNavGraph to DestinationsNavHost, otherwise you could also use @NavGraph<RootGraph> instead of @NavHostGraph
  • On all ExternalNavGraph, ExternalDestination and ExternalModuleDestinations, you can call the annotation's constructor to override (or add depending on the field) stuff like deep links, wrappers, default animations etc.
  • The most common and better way to split navigation graphs on modules is to have feature modules expose a single NavGraph (internally it can contain multiple others) and import it here with @ExternalNavGraph as seen above. Other annotations on above example should be less common practices.