Explore the diverse types of animations in Flutter, including implicit, explicit, hero, and physics-based animations. Learn when to use each type and see practical examples.
Animations are a powerful tool in Flutter that can significantly enhance the user experience by providing visual feedback, guiding user interactions, and adding a layer of polish to your applications. Flutter offers a rich set of animation capabilities, categorized primarily into implicit and explicit animations, along with specialized types like hero animations and physics-based animations. In this section, we will delve into these types, exploring when and how to use each, supported by practical examples and code snippets.
Implicit animations in Flutter are designed to be simple and easy to use. They automatically animate changes to their properties over a given duration whenever those properties change. This makes them ideal for straightforward animations where you don’t need fine-grained control over the animation process.
AnimatedContainer
: Animates changes to its properties such as color, width, height, and alignment.AnimatedOpacity
: Fades a widget in and out by animating its opacity.AnimatedPadding
: Animates changes to the padding of a widget.AnimatedContainer
class ImplicitAnimationExample extends StatefulWidget {
@override
_ImplicitAnimationExampleState createState() => _ImplicitAnimationExampleState();
}
class _ImplicitAnimationExampleState extends State<ImplicitAnimationExample> {
bool _isExpanded = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Implicit Animation')),
body: Center(
child: GestureDetector(
onTap: () {
setState(() {
_isExpanded = !_isExpanded;
});
},
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: _isExpanded ? 200 : 100,
height: _isExpanded ? 200 : 100,
color: _isExpanded ? Colors.blue : Colors.red,
alignment: _isExpanded ? Alignment.center : Alignment.topCenter,
child: FlutterLogo(size: 75),
),
),
),
);
}
}
In this example, tapping the widget toggles its size, color, and alignment, all animated smoothly over one second.
Explicit animations provide more control over the animation process, allowing you to define the animation’s behavior, timing, and sequence. They are suitable for more complex animations where you need to orchestrate multiple animations or respond to user interactions dynamically.
AnimationController
to manage the animation’s lifecycle.Tween
to define the range of values for the animation.class ExplicitAnimationExample extends StatefulWidget {
@override
_ExplicitAnimationExampleState createState() => _ExplicitAnimationExampleState();
}
class _ExplicitAnimationExampleState extends State<ExplicitAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_animation = Tween<double>(begin: 0, end: 300).animate(_controller)
..addListener(() {
setState(() {});
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Explicit Animation')),
body: Center(
child: Container(
width: _animation.value,
height: _animation.value,
color: Colors.green,
child: FlutterLogo(),
),
),
);
}
}
This example demonstrates an explicit animation where the size of a container oscillates between 0 and 300 pixels.
Hero animations are used for shared element transitions between routes. They create a smooth transition effect for a widget as it moves from one screen to another, maintaining the user’s focus on the element.
class HeroExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Hero Animation')),
body: Center(
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (_) => DetailPage()));
},
child: Hero(
tag: 'hero-logo',
child: FlutterLogo(size: 100),
),
),
),
);
}
}
class DetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Detail Page')),
body: Center(
child: Hero(
tag: 'hero-logo',
child: FlutterLogo(size: 200),
),
),
);
}
}
In this example, tapping the logo transitions it to a new screen, smoothly resizing it in the process.
Physics-based animations simulate real-world physics, such as springs and gravity, to create more natural and intuitive animations. Flutter provides the SpringSimulation
and GravitySimulation
classes to achieve these effects.
class PhysicsAnimationExample extends StatefulWidget {
@override
_PhysicsAnimationExampleState createState() => _PhysicsAnimationExampleState();
}
class _PhysicsAnimationExampleState extends State<PhysicsAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
final spring = SpringDescription(
mass: 1,
stiffness: 100,
damping: 5,
);
_animation = _controller.drive(
Tween<double>(begin: 0, end: 300).chain(CurveTween(curve: Curves.easeInOut)),
);
_controller.animateWith(SpringSimulation(spring, 0, 300, 0));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Physics-Based Animation')),
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.orange,
child: FlutterLogo(),
);
},
),
),
);
}
}
This example uses a spring simulation to animate the size of a container, creating a bouncy effect.
Sequence and chained animations involve combining multiple animations either in sequence or simultaneously. This is useful for creating complex animations that involve multiple elements or stages.
class ChainedAnimationExample extends StatefulWidget {
@override
_ChainedAnimationExampleState createState() => _ChainedAnimationExampleState();
}
class _ChainedAnimationExampleState extends State<ChainedAnimationExample> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _sizeAnimation;
Animation<Color> _colorAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
);
_sizeAnimation = Tween<double>(begin: 50, end: 200).animate(
CurvedAnimation(parent: _controller, curve: Interval(0.0, 0.5, curve: Curves.easeIn)),
);
_colorAnimation = ColorTween(begin: Colors.red, end: Colors.blue).animate(
CurvedAnimation(parent: _controller, curve: Interval(0.5, 1.0, curve: Curves.easeOut)),
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Chained Animation')),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Container(
width: _sizeAnimation.value,
height: _sizeAnimation.value,
color: _colorAnimation.value,
child: FlutterLogo(),
);
},
),
),
);
}
}
In this example, the size and color of a container are animated in sequence, creating a smooth transition from one state to another.
Animations in Flutter are a versatile tool that can greatly enhance the user experience when used appropriately. By understanding the different types of animations available and their use cases, you can create engaging and intuitive applications. Whether you’re using implicit animations for simple transitions or explicit animations for complex sequences, Flutter provides the tools you need to bring your app to life.
For further exploration, consider diving into the official Flutter animations documentation and experimenting with different animation techniques in your projects.
graph TD; A[App Start] --> B[User Taps Widget] B --> C[Navigate to New Route] C --> D[Hero Animation Transition] D --> E[Widget on New Route]