chore: bring flutter client, docs, deploy and tools into monorepo
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,553 @@
|
||||
# Explicit Animations Reference
|
||||
|
||||
Explicit animations provide full control using AnimationController, Tween, and animation status listeners.
|
||||
|
||||
## Core Components
|
||||
|
||||
### AnimationController
|
||||
|
||||
The heart of explicit animations. Manages animation lifecycle, value, and timing.
|
||||
|
||||
```dart
|
||||
late AnimationController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
duration: const Duration(seconds: 2),
|
||||
vsync: this, // Required TickerProvider
|
||||
lowerBound: 0.0, // Optional: default 0.0
|
||||
upperBound: 1.0, // Optional: default 1.0
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose(); // ALWAYS dispose!
|
||||
super.dispose();
|
||||
}
|
||||
```
|
||||
|
||||
**Lifecycle methods:**
|
||||
|
||||
```dart
|
||||
// Start animation forward
|
||||
_controller.forward();
|
||||
|
||||
// Start animation backward
|
||||
_controller.reverse();
|
||||
|
||||
// Stop animation
|
||||
_controller.stop();
|
||||
|
||||
// Repeat animation
|
||||
_controller.repeat();
|
||||
|
||||
// Animate to specific value
|
||||
await _controller.animateTo(0.5);
|
||||
|
||||
// Reset to beginning
|
||||
_controller.reset();
|
||||
|
||||
// Animate with physics simulation
|
||||
_controller.fling(velocity: 2.0);
|
||||
|
||||
// Animate with custom simulation
|
||||
_controller.animateWith(SpringSimulation(...));
|
||||
```
|
||||
|
||||
**Listening to changes:**
|
||||
|
||||
```dart
|
||||
_controller.addListener(() {
|
||||
// Value changed - rebuild needed
|
||||
setState(() {});
|
||||
});
|
||||
|
||||
_controller.addStatusListener((status) {
|
||||
// Status changed
|
||||
switch (status) {
|
||||
case AnimationStatus.dismissed:
|
||||
print('Animation dismissed (at 0)');
|
||||
break;
|
||||
case AnimationStatus.forward:
|
||||
print('Animation moving forward');
|
||||
break;
|
||||
case AnimationStatus.reverse:
|
||||
print('Animation moving backward');
|
||||
break;
|
||||
case AnimationStatus.completed:
|
||||
print('Animation completed (at 1)');
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Tween
|
||||
|
||||
Interpolates between begin and end values.
|
||||
|
||||
```dart
|
||||
// Simple tween
|
||||
animation = Tween<double>(begin: 0, end: 300).animate(_controller);
|
||||
|
||||
// Color tween
|
||||
animation = ColorTween(
|
||||
begin: Colors.red,
|
||||
end: Colors.blue,
|
||||
).animate(_controller);
|
||||
|
||||
// Border radius tween
|
||||
animation = BorderRadiusTween(
|
||||
begin: BorderRadius.circular(4),
|
||||
end: BorderRadius.circular(75),
|
||||
).animate(_controller);
|
||||
|
||||
// Rect tween
|
||||
animation = RectTween(
|
||||
begin: Rect.fromLTWH(0, 0, 100, 100),
|
||||
end: Rect.fromLTWH(100, 100, 200, 200),
|
||||
).animate(_controller);
|
||||
```
|
||||
|
||||
**Common Tweens:**
|
||||
- `Tween<T>` - Generic tween (use for most types)
|
||||
- `ColorTween` - Color interpolation
|
||||
- `RectTween` - Rectangle interpolation
|
||||
- `IntTween` - Integer interpolation
|
||||
- `SizeTween` - Size interpolation
|
||||
- `OffsetTween` - Offset interpolation
|
||||
- `BorderRadiusTween` - Border radius interpolation
|
||||
|
||||
### CurvedAnimation
|
||||
|
||||
Applies a curve to transform animation values.
|
||||
|
||||
```dart
|
||||
animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeInOut,
|
||||
reverseCurve: Curves.easeIn, // Optional: different curve for reverse
|
||||
);
|
||||
```
|
||||
|
||||
**With Interval:**
|
||||
|
||||
```dart
|
||||
animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: const Interval(
|
||||
0.25, // Start at 25% of controller
|
||||
0.75, // End at 75% of controller
|
||||
curve: Curves.easeInOut,
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
### ReverseAnimation
|
||||
|
||||
Reverses the parent animation.
|
||||
|
||||
```dart
|
||||
final forwardAnimation = Tween<double>(begin: 0, end: 1).animate(_controller);
|
||||
final reverseAnimation = ReverseAnimation(forwardAnimation);
|
||||
```
|
||||
|
||||
## Patterns
|
||||
|
||||
### AnimatedWidget Pattern
|
||||
|
||||
Best for reusable animated widgets. Automatically handles rebuilds.
|
||||
|
||||
```dart
|
||||
class AnimatedLogo extends AnimatedWidget {
|
||||
const AnimatedLogo({super.key, required Animation<double> animation})
|
||||
: super(listenable: animation);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animation = listenable as Animation<double>;
|
||||
return Center(
|
||||
child: Container(
|
||||
height: animation.value,
|
||||
width: animation.value,
|
||||
child: const FlutterLogo(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedLogo(animation: _animation);
|
||||
}
|
||||
```
|
||||
|
||||
### AnimatedBuilder Pattern
|
||||
|
||||
Best for complex widgets. Separates animation logic from widget logic.
|
||||
|
||||
```dart
|
||||
class GrowTransition extends StatelessWidget {
|
||||
const GrowTransition({
|
||||
required this.child,
|
||||
required this.animation,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final Animation<double> animation;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, child) {
|
||||
return SizedBox(
|
||||
height: animation.value,
|
||||
width: animation.value,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GrowTransition(
|
||||
animation: _animation,
|
||||
child: const LogoWidget(),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Why pass child twice?**
|
||||
The `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.
|
||||
|
||||
### Multiple Animations Pattern
|
||||
|
||||
Animate multiple properties from single controller.
|
||||
|
||||
```dart
|
||||
class MultiPropertyAnimation extends AnimatedWidget {
|
||||
const MultiPropertyAnimation({super.key, required Animation<double> animation})
|
||||
: super(listenable: animation);
|
||||
|
||||
static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
|
||||
static final _sizeTween = Tween<double>(begin: 0, end: 300);
|
||||
static final _colorTween = ColorTween(
|
||||
begin: Colors.red,
|
||||
end: Colors.blue,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animation = listenable as Animation<double>;
|
||||
return Center(
|
||||
child: Opacity(
|
||||
opacity: _opacityTween.evaluate(animation),
|
||||
child: Container(
|
||||
height: _sizeTween.evaluate(animation),
|
||||
width: _sizeTween.evaluate(animation),
|
||||
decoration: BoxDecoration(
|
||||
color: _colorTween.evaluate(animation),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const FlutterLogo(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Built-in Transitions
|
||||
|
||||
Flutter provides pre-built transitions for common use cases.
|
||||
|
||||
### FadeTransition
|
||||
|
||||
```dart
|
||||
FadeTransition(
|
||||
opacity: _animation,
|
||||
child: const FlutterLogo(),
|
||||
)
|
||||
```
|
||||
|
||||
### ScaleTransition
|
||||
|
||||
```dart
|
||||
ScaleTransition(
|
||||
scale: _animation,
|
||||
child: const FlutterLogo(),
|
||||
)
|
||||
```
|
||||
|
||||
### SlideTransition
|
||||
|
||||
```dart
|
||||
SlideTransition(
|
||||
position: Tween<Offset>(
|
||||
begin: Offset(0, 1),
|
||||
end: Offset.zero,
|
||||
).animate(_animation),
|
||||
child: const FlutterLogo(),
|
||||
)
|
||||
```
|
||||
|
||||
### SizeTransition
|
||||
|
||||
```dart
|
||||
SizeTransition(
|
||||
sizeFactor: _animation,
|
||||
axis: Axis.vertical,
|
||||
child: const FlutterLogo(),
|
||||
)
|
||||
```
|
||||
|
||||
### RotationTransition
|
||||
|
||||
```dart
|
||||
RotationTransition(
|
||||
turns: _animation,
|
||||
child: const FlutterLogo(),
|
||||
)
|
||||
```
|
||||
|
||||
### DecoratedBoxTransition
|
||||
|
||||
```dart
|
||||
DecoratedBoxTransition(
|
||||
decoration: DecorationTween(
|
||||
begin: BoxDecoration(color: Colors.red),
|
||||
end: BoxDecoration(color: Colors.blue),
|
||||
).animate(_animation),
|
||||
child: const FlutterLogo(),
|
||||
)
|
||||
```
|
||||
|
||||
### PositionedTransition
|
||||
|
||||
```dart
|
||||
Stack(
|
||||
children: [
|
||||
PositionedTransition(
|
||||
rect: RelativeRectTween(
|
||||
begin: RelativeRect.fill(calculateRect(context, startPosition)),
|
||||
end: RelativeRect.fill(calculateRect(context, endPosition)),
|
||||
).animate(_animation),
|
||||
child: const FlutterLogo(),
|
||||
),
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
## Animation Status Handling
|
||||
|
||||
### Basic Status Loop
|
||||
|
||||
```dart
|
||||
_animation.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
_controller.reverse();
|
||||
} else if (status == AnimationStatus.dismissed) {
|
||||
_controller.forward();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### One-way Animation
|
||||
|
||||
```dart
|
||||
_animation.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
_controller.stop(); // Or navigate, show dialog, etc.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Reset and Repeat
|
||||
|
||||
```dart
|
||||
_animation.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
_controller.reset();
|
||||
_controller.forward();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Performance Best Practices
|
||||
|
||||
### DO
|
||||
|
||||
- Always dispose AnimationController
|
||||
- Use `AnimatedWidget` or `AnimatedBuilder` instead of `setState()` in listeners
|
||||
- Minimize widget rebuilds during animation
|
||||
- Use static Tweens when possible (avoid recreating)
|
||||
- Profile with Flutter DevTools
|
||||
- Use `timeDilation` for debugging
|
||||
|
||||
### DON'T
|
||||
|
||||
- Forget to dispose controllers (memory leak)
|
||||
- Call `setState()` in animation listeners unnecessarily
|
||||
- Create complex widget trees inside animation builds
|
||||
- Animate too many widgets simultaneously
|
||||
- Use animation values directly in expensive operations
|
||||
|
||||
### Performance Pattern: RepaintBoundary
|
||||
|
||||
```dart
|
||||
RepaintBoundary(
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
return ExpensiveWidget();
|
||||
},
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### Slow Animations
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
timeDilation = 10.0; // 10x slower
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
### Print Animation Values
|
||||
|
||||
```dart
|
||||
_controller.addListener(() {
|
||||
print('Animation value: ${_controller.value}');
|
||||
});
|
||||
|
||||
_animation.addStatusListener((status) {
|
||||
print('Animation status: $status');
|
||||
});
|
||||
```
|
||||
|
||||
### Flutter DevTools Performance Overlay
|
||||
|
||||
1. Run app in debug mode
|
||||
2. Press 'p' to toggle performance overlay
|
||||
3. Look for "GPU UI" and "GPU Raster" graphs
|
||||
4. Aim for 60fps (16.67ms per frame)
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Fade In Then Slide Up
|
||||
|
||||
```dart
|
||||
_animation = Tween<double>(begin: 0, end: 1).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: const Interval(0.0, 0.5, curve: Curves.easeIn),
|
||||
),
|
||||
);
|
||||
|
||||
_slideAnimation = Tween<Offset>(
|
||||
begin: Offset(0, 0.5),
|
||||
end: Offset.zero,
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: const Interval(0.3, 1.0, curve: Curves.easeOut),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
### Bounce Effect
|
||||
|
||||
```dart
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.elasticOut,
|
||||
);
|
||||
```
|
||||
|
||||
### Shake Effect
|
||||
|
||||
```dart
|
||||
class ShakeCurve extends Curve {
|
||||
@override
|
||||
double transform(double t) {
|
||||
return sin(t * pi * 2);
|
||||
}
|
||||
}
|
||||
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: ShakeCurve(),
|
||||
);
|
||||
```
|
||||
|
||||
### Parallax Scrolling
|
||||
|
||||
```dart
|
||||
class ParallaxWidget extends StatelessWidget {
|
||||
const ParallaxWidget({
|
||||
required this.animation,
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Animation<double> animation;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: animation,
|
||||
builder: (context, child) {
|
||||
return Transform.translate(
|
||||
offset: Offset(0, animation.value * 50),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Comparison: Implicit vs Explicit
|
||||
|
||||
| Feature | Implicit | Explicit |
|
||||
|---------|----------|-----------|
|
||||
| Controller | None needed | AnimationController required |
|
||||
| Setup | Simple | More complex |
|
||||
| Control | Limited (duration, curve) | Full (lifecycle, status, physics) |
|
||||
| Multiple Properties | AnimatedContainer only | Unlimited |
|
||||
| Performance | Good | Excellent (with patterns) |
|
||||
| Reusability | Widget-based | Component-based |
|
||||
| State Monitoring | Limited (onEnd) | Full (status listeners) |
|
||||
|
||||
## When to Use Explicit Animations
|
||||
|
||||
Use explicit animations when:
|
||||
- Need full control over animation lifecycle
|
||||
- Animating multiple properties
|
||||
- Need to react to animation state changes
|
||||
- Creating reusable animation components
|
||||
- Need complex timing or sequencing
|
||||
- Performance is critical
|
||||
- Want physics-based or custom animations
|
||||
|
||||
Use implicit animations when:
|
||||
- Simple, one-off animations
|
||||
- Animation triggered by state change
|
||||
- No need for fine-grained control
|
||||
- Want simple, declarative code
|
||||
Reference in New Issue
Block a user