Implicit Animations Reference
Implicit animations automatically handle animations when widget properties change. No AnimationController or explicit state management needed.
Core Concept
Implicitly animated widgets extend ImplicitlyAnimatedWidget. When you change a property (color, size, etc.), the widget automatically animates to the new value using a specified duration and curve.
Available Widgets
AnimatedContainer
Animates multiple properties simultaneously.
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _expanded ? 200 : 100,
height: _expanded ? 200 : 100,
decoration: BoxDecoration(
color: _expanded ? Colors.blue : Colors.red,
borderRadius: BorderRadius.circular(_expanded ? 20 : 8),
),
child: const FlutterLogo(),
)Animatable properties:
width,height- Sizecolor- Background colordecoration- BoxDecoration (includes border, shadow, image, etc.)padding- Paddingmargin- Margin (via decoration)transform- Matrix4 transformation
AnimatedOpacity
Fades a widget in and out.
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
onEnd: () => print('Animation complete'),
child: const Text('Hello'),
)AnimatedPadding
Animates padding changes.
AnimatedPadding(
padding: EdgeInsets.all(_padded ? 16.0 : 8.0),
duration: const Duration(milliseconds: 300),
curve: Curves.ease,
child: const Card(child: Text('Padded content')),
)AnimatedPositioned
Animates position within a Stack.
Stack(
children: [
AnimatedPositioned(
top: _top ? 0 : 100,
left: _left ? 0 : 100,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
child: const FlutterLogo(),
),
],
)AnimatedAlign
Animates alignment changes.
AnimatedAlign(
alignment: _aligned ? Alignment.topLeft : Alignment.bottomRight,
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
child: const Text('Align me'),
)AnimatedContainer (Multiple Properties)
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
transform: Matrix4.rotationZ(_rotated ? 0.5 : 0),
child: const FlutterLogo(),
)TweenAnimationBuilder
Custom tween animation without creating a custom widget.
TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0, end: 1),
duration: const Duration(seconds: 1),
curve: Curves.easeInOut,
builder: (context, value, child) {
return Opacity(
opacity: value,
child: Transform.scale(
scale: value,
child: child,
),
);
},
child: const FlutterLogo(),
)AnimatedSwitcher
Cross-fades between two widgets.
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition(
opacity: animation,
child: child,
);
},
child: _showFirst
? const Text('First')
: const Text('Second'),
)With custom size transition:
AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation) {
return SizeTransition(
sizeFactor: animation,
axis: Axis.vertical,
child: child,
);
},
child: _expanded
? const Text('Expanded content')
: const Text('Collapsed content'),
)AnimatedDefaultTextStyle
Animates text style changes.
AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 300),
style: TextStyle(
fontSize: _large ? 32 : 16,
color: _colored ? Colors.blue : Colors.black,
fontWeight: _bold ? FontWeight.bold : FontWeight.normal,
),
child: const Text('Animated text'),
)AnimatedPhysicalModel
Animates elevation, color, and shape with physical shadow.
AnimatedPhysicalModel(
duration: const Duration(milliseconds: 300),
shape: BoxShape.rectangle,
elevation: _elevated ? 12 : 0,
color: _elevated ? Colors.blue : Colors.grey[300]!,
borderRadius: BorderRadius.circular(12),
child: const Padding(
padding: EdgeInsets.all(16),
child: Text('Physical model'),
),
)AnimatedTheme
Animates theme changes.
AnimatedTheme(
duration: const Duration(milliseconds: 500),
data: ThemeData(
brightness: _darkMode ? Brightness.dark : Brightness.light,
primaryColor: _darkMode ? Colors.blue[700] : Colors.blue,
),
child: const MyChildWidget(),
)Common Parameters
All implicit animations support these parameters:
AnimatedContainer(
duration: Duration(milliseconds: 300), // Required: Animation duration
curve: Curves.easeInOut, // Optional: Animation curve
onEnd: () {}, // Optional: Completion callback
child: Widget(), // The child widget to animate
)Duration
const Duration(milliseconds: 300) // 0.3 seconds
const Duration(seconds: 1) // 1 second
const Duration(days: 1) // 1 dayCurves
Common curves from Curves class:
Curves.linear // Linear (no easing)
Curves.ease // Simple ease in and out
Curves.easeIn // Slow start, fast end
Curves.easeOut // Fast start, slow end
Curves.easeInOut // Slow start and end, fast middle
Curves.easeInCubic // Cubic ease in
Curves.easeOutCubic // Cubic ease out
Curves.easeInOutCubic // Cubic ease in and out
Curves.elasticIn // Bounces in
Curves.elasticOut // Bounces out
Curves.elasticInOut // Bounces in and out
Curves.bounceIn // Bounces in (more subtle)
Curves.bounceOut // Bounces out (more subtle)Custom curve:
class ShakeCurve extends Curve {
@override
double transform(double t) => sin(t * pi * 2);
}
// Usage
AnimatedContainer(
curve: ShakeCurve(),
// ...
)onEnd Callback
Trigger action when animation completes:
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
onEnd: () {
if (!_visible) {
// Animation finished fading out
// Remove widget, navigate, etc.
}
},
child: const Text('Hello'),
)Best Practices
DO
- Use implicit animations for simple, one-off animations
- Set appropriate durations for natural feel (200-500ms typical)
- Use curves to make animations feel natural
- Use
onEndcallback for post-animation actions - Test animations on various devices
- Consider
MediaQuery.disableAnimationsfor accessibility
DON'T
- Use implicit animations for complex, multi-property sequences (use staggered instead)
- Forget to set duration (defaults to instant change)
- Use extremely long durations (> 1s) without good reason
- Animate too many properties simultaneously (performance impact)
- Nest implicit animations deeply (can cause jank)
Performance Tips
- Implicit animations are optimized but still rebuild during animation
- Avoid animating complex widget trees
- Use
RepaintBoundaryaround animated children to isolate repaints - Test performance with Flutter DevTools Performance overlay
- Consider using
AnimatedBuilderfor complex widgets to minimize rebuilds
Debugging
Slow Animations
// Slow all animations by 10x during development
timeDilation = 10.0;Show Repaint Rainbows
void main() {
debugPaintSizeEnabled = true;
runApp(MyApp());
}Performance Overlay
void main() {
runApp(MyApp());
}
// In debug mode, press 'p' to toggle performance overlayComparison: Implicit vs Explicit
| Feature | Implicit | Explicit |
|---|---|---|
| Controller needed | No | Yes (AnimationController) |
| Setup complexity | Low | Medium/High |
| Reusability | Widget-based | Controller-based |
| Multiple properties | AnimatedContainer only | Unlimited |
| Lifecycle control | Limited | Full |
| State monitoring | Limited (onEnd) | Full (addStatusListener) |
| Performance | Good | Excellent (with proper patterns) |
When to Use Implicit Animations
Use implicit animations when:
- Animating a single property
- Animation is triggered by state change
- No need for fine-grained control
- Want simple, declarative code
- Animation is one-time or simple toggle
Use explicit animations when:
- Need full control over animation lifecycle
- Animating multiple properties
- Need to react to animation state
- Creating reusable animation components
- Need complex timing or sequencing
- Performance is critical