Styles and Animations
Compose Destinations allows you to define different styles for your Composable screens.
These "styles" describe the way the Composable enters and leaves the screen or how it is shown.
Each destination can have one of these styles:
See also: Common setup for Animations and Bottom Sheets
The way you can choose this is by passing the style
argument to the @Destination
annotation, example:
@Destination(style = DestinationStyle.Dialog::class)
@Composable
fun SomeScreen() { /*...*/ }
The class has to be a subclass of the sealed interface DestinationStyle
.
Default Style
As you probably have guessed, this is the style that's going to be applied to all Destinations that don't explicitly use another style.
This usually means that no animation and no other special style is used when navigating to it, but, if you're using Compose Destinations animations-core
, you will be able to change this default to actually have a custom animation. That is done through the arguments of rememberNavHostEngine
call, which you need to do to pass the returned AnimatedNavHostEngine
into DestinationsNavHost
.
Dialog Style
This style will make your Composable be displayed as a dialog above the previous screen.
DestinationStyle.Dialog
is also an interface with a DialogProperties
field. This enables you to create specific configurations of Dialogs subclassing it with an object
. Then you can pass that object's class to the style argument, for example:
object NonDismissableDialog : DestinationStyle.Dialog {
override val properties = DialogProperties(
dismissOnClickOutside = false,
dismissOnBackPress = false,
)
}
@Destination(style = NonDismissableDialog::class)
@Composable
fun SomeDialogScreen() { /*...*/ }
If you declare the style as style = DestinationStyle.Dialog::class
, then the default DialogProperties
will be used.
BottomSheet Style
This style requires you to use io.github.raamcosta.compose-destinations:animations-core
instead of the normal core
. There is a compile-time check that will throw an error if you don't.
The DestinationStyle.BottomSheet
is a simple object that you can use to set this style.
@Destination(style = DestinationStyleBottomSheet::class)
@Composable
fun ColumnScope.SomeBottomSheetScreen() { /*...*/ }
Notice the ColumnScope
receiver. This is optional if you're using the bottom sheet style and the reason is that the bottom sheet is internally placed inside a Column, so you can potentially do things that are only possible within that type of scope without needing another Column.
Just as if you were working with Accompanist Navigation-Material directly, you will need to wrap your top-most Composable with a ModalBottomSheetLayout
.
val navController = rememberNavController()
val bottomSheetNavigator = rememberBottomSheetNavigator()
navController.navigatorProvider += bottomSheetNavigator
ModalBottomSheetLayout(
bottomSheetNavigator = bottomSheetNavigator,
// other configuration for you bottom sheet screens, like:
sheetShape = RoundedCornerShape(16.dp),
) {
// ...
DestinationsNavHost(
navController = navController
// ...
)
}
Animated Style
The animated style enables you to define custom animations for your screen transitions. It requires the io.github.raamcosta.compose-destinations:animations-core
dependency. With this, you can then subclass DestinationStyle.Animated
interface with an object class and define the enter and exit transitions with normal animation APIs.
@OptIn(ExperimentalAnimationApi::class)
object ProfileTransitions : DestinationStyle.Animated {
override fun AnimatedContentScope<NavBackStackEntry>.enterTransition(): EnterTransition? {
//...
(expand to see the rest of ProfileTransitions.kt)
//...
return when (initialState.appDestination()) {
GreetingScreenDestination ->
slideInHorizontally(
initialOffsetX = { 1000 },
animationSpec = tween(700)
)
else -> null
}
}
override fun AnimatedContentScope<NavBackStackEntry>.exitTransition(): ExitTransition? {
return when (targetState.appDestination()) {
GreetingScreenDestination ->
slideOutHorizontally(
targetOffsetX = { -1000 },
animationSpec = tween(700)
)
else -> null
}
}
override fun AnimatedContentScope<NavBackStackEntry>.popEnterTransition(): EnterTransition? {
return when (initialState.appDestination()) {
GreetingScreenDestination ->
slideInHorizontally(
initialOffsetX = { -1000 },
animationSpec = tween(700)
)
else -> null
}
}
override fun AnimatedContentScope<NavBackStackEntry>.popExitTransition(): ExitTransition? {
return when (targetState.appDestination()) {
GreetingScreenDestination ->
slideOutHorizontally(
targetOffsetX = { 1000 },
animationSpec = tween(700)
)
else -> null
}
}
}
@Destination(style = ProfileTransitions::class)
@Composable
fun AnimatedVisibilityScope.ProfileScreen() { /*...*/ }
Notice the AnimatedVisibilityScope
receiver. This scope is available to all "non dialog" and "non bottom sheet" Composables in the nav graph once you're using the animations-core
dependency instead of the normal core
.
Animations / Bottom Sheet setup
-
If you want to use bottom sheet styled screens, you need to replace the normal
core
dependency with theanimations-core
(io.github.raamcosta.compose-destinations:animations-core
) in your module's build.gradle file. -
rememberNavHostEngine
has a parameter to define the default animations. This will make it so that all destinations with no specified style, will actually enter and exit as defined in that parameter. Besides, you can also override this default for specific nested navigation graphs. If you want to do that use thedefaultAnimationsForNestedNavGraph: Map<NavGraph, NestedNavGraphDefaultAnimations>
by mapping with navigation graphs to specific default animation parameters.DestinationsNavHost
contains acontentAlignment
parameter which you can also pass in this call as thenavHostContentAlignment
.
val navHostEngine = rememberNavHostEngine(
navHostContentAlignment = Alignment.TopCenter,
rootDefaultAnimations = RootNavGraphDefaultAnimations.ACCOMPANIST_FADING, //default `rootDefaultAnimations` means no animations
defaultAnimationsForNestedNavGraph = mapOf(
NavGraphs.settings to NestedNavGraphDefaultAnimations(
enterTransition = { fadeIn(animationSpec = tween(2000)) },
exitTransition = { fadeOut(animationSpec = tween(2000)) }
),
NavGraphs.otherNestedGraph to NestedNavGraphDefaultAnimations.ACCOMPANIST_FADING
) // all other nav graphs not specified in this map, will get their animations from the `rootDefaultAnimations` above.
)
Runtime Style
This style is just a marker for KSP task to keep the style of a Destination open at compile time. When using it, you need to manualy call the setter of the style on the corresponding generated Destination at runtime before calling DestinationsNavHost
. If you don't, it will crash with appropriate error message.
This feature is useful when you want to use style classes defined on different modules, which might be the only option when for example the style is Animated
and you need to reference Destinations from other modules.
Example:
@Destination(style = DestinationStyle.Runtime::class)
@Composable
fun YourScreen(
//...
) { /*...*/ }
And then wherever you call DestinationsNavHost
// YourScreenTransitions being an object that implements DestinationStyle.Animated (for example)
YourScreenDestination.style = YourScreenTransitions
DestinationsNavHost(
//...
)