Flutter Explicit Animations | Master Flutter animations (part 2)
In the first part of this article, we had an introduction to animations in Flutter, in part one we only talked about implicit animations, and if you read that article, you know that even though implicit animations are easier to implement, they limit the control we can have over the animation, that's exactly why we have explicit animations, they are relatively more difficult compared to the implicit counterparts but they give you total control over the animation, this article will get you up to speed with explicit animations in flutter.
Now, before we start I would like to add that this article is totally based on the official documentation, which you can check out here, now if you are ready let's start writing some code…
Initial Code
Let’s imagine that you are an intern at a development startup, and your first task is to build a loading screen for a taxi app the team is working on, the loading screen is pretty simple, the want a small car icon that moves back and forward (easy right?), so you come up with this code.
What we are doing in the code above is, we are using a periodic timer, and every second, we are changing the distance traveled by the car, if the distance is 0, we change it to 300, and if it is 300 we change it back to 0… well… the only problem here is that this animation looks choppy and natural and that's mostly due to the low frame-rate, whats a frame rate you ask?
Frames and Framerates
To understand frames and framerates you first have to understand how animations work in general, an animation is basically an illusion of movement that is created by displaying a sequence of images (frames) in a short span of time, so a frame would be a single image displayed in that sequence of images, and the framerate is the number of frames that are displayed every second denoted by fps (frames-per-second), so when we say flutter uses 60 fps (60 frames per second), we mean that under normal circumstances a flutter app will display 60 images every second, or you could say that the widget tree is rebuilt 60 times every second.
Improving the initial code
The animation we created at the top is choppy and unnatural, one thing that you may try to do to fix that is to reduce the duration of the periodic timer, and instead of changing the distance traveled from 0 to 300, you can gradually increment the distance traveled, to do that we will replace the code above with this new code.
Now, this looks way better right? The animation looks smoother and we can all go how right? WRONG! Even though this approach does in fact look better, there are still major drawbacks of this approach.
- First, by using a Timer to animate our flutter apps, there’s no way of telling for sure if our animation runs at 60fps, we don't know if we are animating through every frame.
- We had to interpolate between the range of 0 to 300 manually, which is less than ideal.
- Now, the car that we are animating only moves forward, we would have to add a lot of code to make the car reverse.
- We have no way of stoping the animation once it starts, and so much more…
That's why now, I will show you how to animate the correct way.
AnimationController
The
AnimationController
is a specialAnimation
object that generates a new value whenever the hardware is ready for a new frame. All explicit animations require anAnimationController
.
AnimatedController
will create an interpolation of values between the upper and lower bounds, based on all of the possible frames for the given animation, what that means is that with an animation controller we would not have to manually create an interpolation of values, you only tell it that you want to animate between let's say 0 and 100 in one second and the controller will take care of everything (that's is awesome right?), AnimationController is playable, that basically means that we can start the animation, stop it, forward and reverse the animation.
Tickers and TickerProvider
In flutter, a Ticker
is a class that does something (calls a callback function) on every new frame, by default Tickers start muted and we have to manually start them, a TickerProvider
is an interface implemented by classes that wish to do something every time a new frame triggers, a TickerProvider
, does exactly what its name suggests it provides a Ticker, and this is exactly what we need when we create explicit animations, previously we animated with the aid of a periodic timer, but that's not what we want, we want to create animations based on frames and not on a fixed time, and that's why from now on we will use TickerProvider instead of a periodic timer.
Vsync
AnimationController
, takes an argument called vsync
, vsync
is used to “tell flutter if an object is being displayed on the screen”, this is useful because if the widget is not visible, then flutter will not animate that particular widget.
vsync
must implement aTickerProvider
interface. Therefore, the first step for creating an explicit animation is to make aTickerProvider
object available to pass as thevsync
argument toAnimationController
. To do this, use aTickerProvider
mixin with the widget that you are animating.
The only thing, that you have to do, is to add SingleTickerProviderStateMixin
or TickerProviderStateMixin
(when animating multiple widgets) to the state of the widget
Let’s animate the right way
First, let's add an animation controller and remove the other variables, and secondly, we will add a dispose method to make sure that all resources used by the animation controller will be freed once the widget is disposed.
The AnimationController
is defined using the late keyword because it will be initialized after its declared, so basically, we are telling flutter that the controller, is null at the declaration stage but it will be initialized later (before it's used).
AnimationController(vsync: this)
: here we are passing _CarAnimationScreenState
, as the TickerProvider
argument vsync
, we are able to pass _CarAnimationScreenState
as a TickerProvider
, because _CarAnimationScreenState
implements SingleTickerProviderMixin
controller..dispose()
: will dispose the animation controller when the widget is closed.
Now let's pass more arguments to the AnimationController
Line 1, represents the duration of the animation, in our case, is one second, lines 2 and 3, represent the bounds of the range of values that we wish to interpolate through.
An
AnimationController
doesn’t know anything about the UI—it merely triggers changes to itsvalue
property over a specifiedduration
. To make your UI respond to the changes thatAnimationController
makes to itsvalue
property, register a listener withAnimationController
that callssetState()
each timeAnimationController
changes its value:
And since we removed the distance variable, we will use controller.value
, to change the position of the car.
AnimationController
provides anaddListener()
method that takes a callback function.AnimationController
invokes this callback function every timeAnimationController
changes itsvalue
property. In order to rebuild the widget tree to reflect each change inAnimationController
’svalue
property, you must callsetState()
from within the listener callback that you pass toaddListener()
. This is why explicit animations requireStatefulWidget
s.
Animation Triggers
The AnimationController
comes with several methods that can be used to trigger the animation, those methods are:
forward()
this method is used to start the animation from the lower bound to the upper bound, on our specific example that would mean moving the car from 0 to 300reverse()
this method is used to “play the animation backwards”, in our example that would be moving the car from 300 to 0stop()
used to stop the animationrepeat()
this method is used to play the animation in a loop, it as a boolean parameter calledreverse
that is set totrue
the animation will animate forward and backwards and is set tofalse
the animation will animate in a forward-only loop, in our example that would mean moving the car from 0 to 300, and then from 0 to 300 again without never moving it from 300 to 0reset()
resets the values of the property value, to the lower bound. “restarts the animation”
Now let’s add a few buttons to our app and see these methods in action
From lines 2 to 12 we are adding ElevatedButton
widgets that will move the car forward, reverse it and stop it, and on line 19 we are adding another button that will loop the movement animation, notice this line here controller.repeat(reverse:true)
here we have the value of reverse set to true because we want to move the can forward and in reverse as well, here is the final code and the resulting output
🎉 You Made It!
Congratulations to you if you made it this far, by now I really believe that you are more than ready to dive deeper into explicit animations using flutter, and I really hope this article was helpful to you… and if you liked the articles you probably will like my youtube channel 😉 and my github!