Flutter Animations Guide — AnimationController, Hero, Implicit, and Rive
Animations are the glue of great UX. Done right they feel natural; done wrong they tank performance. Here's a complete breakdown of Flutter's animation system.
AnimationController + Tween
Most flexible. Use for custom, orchestrated animations.
class FadeInWidget extends StatefulWidget {
final Widget child;
const FadeInWidget({required this.child});
@override
State<FadeInWidget> createState() => _State();
}
class _State extends State<FadeInWidget> with SingleTickerProviderStateMixin {
late final AnimationController _ctrl;
late final Animation<double> _opacity;
@override
void initState() {
super.initState();
_ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 600));
_opacity = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _ctrl, curve: Curves.easeOut),
);
_ctrl.forward();
}
@override
void dispose() { _ctrl.dispose(); super.dispose(); }
@override
Widget build(BuildContext context) => FadeTransition(opacity: _opacity, child: widget.child);
}
Staggered Animations
_slide = Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate(
CurvedAnimation(parent: _ctrl, curve: const Interval(0.0, 0.6, curve: Curves.easeOut)),
);
_opacity = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _ctrl, curve: const Interval(0.0, 0.5)),
);
_scale = Tween(begin: 0.9, end: 1.0).animate(
CurvedAnimation(parent: _ctrl, curve: const Interval(0.2, 0.8, curve: Curves.elasticOut)),
);
Hero Transitions
// List screen
Hero(tag: 'task-${task.id}', child: TaskCard(task: task))
// Detail screen
Hero(tag: 'task-${task.id}', child: TaskDetailHeader(task: task))
// Works out of the box with GoRouter
AnimatedSwitcher
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) => FadeTransition(
opacity: animation,
child: ScaleTransition(scale: animation, child: child),
),
child: isLoading
? const CircularProgressIndicator(key: ValueKey('loading'))
: TaskContent(key: ValueKey(task.id), task: task),
)
Implicit Animations (Simplest)
// AnimatedContainer
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: isExpanded ? 300 : 100,
color: isExpanded ? Colors.blue : Colors.grey,
)
// AnimatedOpacity
AnimatedOpacity(opacity: isVisible ? 1.0 : 0.0, duration: const Duration(milliseconds: 200), child: w)
Rive (Designer-authored)
Complex animations built in Rive editor, played in Flutter.
dependencies:
rive: ^0.13.0
RiveAnimation.network(
'https://cdn.rive.app/animations/vehicles.riv',
stateMachines: ['Drive'],
onInit: (artboard) {
final ctrl = StateMachineController.fromArtboard(artboard, 'Drive')!;
artboard.addController(ctrl);
(ctrl.findInput<double>('Speed')! as SMINumber).value = 50;
},
)
Performance Tips
- Wrap animated widgets in
RepaintBoundary - Prefer
FadeTransitionoverOpacitywidget (GPU layer) - Profile with
flutter run --profileto verify 60fps - Keep
constconstructors everywhere you can
Adding micro-animations increased average session time by 23% in my app.
What's your go-to Flutter animation pattern? Drop a comment below.
Top comments (0)