skills/flutter-animations/references/curves.md

567 lines
11 KiB
Markdown

# Curves Reference
Curves define the easing function for animations, controlling how values interpolate from start to end.
## Core Concept
A curve takes an input `t` (0.0 to 1.0, representing time) and outputs a value (typically 0.0 to 1.0, representing progress).
## Built-in Curves
### Linear
```dart
Curves.linear
```
No easing - straight line from 0 to 1. Use when you want constant speed.
### Ease (Sigmoid)
```dart
Curves.ease
Curves.easeIn
Curves.easeOut
Curves.easeInOut
```
**Curves.ease** - Slow start and end, fast middle (most common)
**Curves.easeIn** - Slow start, fast end (accelerating)
**Curves.easeOut** - Fast start, slow end (decelerating)
**Curves.easeInOut** - Slow start and end, fast middle (combination)
### Cubic (Stronger Ease)
```dart
Curves.easeInCubic
Curves.easeOutCubic
Curves.easeInOutCubic
```
Stronger easing than basic ease. More dramatic slow/fast zones.
### Elastic (Bouncy)
```dart
Curves.elasticIn
Curves.elasticOut
Curves.elasticInOut
```
**Curves.elasticIn** - Bounces in (starts behind, shoots forward)
**Curves.elasticOut** - Bounces out (goes past, comes back)
**Curves.elasticInOut** - Bounces in and out
Example: Good for playful UI, notifications
### Bounce
```dart
Curves.bounceIn
Curves.bounceOut
Curves.bounceInOut
```
**Curves.bounceIn** - Bounces in (less bouncy than elastic)
**Curves.bounceOut** - Bounces out (less bouncy than elastic)
**Curves.bounceInOut** - Bounces in and out
Example: Good for feedback animations
### Back (Overshoot)
```dart
Curves.backIn
Curves.backOut
Curves.backInOut
```
**Curves.backIn** - Backs up before going in
**Curves.backOut** - Backs up before coming out
**Curves.backInOut** - Backs up on both ends
Example: Good for revealing content
### Decelerate (Fast then Slow)
```dart
Curves.decelerate
```
Fast start, very slow end. Similar to easeOut but more dramatic.
### Fast Out Slow In
```dart
Curves.fastOutSlowIn
```
Very fast start, very slow middle, very fast end. Dramatic.
### Custom Curves in Flutter API
Flutter includes specialized curves:
```dart
// Material design curves
Curves.fastLinearToSlowEaseIn
Curves.slowMiddle
// Specific curves for certain transitions
Curves.ease
```
## Using Curves
### With Implicit Animation
```dart
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
child: const FlutterLogo(),
)
```
### With Explicit Animation (CurvedAnimation)
```dart
animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
```
### With Interval
```dart
animation = CurvedAnimation(
parent: _controller,
curve: const Interval(
0.0,
0.5,
curve: Curves.easeIn,
),
);
```
### With Curves.elasticOut
```dart
AnimatedContainer(
duration: const Duration(milliseconds: 1000),
curve: Curves.elasticOut,
transform: Matrix4.rotationZ(_rotated ? 0.5 : 0),
child: const FlutterLogo(),
)
```
## Custom Curves
### Creating a Custom Curve
```dart
import 'dart:math' as math;
class CustomCurve extends Curve {
@override
double transform(double t) {
// t is 0.0 to 1.0
// Return 0.0 to 1.0
return math.pow(t, 2); // Quadratic ease in
}
}
```
**Usage:**
```dart
AnimatedContainer(
curve: CustomCurve(),
duration: const Duration(milliseconds: 300),
width: _expanded ? 200 : 100,
child: const FlutterLogo(),
)
```
### Shake Curve
```dart
class ShakeCurve extends Curve {
@override
double transform(double t) {
return sin(t * pi * 2);
}
}
```
### Smooth Step Curve
```dart
class SmoothStep extends Curve {
final double steps;
const SmoothStep({this.steps = 5});
@override
double transform(double t) {
return (t * steps).floor() / steps;
}
}
```
### Exponential Curve
```dart
class ExponentialCurve extends Curve {
final double exponent;
const ExponentialCurve({this.exponent = 2});
@override
double transform(double t) {
return math.pow(t, exponent);
}
}
```
## Curve Composition
### Cubic Bezier
Flutter doesn't have built-in cubic bezier, but you can approximate:
```dart
class CubicBezier extends Curve {
final double p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y;
const CubicBezier({
required this.p0x,
required this.p0y,
required this.p1x,
required this.p1y,
required this.p2x,
required this.p2y,
required this.p3x,
required this.p3y,
});
@override
double transform(double t) {
// Simplified cubic bezier formula
final u = 1 - t;
return (u * u * u * p0y +
3 * u * u * t * p1y +
3 * u * t * t * p2y +
t * t * t * p3y);
}
}
```
### Combining Curves
```dart
class CombinedCurve extends Curve {
final Curve first;
final Curve second;
final double threshold;
const CombinedCurve({
required this.first,
required this.second,
required this.threshold,
});
@override
double transform(double t) {
if (t < threshold) {
return first.transform(t / threshold) * threshold;
}
return threshold +
second.transform((t - threshold) / (1 - threshold)) * (1 - threshold);
}
}
```
**Usage:**
```dart
AnimatedContainer(
curve: CombinedCurve(
first: Curves.easeIn,
second: Curves.easeOut,
threshold: 0.5,
),
duration: const Duration(milliseconds: 500),
width: _expanded ? 200 : 100,
child: const FlutterLogo(),
)
```
## Choosing the Right Curve
### Motion Design Guidelines
| Animation Type | Recommended Curve | Why |
|---------------|-------------------|-----|
| Fade in/out | easeInOut | Natural opacity change |
| Size change | easeOut | Decelerate feels natural for growth |
| Position slide | easeInOut | Smooth start and stop |
| Rotation | easeInOut | Natural angular acceleration |
| Color change | linear | Uniform color transition |
| Scale | elasticOut | Playful, bouncy feel |
| Reveal | backOut | Dramatic reveal effect |
| Loading | linear or ease | Consistent speed |
| Success | elasticOut | Celebratory feel |
### Platform Conventions
**iOS:**
- Prefers subtle curves (ease, easeInOut)
- Limited use of elastic/bounce
- Consistent with system animations
**Android:**
- Wider variety of curves
- More use of elastic/bounce for feedback
- Material design curves (easeIn, fastOutSlowIn)
**Web:**
- CSS-like curves (ease, ease-in, ease-out, ease-in-out)
- Limited use of complex curves
## Curve Combinations
### Multi-Stage Animation
```dart
// Stage 1: Ease in (0.0 - 0.5)
// Stage 2: Ease out (0.5 - 1.0)
animation1 = CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.5, curve: Curves.easeIn),
);
animation2 = CurvedAnimation(
parent: _controller,
curve: const Interval(0.5, 1.0, curve: Curves.easeOut),
);
```
### Staggered with Different Curves
```dart
// Item 1: Bouncy
item1Animation = CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.3, curve: Curves.elasticOut),
);
// Item 2: Smooth
item2Animation = CurvedAnimation(
parent: _controller,
curve: const Interval(0.1, 0.4, curve: Curves.easeInOut),
);
// Item 3: Linear
item3Animation = CurvedAnimation(
parent: _controller,
curve: const Interval(0.2, 0.5, curve: Curves.linear),
);
```
## Performance Considerations
### Curve Complexity
Simple curves (linear, ease) are faster:
- Fewer calculations
- Less GPU work
- Consistent timing
Complex curves (elastic, bounce) are slower:
- More calculations
- Potential for frame drops
- Variable timing
### Optimization Tips
- Use simpler curves on low-end devices
- Cache curve calculations if needed
- Test performance on target devices
- Profile with Flutter DevTools
## Debugging Curves
### Visualize Curve
```dart
class CurveVisualizer extends StatelessWidget {
final Curve curve;
const CurveVisualizer({super.key, required this.curve});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: _CurvePainter(curve: curve),
);
}
}
class _CurvePainter extends CustomPainter {
final Curve curve;
const CurvePainter({required this.curve});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..strokeWidth = 2
..style = PaintingStyle.stroke;
final path = Path();
for (double t = 0; t <= 1; t += 0.01) {
final x = t * size.width;
final y = size.height - (curve.transform(t) * size.height);
if (t == 0) {
path.moveTo(x, y);
} else {
path.lineTo(x, y);
}
}
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(_CurvePainter oldDelegate) => false;
}
```
### Print Curve Values
```dart
void printCurveValues(Curve curve) {
print('Curve: $curve');
for (double t = 0; t <= 1; t += 0.1) {
print('t=$t, output=${curve.transform(t)}');
}
}
// Usage
printCurveValues(Curves.easeInOut);
```
## Accessibility
### Respecting User Preferences
```dart
Curve getAdaptiveCurve(BuildContext context) {
if (MediaQuery.of(context).disableAnimations) {
return Curves.linear; // No easing when animations disabled
}
return Curves.easeInOut;
}
```
### Reduced Motion
```dart
AnimatedContainer(
duration: MediaQuery.of(context).disableAnimations
? Duration.zero
: const Duration(milliseconds: 300),
curve: getAdaptiveCurve(context),
width: _expanded ? 200 : 100,
child: const FlutterLogo(),
)
```
## Common Patterns
### Loading Spinner
```dart
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.linear, // Constant speed
);
```
### Success Checkmark
```dart
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.elasticOut, // Bouncy celebration
);
```
### Page Transition
```dart
_enterAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.easeIn, // Accelerate in
);
_exitAnimation = CurvedAnimation(
parent: _controller,
curve: Curves.easeOut, // Decelerate out
);
```
### Modal Popup
```dart
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeOutBack, // Dramatic reveal
);
```
## Curve Comparison
| Curve | Start | Middle | End | Feel |
|-------|-------|--------|-----|------|
| linear | Fast | Fast | Fast | Mechanical |
| ease | Slow | Fast | Slow | Natural |
| easeIn | Slow | Fast | Fast | Accelerating |
| easeOut | Fast | Slow | Slow | Decelerating |
| easeInOut | Slow | Fast | Slow | Smooth |
| elasticIn | Back | Fast | Normal | Bouncy start |
| elasticOut | Normal | Fast | Back | Bouncy end |
| bounceIn | Bounce | Fast | Normal | Bouncy start |
| bounceOut | Normal | Fast | Bounce | Bouncy end |
| backIn | Back | Fast | Normal | Overshoot start |
| backOut | Normal | Fast | Back | Overshoot end |
## Best Practices
### DO
- Use appropriate curves for animation type
- Test on real devices
- Consider platform conventions
- Respect accessibility settings
- Profile performance with DevTools
### DON'T
- Use elastic/bounce for everything (distracting)
- Over-combine curves (confusing motion)
- Ignore device performance
- Use same curve for all animations (boring)
- Forget to handle edge cases (t < 0 or t > 1)