Implicit Animations: AnimatedContainer & AnimatedPadding
Implicit Animations: AnimatedContainer & AnimatedPadding
Flutter provides a powerful category of widgets called implicit animation widgets. These widgets automatically animate changes to their properties whenever those properties are updated inside setState(). You do not manage AnimationController, Tween, or Ticker objects at all — Flutter handles the interpolation for you behind the scenes.
The two most commonly used implicit animation widgets are AnimatedContainer and AnimatedPadding. Both accept a duration parameter that controls how long the transition takes, and an optional curve parameter that shapes the easing of the animation.
AnimationController.How Implicit Animations Work
Every implicit animation widget follows the same pattern:
- You declare the widget with some initial property values.
- When a state change causes one or more properties to receive new values, the widget detects the difference.
- Over the specified
duration, the widget smoothly interpolates between the old value and the new value. - The animation respects the chosen
Curve, which controls the speed profile of the transition (e.g., ease-in, bounce, elastic).
AnimatedContainer
AnimatedContainer is the animated version of Container. It can animate virtually any property that Container supports: width, height, color, border-radius, alignment, padding, margin, and decoration. This makes it one of the most versatile implicit animation widgets in Flutter.
AnimatedContainer — Animating Size and Color
class AnimatedBox extends StatefulWidget {
const AnimatedBox({super.key});
@override
State<AnimatedBox> createState() => _AnimatedBoxState();
}
class _AnimatedBoxState extends State<AnimatedBox> {
bool _expanded = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
_expanded = !_expanded;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
width: _expanded ? 280.0 : 120.0,
height: _expanded ? 280.0 : 120.0,
decoration: BoxDecoration(
color: _expanded ? Colors.deepPurple : Colors.teal,
borderRadius: BorderRadius.circular(_expanded ? 40.0 : 8.0),
),
alignment: Alignment.center,
child: Text(
_expanded ? 'Tap to shrink' : 'Tap to expand',
style: const TextStyle(color: Colors.white, fontSize: 14),
textAlign: TextAlign.center,
),
),
);
}
}
When the user taps the box, setState flips _expanded. AnimatedContainer automatically interpolates the width, height, color, and borderRadius over 400 milliseconds using the easeInOut curve. No animation boilerplate required.
decoration and color simultaneously, but do not set both color and a BoxDecoration with a color at the same time. Flutter will throw an assertion error. Put the color inside the BoxDecoration when you also need border-radius or shadows.Animating Multiple Properties at Once
One of the strengths of AnimatedContainer is that every property you change in a single setState call is animated simultaneously. You can animate width, height, color, border-radius, padding, and alignment all in one transition.
AnimatedContainer — Card Highlight Effect
class HighlightCard extends StatefulWidget {
final String label;
const HighlightCard({super.key, required this.label});
@override
State<HighlightCard> createState() => _HighlightCardState();
}
class _HighlightCardState extends State<HighlightCard> {
bool _selected = false;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _selected = !_selected),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.fastOutSlowIn,
margin: EdgeInsets.all(_selected ? 4.0 : 12.0),
padding: EdgeInsets.symmetric(
vertical: _selected ? 24.0 : 16.0,
horizontal: _selected ? 32.0 : 16.0,
),
decoration: BoxDecoration(
color: _selected ? Colors.indigo : Colors.white,
borderRadius: BorderRadius.circular(_selected ? 20.0 : 4.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(_selected ? 0.25 : 0.08),
blurRadius: _selected ? 16.0 : 4.0,
offset: const Offset(0, 4),
),
],
),
child: Text(
widget.label,
style: TextStyle(
color: _selected ? Colors.white : Colors.black87,
fontWeight:
_selected ? FontWeight.bold : FontWeight.normal,
),
),
),
);
}
}
AnimatedPadding
AnimatedPadding is a focused implicit animation widget that animates only the padding around its child. It is lighter than AnimatedContainer when you exclusively need to transition padding values — for example, sliding content inward or outward in response to user interaction or a keyboard appearing.
AnimatedPadding — Keyboard-Aware Content Shift
class FocusableForm extends StatefulWidget {
const FocusableForm({super.key});
@override
State<FocusableForm> createState() => _FocusableFormState();
}
class _FocusableFormState extends State<FocusableForm> {
bool _focused = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
AnimatedPadding(
duration: const Duration(milliseconds: 350),
curve: Curves.easeOut,
padding: EdgeInsets.only(top: _focused ? 8.0 : 60.0),
child: const FlutterLogo(size: 80),
),
Focus(
onFocusChange: (hasFocus) {
setState(() => _focused = hasFocus);
},
child: const TextField(
decoration: InputDecoration(
labelText: 'Username',
border: OutlineInputBorder(),
),
),
),
],
);
}
}
When the text field gains focus, the logo smoothly slides upward by transitioning from top: 60.0 to top: 8.0 — simulating the kind of content shift commonly seen in mobile login screens.
Choosing the Right Curve
The curve parameter controls how the animation progresses over time. Flutter ships many built-in curves in the Curves class:
- Curves.linear — constant speed, mechanical feel.
- Curves.easeIn — starts slow, ends fast.
- Curves.easeOut — starts fast, decelerates naturally.
- Curves.easeInOut — slow start and end, fast middle. Most natural for UI transitions.
- Curves.fastOutSlowIn — Material Design standard motion curve.
- Curves.bounceOut — overshoots and bounces at the end.
- Curves.elasticOut — elastic spring overshoot effect.
Curves.easeInOut or Curves.fastOutSlowIn looks the most polished and follows Material Design motion guidelines. Reserve bounce and elastic curves for playful, game-like interactions.Summary
Implicit animation widgets are Flutter's easiest entry point into motion design. AnimatedContainer lets you animate size, color, border-radius, padding, margin, and decoration in one widget simply by updating state. AnimatedPadding provides a lightweight alternative when only spacing needs to animate. Both accept a duration and a curve, giving you full control over the feel of the transition without any animation controller code. As a rule, reach for implicit widgets first, and graduate to explicit animations only when you need fine-grained control over sequencing or repeating.
Animated and its corresponding non-animated counterpart exists (e.g., Container → AnimatedContainer, Opacity → AnimatedOpacity), it follows the implicit animation pattern: change the property value inside setState() and Flutter animates the transition automatically.