Staggered Animations

Low PriorityAsked in ~40% of mid-level interviews

3 min read

Animations

The AnimationController always runs from 0.0 to 1.0. With Interval(start, end, curve: ...), each child animation only "fires" inside its window:

Timeline:   0.0 ────────────────── 0.5 ────────────────── 1.0
Item 1:     [ animates here ]
Item 2:                       [ animates here ]
Item 3:                                         [ animates here ]

Each item's Interval produces a CurvedAnimation that maps the controller's 0..1 into the item's local 0..1 for the slice. Outside the slice, the value clamps at the boundary.


Code in action — list entrance

class _StaggeredListState extends State<StaggeredList>
    with SingleTickerProviderStateMixin {
  late final _ctrl = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 1500),
  );

  final items = const ['One', 'Two', 'Three', 'Four', 'Five'];

  @override
  void initState() {
    super.initState();
    _ctrl.forward();
  }

  @override
  void dispose() { _ctrl.dispose(); super.dispose(); }

  @override
  Widget build(BuildContext context) => ListView.builder(
    itemCount: items.length,
    itemBuilder: (_, i) {
      final start = i / items.length;
      final end   = (i + 1) / items.length;

      final slide = Tween<Offset>(
        begin: const Offset(1, 0),
        end:   Offset.zero,
      ).animate(CurvedAnimation(
        parent: _ctrl,
        curve: Interval(start, end, curve: Curves.easeOutCubic),
      ));

      final fade = CurvedAnimation(
        parent: _ctrl,
        curve: Interval(start, end),
      );

      return SlideTransition(
        position: slide,
        child: FadeTransition(opacity: fade, child: ListTile(title: Text(items[i]))),
      );
    },
  );
}

When to reach for staggered

SituationApproach
List items entering one after anotherSingle controller + per-item Interval
Multi-step UI reveal (icon → title → buttons)Single controller + 3 intervals
Two animations that partially overlapIntervals that share a range (e.g. 0.3–0.7 and 0.5–1.0)
Independent loops on different timelinesMultiple controllers (not staggered — concurrent)
Quick win, no controller setupflutter_staggered_animations package

Common mistakes to avoid

// ❌ Recomputing intervals + tweens inside builder every frame
itemBuilder: (_, i) {
  final slide = Tween(...).animate(CurvedAnimation(...));   // recreated every rebuild
  return SlideTransition(position: slide, ...);
}
// ✅ Cache them — compute once per item index when items change

// ❌ Using one controller per item
// → N tickers, complex orchestration, worse perf
// ✅ Single controller, intervals for each item

// ❌ Intervals that don't add up to a full sweep
// e.g. intervals [0.0, 0.3], [0.3, 0.6] for 5 items → last 2 items have no time
// ✅ Generic formula: start = i/N, end = (i+1)/N

// ❌ Mixing direction of travel without thought
// All items slide from the right + fade in → polished
// Random directions → chaotic, distracting

// ❌ Long total duration for many items
// 1500ms for 20 items = 75ms per item → choppy and rushed
// ✅ Tune total duration AND overlap so each item is perceivable (~150-200ms feels good)

Interview follow-ups

  1. Why use one controller for staggered animations instead of one per item? One controller is simpler to coordinate (one start/stop call), uses fewer tickers, and guarantees timing relationships stay in sync. Multiple controllers are needed only when items animate independently (different start times triggered by different events).

  2. What's the difference between staggered and sequential animations? Sequential = item 2 starts after item 1 ends. Staggered = item 2 can overlap item 1's tail. Most polished UI animations are staggered with some overlap — it feels natural and less robotic than strict sequence.

  3. When would you reach for the flutter_staggered_animations package? When you want a clean, declarative AnimationLimiter + AnimationConfiguration.staggeredList(...) API, especially for ListView/GridView entry animations. It hides the controller bookkeeping. For one-off custom choreography, hand-rolled intervals are still the right call.

  4. How do you stagger animations across items that appear at different times (e.g. paginated list)? Different problem from "one controller animating N items at startup." Each newly added item needs its own short-running animation when it appears. Use AnimatedSwitcher or AnimatedList with per-item transitions, or flutter_staggered_animations' AnimationLimiter which scopes timing to a single appearance pass.


How helpful was this content?

Please sign in to rate this article.