Flutter Animations: A Practical Guide (with Copy-Paste Examples)
Animations make your app feel alive, guide attention, and communicate state changes. In Flutter, you get two broad styles:
-
Implicit animations – easy, one-liner style; great for simple property changes.
-
Explicit animations – full control via
AnimationController
; ideal for sequences, custom curves, and orchestrated motion.
Below is a hands-on tour with small, focused examples you can paste straight into your project.
1) Implicit Animations: Zero-Boilerplate Polish
Implicit widgets animate from their old value to the new value whenever they rebuild.
1.1 AnimatedContainer (size, color, radius)
1.2 AnimatedOpacity (fade things in/out)
1.3 AnimatedSwitcher (cross-fade / scale between children)
2) Explicit Animations: Full Control
Use an AnimationController
when you need to:
-
coordinate multiple properties
-
run sequences (staggered)
-
loop / reverse / fling
-
pause/resume, or hook into status callbacks
2.1 Scale + Opacity with AnimationController
3) Staggered Animation (Sequence with Intervals)
Staggering lets you chain parts of a motion for storytelling.
4) Page-to-Page Delight: Hero
Transition
Hero
links matching widgets across routes by tag
.
5) List & State Changes: AnimatedList
and Small Wins
5.1 AnimatedList (insert/remove with animation)
5.2 AnimatedIcon (built-in menu morph)
6) CustomPainter + Animation (for bespoke effects)
When you need something unique (progress rings, waves, charts), drive a CustomPainter
with an animation.
7) Putting It Together: A Mini Demo App with Tabs
Paste this into main.dart
to browse most examples quickly:
Tip: If you split files, ensure each widget is imported or kept in the same file for a quick demo.
8) Performance & UX Tips
-
Prefer implicit animations for simple property changes. They’re cheap and expressive.
-
Repaint boundaries: wrap complex animated children with
RepaintBoundary
when they don’t need to repaint ancestors. -
Short durations (150–400 ms) feel snappy for micro-interactions. Longer (600–1200 ms) for staged/staggered onboarding.
-
Use curves (
Curves.easeInOut
,easeOutCubic
,decelerate
) to match the feeling of weight and momentum. -
Avoid layout thrash: prefer
Transform
orOpacity
over repeated expensive layout changes for high-frequency animations. -
Dispose controllers in
dispose()
to prevent leaks. -
Test: For critical flows, assert animation end states in widget tests (e.g., pump frames with
pump(const Duration(...))
).
9) Quick Checklist When Adding Motion
-
What’s the purpose? (draw attention, confirm action, connect screens)
-
Is the duration appropriate for the context?
-
Does the motion communicate state (e.g., success/error) clearly?
-
Does it respect accessibility (not overly flashy; consider reduced motion settings)?
-
Are we animating the right properties (transform/opacity vs layout) for smoothness?