Sequenced Animations in Flutter

Thomas Cornet
This might work
Published in
6 min readSep 3, 2019

--

One of the many advantages of Flutter is the ability to create stunning and fluid animations. Animations are a key part of a modern mobile UX, be it in transitions, micro-interactions, state-changes and more.

The Flutter team does a great job helping developers get started with animation on their website.

For example the Staggered animation section provides a good starting point for creating more complex animations involving multiple elements.

In this post I will try to reproduce an animation made in Principle which I found on Principle Templates.

The animation

This micro-interaction animation of a Like button was created by Yann and can be found here.

The benefit of using Principe is that you are able to easily break down the animation in timelines per element accompanied with information on size, position, opacity, … which we will need to reconstruct it in Flutter.

One thing to notice is that this animation has sequenced steps, meaning that one characteristic for the same element undergoes more than one transformation. For this we will need to use a TweenSequence, a type of tween that enables you to sequence multiple steps on a shared timeline, but more on that later.

Project setup

Let’s start by setting up a Flutter project in Android Studio, or your preferred IDE.
If your IDE start with the typical increment demo remove all code until you end up with an empty scaffold.

Your main.dart should look like this:

https://gist.github.com/cornetthomas/0eb15a44639eb60d693a2f820665b1b0

Next we will add our assets to our project. You can easily export the needed assets from Principle for this purpose.

  1. Add the image assets to your project folder in an /images subfolder
Example of assets in /images project folder. Screen from Android Studio

2. Don’t forget to define this folder in your pubspec.yaml

https://gist.github.com/cornetthomas/0cdaf1717f9a0cc3c1d7cb45425685cc

Now we are ready to start recreating the animation.

Setting up the stack

The animation works by placing all assets on top of each other and uses size and opacity to reveal and interact .
To do this in flutter we will start by adding a Stack widget. The Stack widget takes a list of children which it will paint on the screen in different layers which can overlap.

In the body of the Scaffold we will add the following:

https://gist.github.com/cornetthomas/9d2a57e4f4fec1b0e6a77898eef4dac2

This will add a first layer containing the Circle_Red.png image, we’ll give it a fixed size of 150 by 150 for now.
The Align widget is added to center align all Stack children.

When running the app you should see something like this:

Screenshot of app in iOS Simulator after adding first asset.
Screenshot of app in iOS Simulator after adding first asset.

Now we can add all assets in a similar way.
Make sure to use the following order when adding the assets to the stack:

  • Circle_Red
  • Circle_White
  • Heart_WaveEffect
  • Heart_Red
  • Heart_Grey

When running the app you should see this:

Screenshot of app in iOS Simulator after adding all assets.
Screenshot of app in iOS Simulator after adding all assets.

As you notice, the Stack draws it’s layers from top to bottom.
Your build function should now look like this:

https://gist.github.com/cornetthomas/b3b165cf13feb38443e55b1c063b4d00

The animation controller

First we’ll start by creating the animation controller add a GestureDetector to trigger the start of the controller.

Add a TickerProviderStateMixin to our Widget State class. Simply put, this will generate ticks which will drive our animation.
The animation controller takes a TickerProviderStateMixin in its vsync parameter, in this case the class itself will be the ticker provided. We also give it a duration of 2 seconds.

https://gist.github.com/cornetthomas/90bd7480795b31511206a5de27973ea2

Next wrap the Stack widget in a GestureDetector widget. In the onTap callback we will reset and start the animation.

https://gist.github.com/cornetthomas/afc13645276f3ecdd46cf95aa15c9fc4

Grey Heart

We’ll start simple with the grey heart with is visible at the start of the animation. It simply shrinks until it is no longer visible.

The elements starts at a size of 148 by 148 and shrinks to 0 by 0 using an easeInOut curve from 0.0 to 0.15 on the timeline. The achieve this, we’ll create a Tween that return a double, which we’ll use as width and height for the GreyHeart. Because we don’t want this transformation to last during the entire animation, we define an interval.

https://gist.github.com/cornetthomas/0628b3eb2c158fb35c3c9feeea6c20c3

The apply this to the GreyHeart assets, we’ll wrap the widget in an AnimatedBuilder. The AnimatedBuilder will execute its build method on each tick of the AnimationController.
We’ll also use our animated value to provide the size of the widget.

https://gist.github.com/cornetthomas/66400cf774621ff2f721c5ff2c5604ef

Now run the app, every time you hit the image you should see the grey heart shrink and disappear.

Now well to the same to the size of RedCircle, WhiteCircle and WaveEffect, but with different parameters.

https://gist.github.com/cornetthomas/b17163585af94a5b7afd5af7d250bd1d

We will also need to wrap the widgets in an AnimatedBuilder using the new tweens to drive the size of the assets as we did before.

Sequenced Animations

What remains now is to animate the size of the RedHeart and Opacity of the WaveEffect. If you look at the timeline, you will see that these are more complicated as the others, because these involve different behaviour during the animation.
For example the RedHeart first , then it wil shrink again, to finally grow back a little.
To achieve this we will need to combine tweens in different intervals of the animation.

The TweenSequence class was created for this purpose, providing this functionality out of the box.

The TweenSequence works by providing a set of TweenSequenceItem
which consist of a Tween and a weight. The weight will determine how much time each tween will be allowed to run during the duration of the animation.
The order of items will determine which TweenSequenceItem is executed first, this is from top to bottom.

https://gist.github.com/cornetthomas/5fe141eddf23bbda4a46a3076176bea1

Let’s break this down.

The _redHeartSize animation object is created by calling
TweenSequence(..).animate().

Into the TweenSequence we’ll provide the different steps called items.

Each of these items is TweenSequenceItem which holds a tween and a weight. Because we want each of the steps to have their specific curve, we chain a CurveTwee to each of the steps.
For weights we choose 20.0 for the first two, and the remainder for the last steps.

At last we’ll create an Animatable by calling .animate() on the TweenSequence. Because we want the sequence to start with a delay we setup a CurvedAnimation with an interval of 0.1 to 1.0

For the WaveEffect opacity this looks as follows.

https://gist.github.com/cornetthomas/241f85d18d74867d18454062de7dd28e

Here you’ll see a similar structure, but we don’t define a curve for each Item. Because both steps use an easeInOut curve, we define this on the TweenSequence level and not on each TweenItem itself.

Again, don’t forget to wrap the widgets in an AnimatedBuilder and use the animation value to define the height and width.

For the opacity we’ll also wrap the widget in an Opacity widget and use the animation value to determine the opacity of the child widget.

https://gist.github.com/cornetthomas/c2e7ae42189429576f31ee2fc98a6472

The final result

Now we are ready running the full version and testing our animation.
Below you see an example running on an iOS simulator. Keep in mind this is in debug-mode, try running it on an actual device using ‘flutter run — release’ to experience it in full.

Final result running on an iOS Simulator

I hope you had fun following this example on sequenced animation. Feel free to leave comments and feedback.

Check out the full code on Github and have a look at our website for more work we do.

This post was written after giving a talk on animations at Flutter Meetup Belgium some time ago. I only just found the time to finish it and share.

--

--

Thomas Cornet
This might work

Founder of thismightwork.co — Writes about digital product design & strategy, mobile app development, and whatever comes to mind