Phase 5 · Advanced ComposeModule 26~30 min read

Animations in Compose

Bring your UI to life with Compose's concise, powerful animation APIs.

What you'll learn

Motion is what makes an app feel alive and responsive — a button that pulses, a panel that slides, colors that ease instead of snapping. Compose's animation APIs are unusually elegant: because the UI is already driven by state, animating is often as simple as swapping a value for an animated value.

By the end you'll be able to:

  • Animate single values with animate*AsState
  • Animate appearance with AnimatedVisibility and Crossfade
  • Coordinate several values with updateTransition
  • Tune motion with spring and tween specs

animate*AsState

The workhorse. There's an animate*AsState for each common type — animateDpAsState, animateColorAsState, animateFloatAsState. You give it a targetValue; whenever that target changes, it animates from the current value to the new one automatically, and you just read the animated value:

LikeButton.kt
@Composable
fun LikeButton() {
    var liked by remember { mutableStateOf(false) }
    // The color animates smoothly whenever 'liked' flips — no manual tweening.
    val color by animateColorAsState(
        targetValue = if (liked) Color.Red else Color.Gray,
        label = "likeColor",
    )
    Icon(
        Icons.Default.Favorite,
        contentDescription = "Like",
        tint = color,
        modifier = Modifier.clickable { liked = !liked },
    )
}
9:41
♡→♥
animateColorAsState — how this renders on a device

Key idea

This is the whole philosophy: you never write frame-by-frame animation code. You change a target value, and the animated state eases toward it. Declarative UI makes declarative motion — describe the destination, let Compose animate the journey.

AnimatedVisibility & Crossfade

To animate a composable entering or leaving, wrap it in AnimatedVisibility — it fades and expands in, then shrinks and fades out, all configurable. To swap between two composables with a fade, use Crossfade:

Reveal.kt
AnimatedVisibility(visible = expanded) {
    Text("Extra details revealed with a smooth expand/fade.")
}

// Swap between two composables with a fade:
Crossfade(targetState = screen, label = "screen") { current ->
    when (current) {
        Screen.Home -> HomeContent()
        Screen.Detail -> DetailContent()
    }
}

Multi-value transitions

When several properties should animate together off one state change — a card that grows its elevation and padding when selected — use updateTransition. It drives multiple animations from a single source state, keeping them perfectly in sync:

CardTransition.kt
val transition = updateTransition(targetState = selected, label = "card")
val elevation by transition.animateDp(label = "elevation") { if (it) 12.dp else 2.dp }
val padding by transition.animateDp(label = "padding") { if (it) 24.dp else 16.dp }
// elevation and padding animate together, in sync.

Tuning with animationSpec

Every animation takes an animationSpec that shapes how it moves. A spring gives natural, physics-based motion (optionally bouncy) and is the Compose default; a tween runs for a fixed duration with an easing curve. Choose spring for most UI — it feels organic and adapts if the target changes mid-flight:

Spec.kt
val offset by animateDpAsState(
    targetValue = target,
    animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy),
    label = "offset",
)
// Or a fixed duration: tween(durationMillis = 300, easing = FastOutSlowInEasing)

Tip

Animate with purpose. Motion should communicate — show that something appeared, guide the eye, confirm a tap — not decorate. Keep durations short (200–300ms) and respect the system's "reduce motion" accessibility setting for users who prefer less movement.

Recap & quick check

Key takeaways

  • animate*AsState animates a single value to a new target automatically — you just read the animated value.
  • AnimatedVisibility animates composables entering/leaving; Crossfade fades between two composables.
  • updateTransition coordinates several animations from one source state, keeping them in sync.
  • animationSpec shapes the motion: spring (natural, physics-based, the default) or tween (fixed duration + easing).
  • Animate with purpose and keep it short; respect reduce-motion accessibility settings.

Quick check

1. How do you smoothly animate a color when a boolean flips?

2. Which composable animates content appearing and disappearing?

3. You need elevation and padding to animate together from one state. Use…

4. What does animationSpec control?

5. Which spec gives natural, physics-based motion and is the Compose default?

Your UI moves beautifully. Next we go beyond the built-in layouts — building custom layouts and drawing on the Canvas.