IntermediateAdvanced Flutter

What are the main ways to build animations in Flutter?

Short Answer

Implicit animations (the `Animated*` widgets, like `AnimatedContainer`) animate a property change automatically with minimal code; explicit animations give full control via an `AnimationController` and `Tween`, for anything beyond a simple property tween.

Implicit animations are the fast path: wrap a value in `AnimatedContainer`, `AnimatedOpacity`, etc., change the target value (e.g. a new `color` or `width`), and Flutter animates the transition automatically over the given `duration` — no controller needed. This covers most simple "animate this property when it changes" cases.

Explicit animations give you precise control: you create an `AnimationController` (which drives a value over time, typically 0 to 1), wrap it in a `Tween` to map that range onto the actual values you want (e.g. a color or a rotation), and rebuild via `AnimatedBuilder` whenever the controller ticks. This is necessary for coordinating multiple animations, repeating/reversing animations, responding to gestures mid-animation, or anything an implicit widget doesn't expose a parameter for. There's also the newer declarative animation API for Flutter's animated transitions between routes/widgets, but controller-based explicit animation remains the foundation for complex custom motion.

Code Example

class _FadeInState extends State<FadeIn> with SingleTickerProviderStateMixin {
  late final _controller = AnimationController(
    vsync: this,
    duration: const Duration(milliseconds: 400),
  )..forward();
  late final _opacity = CurvedAnimation(parent: _controller, curve: Curves.easeIn);

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

  @override
  Widget build(BuildContext context) =>
      FadeTransition(opacity: _opacity, child: widget.child);
}

Common Mistakes

  • ×Forgetting to `dispose()` an `AnimationController`, leaking the ticker.
  • ×Reaching for explicit `AnimationController` setup when an `Animated*` implicit widget would do the job in two lines.