Phase 5 · Screen Behavior & QualityModule 47~30 min read

Optional · Animations in Compose

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

Optional further study

You can complete every required capstone without this topic. Return after the core phase lessons when you want to explore it.

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 },
    )
}
Focused example: keep your generated package and theme. Add the shown APIs to your Compose project using Android Studio imports.
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()
    }
}
Focused example: keep your generated package and theme. Add the shown APIs to your Compose project using Android Studio imports.

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.
Focused example: keep your generated package and theme. Add the shown APIs to your Compose project using Android Studio imports.

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)
Focused example: keep your generated package and theme. Add the shown APIs to your Compose project using Android Studio imports.

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 animation-duration settings and provide a reduced-motion experience for users who prefer less movement.

Practice: one small working feature

Optional: change a color with animateColorAsState when a Boolean changes.

Check your result

The text states the selected value even with system animations disabled.

Remember

Motion is optional polish. All course projects can be completed without animation APIs.

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.