Connecting animations with conditions in Flutter
⚠ This article covers a previous version of Simple Animation. The basics stay the same but class names have changed.
In this educational article I want to cover a real world problem. Imagine we have a button. The user clicks the button and into transforms into a progress indicator because we are loading something. After things are loaded we transform it into a success indicator. Here is how it looks like:
As we are actively waiting for a certain condition (stuff is loaded) we can start an animation and tween to the progress indicator. We wait until the desired condition occurs. Then we start a second animation that tweens to the success state.
Due to both animations share same widgets (i.e. width or background color) we can’t just display the first animation exclusively and then the second one. We need to connect the animations.
I created an overview about what’s going on:
On the bottom you can see a property called “Child Index”. I will show you how to use tweening techniques to access independent child sub-trees.
Also we need be aware that the animation displays correctly if the loading is faster then our first animation. Like this:
Model the conditions
The whole example plays inside a stateful widget LoadStuffButton. We have three state variables startedLoading, firstAnimationFinished and dataAvailable and it’s corresponding methods:
We simulate the loading action by using a Future.delayed(...)
.
Connecting two animations
For the next part we implement the build-method with two animations. I am using the simple_animations package for less verbose code. I covered the basics in my previous articles.
First I define my tweens by using two MultiTrackTween and apply all properties I want to animate:
Then I’ll define a variable playSecondAnimation
that covers our condition. We only want to play the second animation when our stuff is loaded and the first animation finished.
After that we just return two wrapped ControlledAnimation widgets. Both have a playback
-binding and use the tweens we defined earlier:
The magic of connecting both animation is in line 18. We use the condition to determine which animation tween result ( ani1
or ani2
) we finally want to use to build our button. That’s why we named the properties in our MultiTrackTweens the same.
The button builder
This method is quite simple. We create a Container that uses our tweened properties ( ani
):
But there is more. I don’t want to put all UI elements for “click to load”, “circling progress indicator” and “success” into the buildButton method. So I created an array of custom widget builders (defined as AniWidgetBuilder).
My buildButton method just delegates to one of those, defined by childIndex
tween value. We also pass our tween values ( ani
) so they can make use of it (for example “opacity” is currently not used).
Load button label
This widget builder is very simple. It uses the opacity to fade-out the text when the first animation is starting.
Progress indicator
Here we are doing the same but also use another cascaded ControlledAnimation to endlessly rotate an icon.
Show success
This widget builder is a little bit bigger because we animate the width of the text label and afterwards it’s opacity in an own independent animation. (We don’t use ani
)
One notable widget is the ClipRect that we to clip off the text while the width is quite small. Otherwise we would get overdraw issues.
Put everything together
You might have noticed that I skipped some (layout-related) methods are irrelevant for demonstrating these techniques. You can see the whole source code here.
You can also try this and other examples within the Example App (PlayStore) or clone it. (Github)
I hope you liked it or found it useful. Feel free to ask I something wasn’t clear or if you know how to simplify the one or two aspects.
Leave me some claps when you like to read more articles.
Your fluttering
— Felix —