Skip to content
intermediate Phase 4 · Jetpack Compose

Animation

Animate composables with animateAsState, AnimatedVisibility, transitions, and custom animations.

50m
2 problems
Topic Progress 0%

animateAsState and Simple Animations

Why Animate?

Static UI feels abrupt. Animations guide the user's eye, provide feedback, and make transitions feel natural. Compose provides high-level animation APIs that handle interpolation, frame timing, and recomposition.

animateAsState

The simplest way to animate is animateAsState. It animates a value from its current state to a target state.

@Composable
fun ColorAnimation() {
    var isBlue by remember { mutableStateOf(true) }

    val color by animateColorAsState(
        targetValue = if (isBlue) Color.Blue else Color.Red,
        animationSpec = tween(durationMillis = 1000),
        label = "color"
    )

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(color)
            .clickable { isBlue = !isBlue }
    )
}

When isBlue changes, the color animates smoothly over 1000ms instead of jumping.

Available animateAsState Variants

// Float animation
val size by animateFloatAsState(
    targetValue = if (expanded) 200f else 100f,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioMediumBouncy,
        stiffness = Spring.StiffnessLow
    ),
    label = "size"
)

// Dp animation
val padding by animateDpAsState(
    targetValue = if (expanded) 32.dp else 8.dp,
    animationSpec = tween(500),
    label = "padding"
)

// Int animation
val count by animateIntAsState(
    targetValue = targetCount,
    animationSpec = tween(300),
    label = "count"
)

AnimationSpec

AnimationSpec controls how the animation behaves:

// Linear: constant speed
linearAnimationSpec = tween(durationMillis = 300, easing = LinearEasing)

// EaseInOut: slow start, fast middle, slow end
easeInOutSpec = tween(durationMillis = 500, easing = FastOutSlowInEasing)

// Spring: physics-based overshoot
springSpec = spring(
    dampingRatio = Spring.DampingRatioMediumBouncy,
    stiffness = Spring.StiffnessLow
)

// Tween with delay
delayedSpec = tween<Float>(durationMillis = 400, delayMillis = 200)

Animated Content

AnimatedContent crossfades between different composables based on state:

@Composable
fun AnimatedCounter(count: Int) {
    AnimatedContent(
        targetState = count,
        transitionSpec = {
            if (targetState > initialState) {
                slideInVertically { -it } + fadeIn() togetherWith
                slideOutVertically { it } + fadeOut()
            } else {
                slideInVertically { it } + fadeIn() togetherWith
                slideOutVertically { -it } + fadeOut()
            }
        },
        label = "counter"
    ) { targetCount ->
        Text(
            text = "$targetCount",
            style = MaterialTheme.typography.displayLarge
        )
    }
}

AnimatedVisibility and Transitions

AnimatedVisibility

AnimatedVisibility animates a composable's appearance and disappearance:

@Composable
fun ExpandableCard(title: String, content: String) {
    var expanded by remember { mutableStateOf(false) }

    Card(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(text = title, style = MaterialTheme.typography.titleMedium)

            AnimatedVisibility(
                visible = expanded,
                enter = fadeIn() + expandVertically(),
                exit = fadeOut() + shrinkVertically()
            ) {
                Text(
                    text = content,
                    modifier = Modifier.padding(top = 8.dp)
                )
            }

            TextButton(onClick = { expanded = !expanded }) {
                Text(if (expanded) "Show Less" else "Show More")
            }
        }
    }
}

Enter and Exit Transitions

Combine multiple transitions:

// Enter transitions
fadeIn(tween(300))
slideInVertically(tween(300)) { it / 2 }
expandHorizontally(tween(400))

// Exit transitions
fadeOut(tween(300))
slideOutVertically(tween(300)) { it / 2 }
shrinkVertically(tween(400))

// Combine with +
val enterTransition = fadeIn(tween(300)) + slideInVertically(tween(300))
val exitTransition = fadeOut(tween(300)) + slideOutVertically(tween(300))

AnimatedVisibility(
    visible = showItem,
    enter = enterTransition,
    exit = exitTransition
) {
    ItemContent()
}

updateTransition

updateTransition synchronizes animations across multiple properties:

@Composable
fun BoxAnimation() {
    var currentState by remember { mutableStateOf(BoxState.Collapsed) }
    val transition = updateTransition(currentState, label = "box")

    val size by transition.animateDp(label = "size") { state ->
        when (state) {
            BoxState.Collapsed -> 100.dp
            BoxState.Expanded -> 200.dp
        }
    }

    val color by transition.animateColor(label = "color") { state ->
        when (state) {
            BoxState.Collapsed -> Color.Blue
            BoxState.Expanded -> Color.Red
        }
    }

    val cornerRadius by transition.animateDp(label = "corner") { state ->
        when (state) {
            BoxState.Collapsed -> 0.dp
            BoxState.Expanded -> 24.dp
        }
    }

    Box(
        modifier = Modifier
            .size(size)
            .background(color, RoundedCornerShape(cornerRadius))
            .clickable {
                currentState = when (currentState) {
                    BoxState.Collapsed -> BoxState.Expanded
                    BoxState.Expanded -> BoxState.Collapsed
                }
            }
    )
}

enum class BoxState { Collapsed, Expanded }

Infinite Animation

For looping animations like loading spinners:

@Composable
fun PulsingDot() {
    val infiniteTransition = rememberInfiniteTransition(label = "pulse")
    val scale by infiniteTransition.animateFloat(
        initialValue = 0.8f,
        targetValue = 1.2f,
        animationSpec = infiniteRepeatable(
            animation = tween(600, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ),
        label = "scale"
    )

    Box(
        modifier = Modifier
            .size(16.dp)
            .graphicsLayer { scaleX = scale; scaleY = scale }
            .background(Color.Blue, CircleShape)
    )
}

Quiz

1. What does animateAsState do?

Question 1 options

2. Which animation spec provides physics-based overshoot behavior?

Question 2 options

3. What is the difference between AnimatedVisibility and AnimatedContent?

Question 3 options

4. How do you create a looping animation in Compose?

Question 4 options

Flashcards

Question

What AnimationSpec types are available in Compose?

Answer

tween (interpolated over time), spring (physics-based), keyframes (keyframe-based), snap (instant), infiniteRepeatable (looping).

Question

When should you use updateTransition vs animateAsState?

Answer

updateTransition when multiple properties animate together in sync (e.g., size + color + shape). animateAsState for single independent values.

Question

What enter/exit transitions can AnimatedVisibility use?

Answer

fadeIn/fadeOut, slideIn/OutVertically/Horizontally, expand/shrinkVertically/Horizontally. Combine multiple with + operator.

Question

What is RepeatMode in infinite animations?

Answer

RepeatMode.Restart resets to initial value. RepeatMode.Reverse oscillates between initial and target values.

Revision Notes

Key Takeaways

  • 1. animateAsState is the simplest way to animate a single value change
  • 2. spring() provides natural physics-based motion without manual easing
  • 3. AnimatedVisibility handles show/hide with enter and exit transitions
  • 4. updateTransition synchronizes multiple animated properties for complex states
  • 5. rememberInfiniteTransition creates looping animations for loading indicators

Interview Tips

  • Explain the difference between tween and spring and when to use each
  • Discuss how Compose animations avoid jank by running on the composition thread
  • Know how to combine enter/exit transitions for natural visibility changes
  • Be ready to describe how you would animate a complex state change with updateTransition

Cheat Sheet

Animation Cheat Sheet

Simple Animations:

  • animateFloatAsState, animateColorAsState, animateDpAsState
  • Pass targetValue and animationSpec

AnimationSpecs:

  • tween(duration, easing) -- time-based
  • spring(dampingRatio, stiffness) -- physics-based
  • keyframes { at(0f); at(1f, 500ms) } -- keyframe-based
  • snap() -- instant change

Visibility:

  • AnimatedVisibility(visible, enter, exit)
  • Enter: fadeIn, slideIn, expand
  • Exit: fadeOut, slideOut, shrink

Multi-property:

  • updateTransition(state) -- sync multiple animations
  • transition.animateFloat/animateColor/animateDp

Looping:

  • rememberInfiniteTransition()
  • infiniteRepeatable(tween, RepeatMode.Reverse)

Content:

  • AnimatedContent(targetState) -- crossfade between composables