What You Will Build

A set of colored cards that slide in from the right with staggered timing — each card waits 200ms before the next one starts. The cards use spring physics with low damping for a bouncy entrance. This pattern is used in onboarding flows, list reveals, and dashboard loading states.

Why This Pattern Matters

Staggered animations add polish and visual hierarchy to list content. This project teaches the Animatable API, coroutine-based sequencing with delay, and how to combine alpha and translation for slide-in-fade-in effects.

Step 1: Create Animatable Values for Each Item

@Composable
fun SequencedAnimationScreen() {
    val items = listOf(
        Color(0xFFFF6B6B), Color(0xFF4ECDC4),
        Color(0xFF45B7D1), Color(0xFFFFA07A),
        Color(0xFF98D8C8)
    )
    val visibilities = remember {
        items.map { Animatable(0f) }
    }
    var started by remember { mutableStateOf(false) }
    // ...
}

Step 2: Launch Staggered Animations

A LaunchedEffect watches the started flag. When triggered, it resets all values to 0 and then launches each animation with an increasing delay:

LaunchedEffect(started) {
    if (started) {
        // Reset all
        visibilities.forEach { it.snapTo(0f) }
        // Stagger launch
        visibilities.forEachIndexed { i, anim ->
            delay(200L * i)
            launch {
                anim.animateTo(1f, spring(dampingRatio = 0.5f))
            }
        }
    }
}

Step 3: Apply Alpha and Translation

items.forEachIndexed { i, color ->
    Box(
        Modifier
            .fillMaxWidth().height(60.dp)
            .graphicsLayer(
                alpha = visibilities[i].value,
                translationX = (1f - visibilities[i].value) * 300f
            )
            .clip(RoundedCornerShape(12.dp))
            .background(color)
            .padding(16.dp)
    ) {
        Text("Step ${i + 1}", color = Color.White)
    }
    Spacer(Modifier.height(12.dp))
}

Tips and Pitfalls

  • Animatable vs animateFloatAsState: Animatable gives you snapTo (instant reset) and imperative control. animateFloatAsState is simpler but harder to sequence.
  • launch {} inside forEachIndexed: Each animation runs concurrently after its delay. Without launch, they would run sequentially (each waiting for the previous to finish).
  • dampingRatio = 0.5f gives visible overshoot. Use 0.8f for subtle bounce or 1.0f for no bounce.
  • Reverse animation: To dismiss, reverse the loop order and animate to 0f.

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.