Creating animations in Flutter can help bring your app to life and make it feel more interactive and engaging for users. With Flutter’s animation system, you can easily create custom animations that respond to user input, change over time, or react to changes in your app’s state.
In this guide, we’ll cover the basics of working with animations in Flutter, including how to create and manage animations, how to use different types of animation curves and controllers, and how to apply animations to specific widgets in your app.
Getting Started with Animations in Flutter
Flutter’s animation system is based on the concept of an Animation object. An Animation object represents a value that changes over time and can be used to create smooth and fluid animations in your app.
To create an Animation object in Flutter, you’ll need to use one of the many built-in Animation classes provided by the Flutter framework. Some common Animation classes include:
Animation<double>
: An animation that interpolates a value between two endpoints, typically used for animating numeric values like opacity or scale.Animation<Color>
: An animation that interpolates between two colors, typically used for animating color changes.Animation<Offset>
: An animation that interpolates between two offsets, typically used for animating position changes.
Once you’ve created an Animation object, you’ll typically want to apply it to a widget in your app. To do this, you can use the AnimatedWidget
class provided by Flutter. An AnimatedWidget
is a widget that automatically rebuilds itself whenever its associated Animation object changes.
Here’s an example of how you might create an animated widget that fades in and out over time:
class FadingWidget extends StatefulWidget { @override _FadingWidgetState createState() => _FadingWidgetState(); } class _FadingWidgetState extends State<FadingWidget> with SingleTickerProviderStateMixin { AnimationController _controller; Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: Duration(seconds: 1), )..repeat(reverse: true); _animation = Tween<double>(begin: 0, end: 1).animate(_controller); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Opacity( opacity: _animation.value, child: child, ); }, child: Container( width: 100, height: 100, color: Colors.blue, ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
In this example, we’ve created a widget called FadingWidget
that fades in and out over time. We create an AnimationController
with a duration of 1 second and set it to repeat in reverse. We also create an Animation<double>
object that interpolates between 0 and 1 over the duration of the animation.
In the build
method, we use the AnimatedBuilder
widget to wrap a Container
widget. The AnimatedBuilder
widget rebuilds itself whenever its associated animation changes, and provides us with a builder function that we can use to update the widget’s properties based on the current value of the animation. In this case, we’re updating the opacity of the Container
based on the current value of the _animation
object.
Finally, in the dispose
method, we dispose of the animation controller to prevent any memory leaks.
Types of Animations in Flutter
Flutter supports several types of animations that you can use to create engaging and dynamic user interfaces. Let’s take a look at some of the most common types of animations in Flutter.
Tween Animations
A tween animation is an animation that interpolates between two values over a period of time. In Flutter, you can create a Tween animation by using the Tween
class. The Tween
class takes two values of the same type and returns an animation that interpolates between those values.
Here’s an example of how you might create a Tween animation that animates the opacity of a widget:
class MyWidget extends StatefulWidget { @override _MyWidgetState createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin { AnimationController _controller; Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: Duration(seconds: 2), )..repeat(reverse: true); _animation = Tween<double>(begin: 0, end: 1).animate(_controller); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Opacity( opacity: _animation.value, child: child, ); }, child: Container( width: 100, height: 100, color: Colors.blue, ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
In this example, we’re using a Tween animation to animate the opacity of a Container
widget. We create an AnimationController
with a duration of 2 seconds and set it to repeat in reverse. We also create a Tween<double>
object that interpolates between 0 and 1 over the duration of the animation.
In the build
method, we use an AnimatedBuilder
widget to wrap the Container
widget. The AnimatedBuilder
widget rebuilds itself whenever its associated animation changes, and provides us with a builder function that we can use to update the widget’s properties based on the current value of the animation. In this case, we’re updating the opacity of the Container
based on the current value of the _animation
object.
Curved Animations
A curved animation is an animation that applies an easing function to the animation curve. In Flutter, you can create a curved animation by using the CurvedAnimation
class.
Here’s an example of how you might create a curved animation that animates the position of a widget:
class MyWidget extends StatefulWidget { @override _MyWidgetState createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin { AnimationController _controller; Animation<Offset> _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: Duration(seconds: 2), )..repeat(reverse: true); _animation = Tween<Offset>( begin: Offset.zero, end: Offset(0.5, 0), ).animate( CurvedAnimation( parent: _controller, curve: Curves.easeInOut, ), ); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.translate( offset: _animation.value * 200, child: child, ); }, child: Container( width: 100, height: 100, color: Colors.blue, ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } }
In this example, we’re using a curved animation to animate the position of a Container
widget. We create an AnimationController
with a duration of 2 seconds and set it to repeat in reverse. We also create a Tween<Offset>
object that interpolates between an Offset
of (0, 0)
and an Offset
of (0.5, 0)
over the duration of the animation. In the build
method, we use an AnimatedBuilder
widget to wrap the Container
widget. The AnimatedBuilder
widget rebuilds itself whenever its associated animation changes, and provides us with a builder function that we can use to update the widget’s properties based on the current value of the animation. In this case, we’re updating the position of the Container
based on the current value of the _animation
object.
Physics-Based Animations
A physics-based animation is an animation that simulates real-world physics. In Flutter, you can create a physics-based animation by using the SpringSimulation
class.
Here’s an example of how you might create a physics-based animation that animates the scale of a widget:
class MyWidget extends StatefulWidget { @override _MyWidgetState createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin { AnimationController _controller; Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: Duration(seconds: 2), ); _animation = Tween<double>(begin: 1, end: 2).animate( CurvedAnimation( parent: _controller, curve: Curves.easeInOut, ), ); } @override Widget build(BuildContext context) { return GestureDetector( onTap: () { _controller.animateTo( 1, duration: Duration(milliseconds: 500), curve: SpringCurve(), ); }, child: AnimatedBuilder( animation: _animation, builder: (context, child) { return Transform.scale( scale: _animation.value, child: child, ); }, child: Container( width: 100, height: 100, color: Colors.blue, ), ), ); } @override void dispose() { _controller.dispose(); super.dispose(); } } class SpringCurve extends Curve { const SpringCurve(); @override double transformInternal(double t) { final damping = 20.0; final velocity = 0.5; return 1 - (pow(E, -damping * t) * cos(velocity * t) * (1 - pow(E, -20 * t)) * 1.5); } }
In this example, we’re using a physics-based animation to animate the scale of a Container
widget. We create an AnimationController
with a duration of 2 seconds and set it to repeat in reverse. We also create a Tween<double>
object that interpolates between a scale of 1 and a scale of 2 over the duration of the animation.
In the build
method, we use a GestureDetector
widget to listen for taps on the widget. When the widget is tapped, we call the animateTo
method of the _controller
object to trigger the physics-based animation. We pass in a target value of 1 to animate the widget back to its original scale, and we also provide a curve
argument that uses a custom SpringCurve
object. This curve simulates the behavior of a spring by using a damping factor and a velocity value.
We then use an AnimatedBuilder
widget to wrap the Container
widget. The AnimatedBuilder
widget rebuilds itself whenever its associated animation changes, and provides us with a builder function that we can use to update the widget’s properties based on the current value of the animation. In this case, we’re updating the scale of the Container
based on the current value of the _animation
object.
Conclusion
In this guide, we’ve covered the basics of animation in Flutter, including how to use Tween
objects to interpolate between values, how to use AnimationController
objects to control animations, and how to use AnimatedBuilder
widgets to update widget properties based on the current value of an animation. We’ve also shown examples of how to create curved animations and physics-based animations. By combining these techniques, you can create complex and beautiful animations that bring your Flutter apps to life.