Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Implement Flutter animations: implicit, explicit, hero, staggered, and physics-based with a decision tree guide.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/explicit.md
1# Explicit Animations Reference23Explicit animations provide full control using AnimationController, Tween, and animation status listeners.45## Core Components67### AnimationController89The heart of explicit animations. Manages animation lifecycle, value, and timing.1011```dart12late AnimationController _controller;1314@override15void initState() {16super.initState();17_controller = AnimationController(18duration: const Duration(seconds: 2),19vsync: this, // Required TickerProvider20lowerBound: 0.0, // Optional: default 0.021upperBound: 1.0, // Optional: default 1.022);23}2425@override26void dispose() {27_controller.dispose(); // ALWAYS dispose!28super.dispose();29}30```3132**Lifecycle methods:**3334```dart35// Start animation forward36_controller.forward();3738// Start animation backward39_controller.reverse();4041// Stop animation42_controller.stop();4344// Repeat animation45_controller.repeat();4647// Animate to specific value48await _controller.animateTo(0.5);4950// Reset to beginning51_controller.reset();5253// Animate with physics simulation54_controller.fling(velocity: 2.0);5556// Animate with custom simulation57_controller.animateWith(SpringSimulation(...));58```5960**Listening to changes:**6162```dart63_controller.addListener(() {64// Value changed - rebuild needed65setState(() {});66});6768_controller.addStatusListener((status) {69// Status changed70switch (status) {71case AnimationStatus.dismissed:72print('Animation dismissed (at 0)');73break;74case AnimationStatus.forward:75print('Animation moving forward');76break;77case AnimationStatus.reverse:78print('Animation moving backward');79break;80case AnimationStatus.completed:81print('Animation completed (at 1)');82break;83}84});85```8687### Tween8889Interpolates between begin and end values.9091```dart92// Simple tween93animation = Tween<double>(begin: 0, end: 300).animate(_controller);9495// Color tween96animation = ColorTween(97begin: Colors.red,98end: Colors.blue,99).animate(_controller);100101// Border radius tween102animation = BorderRadiusTween(103begin: BorderRadius.circular(4),104end: BorderRadius.circular(75),105).animate(_controller);106107// Rect tween108animation = RectTween(109begin: Rect.fromLTWH(0, 0, 100, 100),110end: Rect.fromLTWH(100, 100, 200, 200),111).animate(_controller);112```113114**Common Tweens:**115- `Tween<T>` - Generic tween (use for most types)116- `ColorTween` - Color interpolation117- `RectTween` - Rectangle interpolation118- `IntTween` - Integer interpolation119- `SizeTween` - Size interpolation120- `OffsetTween` - Offset interpolation121- `BorderRadiusTween` - Border radius interpolation122123### CurvedAnimation124125Applies a curve to transform animation values.126127```dart128animation = CurvedAnimation(129parent: _controller,130curve: Curves.easeInOut,131reverseCurve: Curves.easeIn, // Optional: different curve for reverse132);133```134135**With Interval:**136137```dart138animation = CurvedAnimation(139parent: _controller,140curve: const Interval(1410.25, // Start at 25% of controller1420.75, // End at 75% of controller143curve: Curves.easeInOut,144),145);146```147148### ReverseAnimation149150Reverses the parent animation.151152```dart153final forwardAnimation = Tween<double>(begin: 0, end: 1).animate(_controller);154final reverseAnimation = ReverseAnimation(forwardAnimation);155```156157## Patterns158159### AnimatedWidget Pattern160161Best for reusable animated widgets. Automatically handles rebuilds.162163```dart164class AnimatedLogo extends AnimatedWidget {165const AnimatedLogo({super.key, required Animation<double> animation})166: super(listenable: animation);167168@override169Widget build(BuildContext context) {170final animation = listenable as Animation<double>;171return Center(172child: Container(173height: animation.value,174width: animation.value,175child: const FlutterLogo(),176),177);178}179}180181// Usage182@override183Widget build(BuildContext context) {184return AnimatedLogo(animation: _animation);185}186```187188### AnimatedBuilder Pattern189190Best for complex widgets. Separates animation logic from widget logic.191192```dart193class GrowTransition extends StatelessWidget {194const GrowTransition({195required this.child,196required this.animation,197super.key,198});199200final Widget child;201final Animation<double> animation;202203@override204Widget build(BuildContext context) {205return Center(206child: AnimatedBuilder(207animation: animation,208builder: (context, child) {209return SizedBox(210height: animation.value,211width: animation.value,212child: child,213);214},215child: child,216),217);218}219}220221// Usage222@override223Widget build(BuildContext context) {224return GrowTransition(225animation: _animation,226child: const LogoWidget(),227);228}229```230231**Why pass child twice?**232The `child` parameter to `AnimatedBuilder` is passed to the `builder` function, allowing the child to be built once and reused, rather than rebuilt every animation frame.233234### Multiple Animations Pattern235236Animate multiple properties from single controller.237238```dart239class MultiPropertyAnimation extends AnimatedWidget {240const MultiPropertyAnimation({super.key, required Animation<double> animation})241: super(listenable: animation);242243static final _opacityTween = Tween<double>(begin: 0.1, end: 1);244static final _sizeTween = Tween<double>(begin: 0, end: 300);245static final _colorTween = ColorTween(246begin: Colors.red,247end: Colors.blue,248);249250@override251Widget build(BuildContext context) {252final animation = listenable as Animation<double>;253return Center(254child: Opacity(255opacity: _opacityTween.evaluate(animation),256child: Container(257height: _sizeTween.evaluate(animation),258width: _sizeTween.evaluate(animation),259decoration: BoxDecoration(260color: _colorTween.evaluate(animation),261borderRadius: BorderRadius.circular(12),262),263child: const FlutterLogo(),264),265),266);267}268}269```270271## Built-in Transitions272273Flutter provides pre-built transitions for common use cases.274275### FadeTransition276277```dart278FadeTransition(279opacity: _animation,280child: const FlutterLogo(),281)282```283284### ScaleTransition285286```dart287ScaleTransition(288scale: _animation,289child: const FlutterLogo(),290)291```292293### SlideTransition294295```dart296SlideTransition(297position: Tween<Offset>(298begin: Offset(0, 1),299end: Offset.zero,300).animate(_animation),301child: const FlutterLogo(),302)303```304305### SizeTransition306307```dart308SizeTransition(309sizeFactor: _animation,310axis: Axis.vertical,311child: const FlutterLogo(),312)313```314315### RotationTransition316317```dart318RotationTransition(319turns: _animation,320child: const FlutterLogo(),321)322```323324### DecoratedBoxTransition325326```dart327DecoratedBoxTransition(328decoration: DecorationTween(329begin: BoxDecoration(color: Colors.red),330end: BoxDecoration(color: Colors.blue),331).animate(_animation),332child: const FlutterLogo(),333)334```335336### PositionedTransition337338```dart339Stack(340children: [341PositionedTransition(342rect: RelativeRectTween(343begin: RelativeRect.fill(calculateRect(context, startPosition)),344end: RelativeRect.fill(calculateRect(context, endPosition)),345).animate(_animation),346child: const FlutterLogo(),347),348],349)350```351352## Animation Status Handling353354### Basic Status Loop355356```dart357_animation.addStatusListener((status) {358if (status == AnimationStatus.completed) {359_controller.reverse();360} else if (status == AnimationStatus.dismissed) {361_controller.forward();362}363});364```365366### One-way Animation367368```dart369_animation.addStatusListener((status) {370if (status == AnimationStatus.completed) {371_controller.stop(); // Or navigate, show dialog, etc.372}373});374```375376### Reset and Repeat377378```dart379_animation.addStatusListener((status) {380if (status == AnimationStatus.completed) {381_controller.reset();382_controller.forward();383}384});385```386387## Performance Best Practices388389### DO390391- Always dispose AnimationController392- Use `AnimatedWidget` or `AnimatedBuilder` instead of `setState()` in listeners393- Minimize widget rebuilds during animation394- Use static Tweens when possible (avoid recreating)395- Profile with Flutter DevTools396- Use `timeDilation` for debugging397398### DON'T399400- Forget to dispose controllers (memory leak)401- Call `setState()` in animation listeners unnecessarily402- Create complex widget trees inside animation builds403- Animate too many widgets simultaneously404- Use animation values directly in expensive operations405406### Performance Pattern: RepaintBoundary407408```dart409RepaintBoundary(410child: AnimatedBuilder(411animation: _controller,412builder: (context, child) {413return ExpensiveWidget();414},415),416)417```418419## Debugging420421### Slow Animations422423```dart424void main() {425timeDilation = 10.0; // 10x slower426runApp(MyApp());427}428```429430### Print Animation Values431432```dart433_controller.addListener(() {434print('Animation value: ${_controller.value}');435});436437_animation.addStatusListener((status) {438print('Animation status: $status');439});440```441442### Flutter DevTools Performance Overlay4434441. Run app in debug mode4452. Press 'p' to toggle performance overlay4463. Look for "GPU UI" and "GPU Raster" graphs4474. Aim for 60fps (16.67ms per frame)448449## Common Patterns450451### Fade In Then Slide Up452453```dart454_animation = Tween<double>(begin: 0, end: 1).animate(455CurvedAnimation(456parent: _controller,457curve: const Interval(0.0, 0.5, curve: Curves.easeIn),458),459);460461_slideAnimation = Tween<Offset>(462begin: Offset(0, 0.5),463end: Offset.zero,464).animate(465CurvedAnimation(466parent: _controller,467curve: const Interval(0.3, 1.0, curve: Curves.easeOut),468),469);470```471472### Bounce Effect473474```dart475_animation = CurvedAnimation(476parent: _controller,477curve: Curves.elasticOut,478);479```480481### Shake Effect482483```dart484class ShakeCurve extends Curve {485@override486double transform(double t) {487return sin(t * pi * 2);488}489}490491_animation = CurvedAnimation(492parent: _controller,493curve: ShakeCurve(),494);495```496497### Parallax Scrolling498499```dart500class ParallaxWidget extends StatelessWidget {501const ParallaxWidget({502required this.animation,503required this.child,504super.key,505});506507final Animation<double> animation;508final Widget child;509510@override511Widget build(BuildContext context) {512return AnimatedBuilder(513animation: animation,514builder: (context, child) {515return Transform.translate(516offset: Offset(0, animation.value * 50),517child: child,518);519},520child: child,521);522}523}524```525526## Comparison: Implicit vs Explicit527528| Feature | Implicit | Explicit |529|---------|----------|-----------|530| Controller | None needed | AnimationController required |531| Setup | Simple | More complex |532| Control | Limited (duration, curve) | Full (lifecycle, status, physics) |533| Multiple Properties | AnimatedContainer only | Unlimited |534| Performance | Good | Excellent (with patterns) |535| Reusability | Widget-based | Component-based |536| State Monitoring | Limited (onEnd) | Full (status listeners) |537538## When to Use Explicit Animations539540Use explicit animations when:541- Need full control over animation lifecycle542- Animating multiple properties543- Need to react to animation state changes544- Creating reusable animation components545- Need complex timing or sequencing546- Performance is critical547- Want physics-based or custom animations548549Use implicit animations when:550- Simple, one-off animations551- Animation triggered by state change552- No need for fine-grained control553- Want simple, declarative code554