Getting Started With Staggered Animations in Flutter
Animations in mobile apps are powerful tools to attract users’ attention. They make transitions between screens and states smoother and more appealing for the user. In this tutorial, you’ll learn how to implement animations in Flutter. By Sébastien Bel.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started With Staggered Animations in Flutter
30 mins
- Getting Started
- Animating Goodnightmoonhellosun
- Using Implicit Animations
- Applying Tween Animations
- Implementing Curve Animations
- Animating the Sun and the Moon Implicitly
- Using Explicit Animations
- Animating With Animation
- AnimatedBuilder
- Coordinating Animations With Intervals
- Introducing AnimatedWidgets
- Implementing AnimatedWidget
- Animating Daytime and Nighttime Transition
- Animating the Theme
- Animating the TodayDetails Widget
- Interpolating a Custom Object
- Adding More Animations
- Add an Animation to BottomCard
- Make a Startup Animation
- Improve CloudyWidget
- Make It Rain or Snow
- Solution
- Where to Go From Here?
If you want your app to stand out from the crowd, you might be surprised at what a difference just a few animations make. Not only can animations bring your app to life, but they’re also a very simple way to explain what’s happening on the screen.
Luckily, Flutter makes the process of adding animations very easy. You can use implicit animations for simple cases, or you can choose explicit animations if you need more control.
Implicit animations are really easy to use, but they only allow you to control the duration and the curve of your animations. For example, use AnimatedFoo
instead of Foo
and your widget will animate automatically when its values change — and Container
becomes AnimatedContainer
. Find the full list for ImplicitlyAnimatedWidget
in the Flutter documentation.
When you need more control over the lifecycle of your animation, though, such as the ability to pause it or launch it on demand, you have to use explicit animations. They’re especially useful when there are several elements to coordinate.
In this tutorial, you’ll build a weather app called Good Night Moon Hello Sun. With the help of staggered animations, you’ll create a custom day-to-night animation using:
AnimationController
Tween
-
Tweens
withIntervals
-
AnimatedBuilder
andAnimatedWidget
to render the animations
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial and open it in your favorite IDE.
The code is in the lib folder, and the images are in assets/icons. Open main.dart and take a look at the home
widget given to MaterialApp
:
LayoutBuilder(builder: (context, constraints) {
return HomePage(width: constraints.maxWidth);
})
HomePage
uses LayoutBuilder
constraints to know its size and be more responsive than with hard-coded values.
Speaking of which, open home_page.dart.
Since you’re building a weather app, you need to give it some data. SampleDataGenerator
generates weather mock data using classes you’ll find in model:
- WeatherData: Weather for a day with a list of its details.
- WeatherDataDetails: Weather, temperature and wind at a given time of the day.
You usually change the theme of an app by changing MaterialApp
‘s theme, but MaterialApp
includes a default implicit animation that’s not customizable. So instead, you’ll use the Theme
widget — which is not animated by default — to fully control the animation between the day and night themes.
In home_page.dart, build()
only contains Theme
, which depends on _isDayTheme
because _content()
returns the rest of the widget tree. You’ll use _animationButton()
to launch the animation.
The rest of the widgets are pretty common, except for these custom ones:
SunWidget
MoonWidget
CloudyWidget
TodayDetailsWidget
BottomCard
Take a look at sun_widget.dart. This is your sun, which you’ll animate later. Note, again, the presence of LayoutBuilder
to set its size.
Next, open moon_widget.dart. You’ll see that MoonWidget
uses an image instead of just a Container
.
cloudy_widget.dart shows clouds on top of SunWidget
or MoonWidget
, depending on the theme.
today_details_widget.dart is where you display the temperature, wind and weather of the day.
Finally, open bottom_card.dart. BottomCard
displays both the hourly weather and the forecast for the next five days.
Now that you’re familiar with the project’s code, build and run.
Test switching the theme by clicking SWITCH THEMES; you’ll notice you have two different themes for your app, but no animations yet.
Animating Goodnightmoonhellosun
The transition from day to night includes several elements.
The scheme above illustrates the transition. First, the sun disappears by moving away. Then, the moon replaces it by moving in.
Other elements animate in the middle of this transition:
- Theme: Changes from the day theme to the night theme with a fade in/fade out transition of its colors.
- TodayDetailsWidget: Moves and fades at the same time.
The night-to-day transition is the same, but you swap the sun and moon while the theme transition goes from the night theme to the day theme. Some animations run one after the other, while others animate in parallel.
This is how the animation will look when you finish this tutorial:
Using Implicit Animations
You’ll start by animating the sun and the moon during the day-to-night transition. The sun moves to the left, and the moon appears from the right.
You have several options to do this using implicitly animated widgets, such as AnimatedSlide
and TweenAnimationBuilder
. Here, you’ll use the latter, which needs a Tween
.
Applying Tween Animations
Tween
defines the starting and ending points of your animations, as its definition suggests:
Tween<T>(begin: T, end: T)
To interpolate between 0.0 and 1.0, for instance, you’d write:
Tween<double>(begin: 0.0, end: 1.0)
It works with many objects, and you can even try it with Colors
!
Tween
interpolates between two values using the operators +, – and * in its lerp
method. This means you can make Tween
between custom objects if you implement these. It also means some objects can’t interpolate well since they don’t do it or their operators don’t fit. For example, the operator * in the int
class returns num
instead of int
, which is the reason why Tween
can’t interpolate it.
There are a few prebuilt Tween
s for these cases. For example, you can use IntTween
for int
or ConstantTween
, which is a Tween
that stays at the same value. See the implementers of Tween
for the full list.
Implementing Curve Animations
Instead of playing your animations linearly, you can apply different curves to them. Curves
includes the most common ones, like easeIn
and easeOut
. See them animated in the docs.
There are several ways to apply a curve to your animations. One is to apply the curve directly to a Tween
by calling chain()
on it:
Tween<double>(begin: 0.0, end: 1.0)
.chain(CurveTween(curve: Curves.slowMiddle));
This Tween
would have a slowMiddle
curve.
Animating the Sun and the Moon Implicitly
To see how implicit animations work and what their limitations are, you’ll try animating the sun and moon implicitly first.
Replace the contents of _sunOrMoon()
in lib/ui/home_page.dart with the following:
return Stack(children: [
// TweenAnimationBuilder for the sun
TweenAnimationBuilder<Offset>(
duration: const Duration(seconds: 2),
curve: Curves.bounceIn,
tween: Tween<Offset>(
begin: const Offset(0, 0), end: const Offset(-500, 0)),
child: const SunWidget(),
builder: (context, offset, child) {
return Transform.translate(offset: offset, child: child);
},
),
// TweenAnimationBuilder for the moon
TweenAnimationBuilder<Offset>(
duration: const Duration(seconds: 2),
curve: Curves.bounceOut,
tween:
Tween<Offset>(begin: const Offset(500, 0), end: const Offset(0, 0)),
child: const MoonWidget(),
builder: (context, offset, child) {
return Transform.translate(offset: offset, child: child);
},
)
]);
Here, you use two TweenAnimationBuilder
s to animate SunWidget
and MoonWidget
implicitly.
Your TweenAnimationBuilder
has several arguments:
- duration: Your animation duration.
-
curve: Here, you make
SunWidget
bounceIn
andMoonWidget
bounceOut
. -
tween: You used
Tween
to make the translation animation. -
child: The non-moving part of your widget. In this case, the children are
SunWidget
andMoonWidget
. -
builder: Where you decide how to animate. Here, you used
Transform.translate()
.TweenAnimationBuilder
calls it several times with an updatedoffset
interpolated from yourTween
.
Note that you display both the sun and the moon in a Stack
because you haven’t handled the transition between the themes yet.
Hot reload to see the result.
Though you get an animation, it only animates on startup. It’s not easy to start it on a button press or launch the moon animation after the sun animation finishes. This shows that implicit animations are not the best tool here.