Flutter Animations Comprehensive Guide
In this article, I will do my best to cover, in great detail and with multiple examples, everything you need to know about Flutter animations. By the end of this article you’ll be able to add animations to your Flutter app with a wide range of complexity. And you’ll know which of the various approaches available in Flutter is best suited for your animation goals. So let’s get right to it!
Index:
- Introduction
- Implicit Animations — AnimatedFoo Widgets
- Implicit Animations — TweenAnimationBuilder Widget
- Explicit Animations — FooTransition Widgets
- The AnimationController
- Explicit Animations — AnimatedBuilder widget
- Explicit Animations — AnimatedWidget class
- Choosing the Right Animation Approach
- Animations Using 3rd Party Packages
TL;DR
You can see and tinker with the code of all the animations in this tutorial in this DartPad.
Also, I recently shared a Twitter thread that is basically a summary of this tutorial, so you can check it out.
Introduction
At the most basic level, animations in a Flutter app can be seen as one of two types: drawing-based, and code-based animations. Drawing-based animations are animated graphics, vectors, characters, or anything that is “drawn” then animated. On the other hand, code-based animations are focused on widget layouts and styles (lists, colors, text, ..etc). We’ll talk about drawing-based briefly at the end of this article, but because they’re usually achieved using 3rd party frameworks/packages, we will focus more on code-based animations. This is not to say code-based animations are limited, but on the contrary, Flutter animations allow you to create absolutely stunning, creative, and pretty complex animations without the need for 3rd party packages!
Code-based animations in Flutter have 2 types: Implicit animations & Explicit animations. In each of these types, you can either use ready-to-use widgets, or create your own widgets. Let’s dive deeper into each type with some examples.
1. Implicit Animations
Back to top 👆🏼
These are the simplest animations and easiest to use. Changing a value is enough to trigger an animation and Flutter handles everything behind the scenes.
1.1 Implicit Animations with Ready-to-use widgets
They’re called AnimatedFoo widgets, where Foo is the animated property. Most of them are animated versions of the widgets you already know and use, like Container/AnimatedContainer, Padding/AnimatedPadding, Positioned/AnimatedPositioned, …etc.
For example, check out this animation:
This animation was achieved using only AnimatedContainer, AnimatedPositioned, and AnimatedDefaultTextStyle widgets. Just give them a duration
value and a changing variable, and you’re good to go!
AnimatedPositioned(
top: selectedItemIndex * itemHeight,
left: 0,
right: 0,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
child: //...
),//...AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
decoration: BoxDecoration(
color: selectedItemIndex == i ? yellow : pink,
border: Border.all(
color: selectedItemIndex == i
? Colors.white
: Colors.transparent,
width: 2,
),
),
child: AnimatedDefaultTextStyle(
duration: const Duration(milliseconds: 200),
style: TextStyle(
color: selectedItemIndex == i
? Colors.black
: Colors.white,
),
child: const Text('Featured!'),
),
),
And simply each list item is wrapped with an InkWell
widget with the following onTap
method:
onTap: () => setState(() => selectedItemIndex = i),
And this is enough to trigger the animation!
You can view the full code of this animation as well as all the others in this tutorial in this DartPad. So in this article I’m going to highlight only the parts of the code that were directly responsible for the animation.
Here’s a full list of available AnimatedFoo widgets
AnimatedAlign
AnimatedContainer
AnimatedDefaultTextStyle
AnimatedOpacity
AnimatedPadding
AnimatedPhysicalModel
AnimatedPositioned
AnimatedPositionDirectional
AnimatedSize
So we have AnimatedFoo widgets for properties like opacity, padding, align, position, ..etc. But what if you want to animate another property and you still want something easy and quick to use?
1.2. Implicit Animations with TweenAnimationBuilder
Back to top 👆🏼
The TweenAnimationBuilder allows you to implicitly animate any property of any widget using a Tween class. The Tween class takes its name from “Between”. It basically gives you a begin
and end
values to animate between. And the builder of the TweenAnimationBuilder widget gives you the animated value that you can apply to whatever property you want in the widgets you return in that builder.
Here’s an example animation:
And here’s the code:
TweenAnimationBuilder(
duration: const Duration(milliseconds: 200),
tween: Tween<double>(begin: 0.01, end: _sliderValue),
child: Container(
decoration: BoxDecoration(
//...
),
child: Slider(
value: _sliderValue,
min: 0.01,
onChanged: (value) {
setState(() => _sliderValue = value);
},
),
),
builder: (BuildContext context, double? value, Widget? child) {
return ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 40 * (value ?? 0.01),
sigmaY: 40 * (value ?? 0.01),
),
child: child,
),
);
},
);
The _sliderValue
variable changes from 0.01 to 1. So for each value change, an animation is triggered and the builder of the TweenAnimationBuilder widget is rebuilt with new values for the sigmaX
& sigmaY
properties of the BackdropFilter widget. (I’m using 0.01 as the min value because the BackdropFilter widget throws an error on the web when it’s given 0 values)
Here we’re using the child
param of the TweenAnimationBuilder widget builder for better performance as this child rebuilds only once instead of rebuilding every time the animation is triggered.
Okay, that was the easy stuff, now to the juicy stuff!
2. Explicit Animations
Back to top 👆🏼
Remember when in implicit animations, merely changing a value inside an AnimatedFoo or TweenAnimationBuilder widget triggered an animation? Well, explicit animations don’t animate until “explicitly” being told to animate. And you tell them to animate and how to animate and “control” their animation using an AnimationController.
Similarly to implicit animations, explicit animations also have ready-to-use widgets as well as widgets with an extra level of customization for you to go wild!
But, wait, what the hell is an AnimationController? Let’s learn about it first before diving into explicit animation widgets that use it.
The AnimationController
Back to top 👆🏼
AnimationController({
double? value,
this.duration,
this.reverseDuration,
this.debugLabel,
this.lowerBound = 0.0,
this.upperBound = 1.0,
this.animationBehavior = AnimationBehavior.normal,
required TickerProvider vsync,
})
As mentioned before, the AnimationController allows you to “control” the animation. To do that, it needs a vsync
value of type TickerProvider. a Ticker basically keeps track of Flutter’s frames rendering and allows the controller to follow that ticker and “animate” through it within the specified duration, while linearly producing values between the lowerBound and the upperBound values which are by default 0 & 1.
As a result, with the AnimationController, you can:
- call
forward()
to play the animation forward - call
reverse()
to play the animation in reverse - call
stop()
to stop the animation - call
repeat()
to repeat the animation as long as it’s visible - call
reset()
to reset the animation to lowerBound - set it’s
value
- Access various getters to know the state of the animation like:
isAnimating
,isCompleted
,isDismissed
, …etc.
Now let’s use this bad boy and see it in action 🎬
2.1. Explicit Animations with Ready-to-Use Widgets
Back to top 👆🏼
They’re called FooTransition widgets, where Foo is the animated property of the widget. Some of them are also animatable widgets of the regular widget you use, e.g. AlignTransition, PositionedTransition.
Check out this animation:
This was achieved with AlignTransition and RotationTransition widgets like so:
Code breakdown:
- Lines 10 & 17: initializing and defining the AnimationController.
- The
vsync
value of the AnimationController (line 19) takesthis
that comes from theSingleTickerProviderStateMixin
(line 9) This mixin provides us with the TickerProvider that we talked about. This also insures that the animation is only running when the widget is visible - Lines 11 & 22: initializing and defining the Animation of the AlignTransition widget (line 51) using a Tween of type AlignmentGeometry. So we’re basically telling the animation that it’s going to animate from
Alignment.centerLeft
toAlignment.centerRight
and by calling theanimate
method on the Tween, we’re hooking this animation with our AnimationController and returning an Animation of type AlignmentGeometry. This way instead of the animation animating between the lowerBound & upperBound of the AnimationController, it’s animating between the begin & end values of the Tween.
An Animation class is a Listenable that listens to & notifies value changes of a specified type.
- Lines 12 & 32: initializing and defining the animation of the RotationTransition widget’s
turns
property (line 53). So with a 0 to 2 begin & end values, the widget will have turned twice by the end of the animation. - Line 20: calling
repeat
on the AnimationController to make it repeat indefinitely. And withreverse
set to true, the animation will animate forward, then in reverse, then forward, … and so on. - Line 42: disposing the AnimationController in the widget’s state’s dispose lifecycle method. It’s always important to dispose our AnimationController to avoid leaks!
This is a reminder of the DartPad containing all the animations in this tutorial
But don’t worry, with a little bit of practice, it will become really easy and you’ll get the hang of it!
Here’s a full list of available FooTransition widgets:
AlignTransition
DecoratedBoxTransition
DefaultTextStyleTransition
FadeTransition
PositionedTransition
RelativePositionedTransition
RotationTransition
ScaleTransition
ScaleTransition
SizeTransition
SlideTransition
StatusTransitionWidget
Remember in implicit animations what we did if all the AnimatedFoo widgets were not enough for our animation purpose? We used the TweenAnimationBuilder to animate a property not covered in AnimatedFoo widgets. Similarly, in explicit animations, we can use the AnimatedBuilder widget to animate any and/or multiple widget properties. Or we can go a step further and use the AnimatedWidget class to create our own FooTransition!
2.2 Explicit Animations with the AnimatedBuilder widget
Back to top 👆🏼
Check out this animation:
There is no GradientTransition widget, right? So how did we achieve it? With the AnimatedBuilder widget! Here’s the code:
Here we initialized and defined an AnimationController just like before (lines 10 & 14), and used it as the animation
value of the AnimatedBuilder widget (line 30). Now the AnimatedBuilder “rebuilds” for each changed value of the controller and calls the builder
to return the new widget with the updated _controller.value
value (line 37), causing the gradient to animate.
Of course, if you want something other than the lowerBound & upperBound values of the AnimationController, you can create your own Animation and hook it with the AnimationController and then give it to the AnimatedBuilder widget
_animation = Tween<double>(begin: 0, end: 0.5).animate(_controller);//...AnimatedBuilder(
animation: _animation,
builder: (context, child) {
//... Using the value: _animation.value
}
)
You can also make use of the child param of the AnimatedBuilder widget for better performance as it doesn’t rebuild with every animation value change.
You still with me? Hang in there, we’re almost done!
Now we’re going to go one step further and create our own FooTransition widget using the AnimatedWidget class!
2.3 Explicit Animations with the AnimatedWidget Class
Back to top 👆🏼
Let me tell you a secret that is not very secret. Go to the source code of any FooTransition widget, what do you see?
😱 It extends an AnimatedWidget class, and from what we see, the Animation type parameter (in this case turns
) is passed as a listenable
to the super class:
And the AnimatedWidget is basically a StatefulWidget! So we can do the exact same!
Let’s create our own GradientTransition widget then:
class GradientTransition extends AnimatedWidget {
final Animation<double> stop;
const GradientTransition({
Key? key,
required this.stop,
}) : super(key: key, listenable: stop);
@override
Widget build(BuildContext context) {
return Container(
height: 100,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: const [purple, pink, yellow],
stops: [0, stop.value, 1],
),
),
);
}
}
And to use it, we simply pass it the AnimationController:
GradientTransition(stop: _controller),
And that’s it!
If you survived to this point, then you have enough knowledge to create an unlimited amount of Flutter animations varying from very simple to very complex. All you need is a bit of practice and the sky is your limit!
Choosing the Right Animation Approach
Back to top 👆🏼
But hang on, how do you know which of the many ways we went over above you should choose for your animation? Well, the amazing people on the Flutter team created a video and a decision tree to help you with that. And I’m going to do my best to summarize it for you.
1. Drawing-based vs. Code-based
Your first choice will be between Drawing-based & Code-based animations, for that you can ask yourself if your animation is more like a drawing (use drawing-based, 3rd party packages, more on that shortly) or is it related to layout, widgets, widget styles, colors, borders, text, ..etc (use code-based, discussed above)
2. Implicit vs. Explicit
Your next choice will be between Implicit & Explicit animations, for which you have multiple criteria to decide:
- Infinitely repeated animation
- Discontinuous animation: the animation doesn’t return to its starting point.
- Multiple widgets are animating together.
If your animation has any of the above criteria, you should use Explicit animations
3. Built-in vs. Custom Widgets
Your last choice is between built-in widgets (AnimatedFoo & FooTransition widgets) and custom widgets (TweenAnimationBuilder & AnimatedBuilder/AnimatedWidget). This is as simple as viewing this list and considering if the property you want to animate already has a built-in widget (in which you use that) or not (in which you create your own)
Animations Using 3rd Party Packages
But what if you want to use drawing-based animations in your app? Well, you saved yourself some coding here 🫢, and Flutter is also amazing in this! With great packages out there, such as Rive and Lottie, It allows you to seamlessly integrate with 3rd party animations and add them to your app. You’ll need a graphic designer/motion graphics designer, or you can download/purchase animations created by the community and use them in your app quickly and easily.
I will write a dedicated article for using 3rd party packages for animations in Flutter. For now, here are some useful links:
That’s it from me! Thank you for reading this article. I hope this will be your go-to guide when you want to add an animation to your Flutter app. And when you do, go wild!