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:
- v1
- v2
- diff
@Destination
@Composable
fun MyScreen() { /*...*/ }
@RootNavGraph(start = true)
@Destination
@Composable
fun MyStartScreen() { /*...*/ }
@MyGraph(start = true)
@Destination
@Composable
fun AnotherScreen() { /*...*/ }
@Destination<RootGraph>
@Composable
fun MyScreen() { /*...*/ }
@Destination<RootGraph>(start = true)
@Composable
fun MyStartScreen() { /*...*/ }
@Destination<MyGraph>(start = true)
@Composable
fun AnotherScreen() { /*...*/ }
-@Destination
+@Destination<RootGraph>
@Composable
fun MyScreen() { /*...*/ }
-@RootNavGraph(start = true)
-@Destination
+@Destination<RootGraph>(start = true)
@Composable
fun MyStartScreen() { /*...*/ }
-@MyGraph(start = true)
-@Destination
+@Destination<MyGraph>(start = true)
@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>
- sealed
-
rememberAnimatedNavHostEngine
->rememberNavHostEngine
-
navArgsDelegate
parameter of@Destination
renamed tonavArgs
-
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.
- v1
- v2
@RootNavGraph
@NavGraph
annotation class MyGraph(
val start: Boolean = false
)
@MyGraph(start = true)
@NavGraph
annotation class AnotherGraph(
val start: Boolean = false
)
@NavGraph<RootGraph>
annotation class MyGraph
@NavGraph<MyGraph>(start = true)
annotation class AnotherGraph
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>
).
- v1
- v2
@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() { /*...*/ }
@NavHostGraph
annotation class MyMainGraph
@Destination<MyMainGraph>(start = true)
@Composable
fun MyStartScreen() { /*...*/ }
@Destination<MyMainGraph>
@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() { /*...*/ }
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:
- There's now a way to do that with annotation
// 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) onmanualComposableCallsBuilder
param of DestinationsNavHost
- You can use
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:
- v1
- v2
- diff
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
}
}
}
object ProfileTransitions : DestinationStyle.Animated() {
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition? = {
when (initialState.destination()) {
GreetingScreenDestination ->
slideInHorizontally(
initialOffsetX = { 1000 },
animationSpec = tween(700)
)
else -> null
}
}
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition? = {
when (targetState.destination()) {
GreetingScreenDestination ->
slideOutHorizontally(
targetOffsetX = { -1000 },
animationSpec = tween(700)
)
else -> null
}
}
}
- object ProfileTransitions : DestinationStyle.Animated {
+ object ProfileTransitions : DestinationStyle.Animated() {
- override fun AnimatedContentTransitionScope<NavBackStackEntry>.enterTransition(): EnterTransition? {
-
- return when (initialState.destination()) {
+ override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition? = {
+ when (initialState.destination()) {
GreetingScreenDestination ->
slideInHorizontally(
initialOffsetX = { 1000 },
}
}
- override fun AnimatedContentTransitionScope<NavBackStackEntry>.exitTransition(): ExitTransition? {
-
- return when (targetState.destination()) {
+ override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition? = {
+ when (targetState.destination()) {
GreetingScreenDestination ->
slideOutHorizontally(
targetOffsetX = { -1000 },
}
}
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
- v1
- v2
- diff
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 }
}
)
DestinationsNavHost(
//...
dependenciesContainerBuilder = {
dependency(aDependencyForEveryDestination)
destination(ProfileScreenDestination) {
dependency(anotherDependency)
dependency(someOtherDependency)
}
navgraph(NavGraphs.settings) {
val parentEntry = remember(navBackStackEntry) {
navController.getBackStackEntry(NavGraphs.settings.route)
}
dependency(viewModel<SettingsViewModel>(parentEntry))
dependency(anotherDependencyForSettingsGraph)
}
}
)
DestinationsNavHost(
//...
dependenciesContainerBuilder = {
dependency(aDependencyForEveryDestination)
-
- dependency(ProfileScreenDestination) { anotherDependency }
- dependency(ProfileScreenDestination) { someOtherDependency }
-
- dependency(NavGraphs.settings) {
+
+ destination(ProfileScreenDestination) {
+ dependency(anotherDependency)
+ dependency(someOtherDependency)
+ }
+
+ navGraph(NavGraphs.settings) {
val parentEntry = remember(navBackStackEntry) {
navController.getBackStackEntry(NavGraphs.settings.route)
}
- viewModel<SettingsViewModel>(parentEntry)
- }
-
- dependency(NavGraphs.settings) { anotherDependencyForSettingsGraph }
+
+ dependency(viewModel<SettingsViewModel>(parentEntry))
+ dependency(anotherDependencyForSettingsGraph)
+ }
}
)
14. If using "routedIn", "within" or "withDeepLinks" APIs
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
.
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
}
- Assumes you want to pass MainNavGraph to DestinationsNavHost, otherwise you could also use
@NavGraph<RootGraph>
instead of@NavHostGraph
- On all
ExternalNavGraph
,ExternalDestination
andExternalModuleDestinations
, 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.