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?
 
Animating the TodayDetails Widget
This widget is composed of two parts: The left one displays temperature, and the right one displays wind and weather type. The animation will take part in three phases:
- Move away from the original position and become transparent.
 - Stay transparent away for a while.
 - Move back to the original position and become visible.
 
In this case, you’ll need to use TweenSequence, which allows you to separate a Tween in several parts. Here’s an example:
TweenSequence<double>([
  TweenSequenceItem(tween: Tween<double>(begin: 0.0, end: 0.5), weight: 4),
  TweenSequenceItem(tween: ConstantTween<double>(0.5), weight: 2),
  TweenSequenceItem(tween: Tween<double>(begin: 0.5, end: 1.0), weight: 4),
]);
Each TweenSequenceItem has a weight used to determine its duration. For example, here you have a total weight of 10 with the following repartition:
- Go from 0.0 to 0.5 with a weight of 4, which is 4/10 of the total 
TweenSequencetime. - Stay at 0.5 with a weight of 2, which is 2/10 of the total 
TweenSequencetime. - Go from 0.5 to 1.0 with a weight of 4, which is 4/10 of the total 
TweenSequencetime. 
Interpolating a Custom Object
You need to animate two properties at once: Offset for the movement and double for the opacity. One way of doing it is to make a class that implements the operators used by Tween for the interpolation. These are *, + and -.
Create a new file, fade_away.dart, in the model directory:
import 'dart:ui';
class FadeAway {
  final Offset offset;
  final double opacity;
  const FadeAway(this.offset, this.opacity);
  FadeAway operator *(double multiplier) =>
      FadeAway(offset * multiplier, opacity * multiplier);
  FadeAway operator +(FadeAway other) =>
      FadeAway(offset + other.offset, opacity + other.opacity);
  FadeAway operator -(FadeAway other) =>
      FadeAway(offset - other.offset, opacity - other.opacity);
}
FadeAway implements all the mentioned operators to be able to animate it. You simply use Offset and double operators.
Go back to home_page.dart, import FadeAway and add these new variables:
late TweenSequence<FadeAway> _temperatureAnim;
late TweenSequence<FadeAway> _weatherDetailsAnim;
You’ll use TweenSequence since three phases make up the animation.
Init the TweenSequences at the end of _initThemeAnims():
_temperatureAnim = TweenSequence<FadeAway>([
  TweenSequenceItem(
    tween: Tween<FadeAway>(
      begin: const FadeAway(Offset(0, 0), 1.0),
      end: const FadeAway(Offset(-100, 0), 0.0),
    ).chain(CurveTween(curve: Curves.easeInOut)),
    weight: 40,
  ),
  TweenSequenceItem(
    tween: ConstantTween<FadeAway>(const FadeAway(Offset(-100, 0), 0.0)),
    weight: 20,
  ),
  TweenSequenceItem(
    tween: Tween<FadeAway>(
      begin: const FadeAway(Offset(-100, 0), 0.0),
      end: const FadeAway(Offset(0, 0), 1.0),
    ).chain(CurveTween(curve: Curves.easeInOut)),
    weight: 40,
  ),
]);
_weatherDetailsAnim = TweenSequence<FadeAway>([
  TweenSequenceItem(
    tween: Tween<FadeAway>(
      begin: const FadeAway(Offset(0, 0), 1.0),
      end: const FadeAway(Offset(100, 0), 0.0),
    ).chain(CurveTween(curve: Curves.easeInOut)),
    weight: 40,
  ),
  TweenSequenceItem(
    tween: ConstantTween<FadeAway>(const FadeAway(Offset(100, 0), 0.0)),
    weight: 20,
  ),
  TweenSequenceItem(
    tween: Tween<FadeAway>(
      begin: const FadeAway(Offset(100, 0), 0.0),
      end: const FadeAway(Offset(0, 0), 1.0),
    ).chain(CurveTween(curve: Curves.easeInOut)),
    weight: 40,
  ),
]);
Each TweenSequence has a different target position: Offset(-100, 0) for _temperatureAnim and Offset(100, 0) for _temperatureAnim. They move toward it, pause with ConstantTween and finally come back to their origin.
Next, update the call to TodayDetailsWidget() with:
TodayDetailsWidget(
  weatherData: todayWeather,
  progress: _animationController,
  temperatureTween: _temperatureAnim,
  detailsTween: _weatherDetailsAnim,
)
Just like SunWidget and MoonWidget, you’ll transform TodayDetailsWidget into an AnimatedWidget. Then, you’ll use your TweenSequence and _animationController to animate it.
Update TodayDetailsWidget:
// ...
import '../model/fade_away.dart';
class TodayDetailsWidget extends AnimatedWidget {
  final WeatherData weatherData;
  final Animatable<FadeAway> temperatureTween;
  final Animatable<FadeAway> detailsTween;
  const TodayDetailsWidget({Key? key,
    required this.weatherData,
    required Animation<double> progress,
    required this.temperatureTween,
    required this.detailsTween})
      : super(key: key, listenable: progress);
  Animation<double> get _animation => listenable as Animation<double>;
  @override
  Widget build(BuildContext context) {
    // 1
    final temperatureCurrentValue = temperatureTween.evaluate(_animation);
    final detailsCurrentValue = detailsTween.evaluate(_animation);
    final now = DateTime.now();
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        // 2
        Transform.translate(
          offset: temperatureCurrentValue.offset,
          child: Opacity(
            child: _temperature(context, now),
            opacity: temperatureCurrentValue.opacity,
          ),
        ),
        const SizedBox(
          width: 16,
        ),
        // 3
        Transform.translate(
          offset: detailsCurrentValue.offset,
          child: Opacity(
            child: _windAndWeatherText(context, now),
            opacity: detailsCurrentValue.opacity,
          ),
        ),
      ],
    );
  }
TodayDetailsWidget is a bit different from your previous AnimatedWidgets, especially in build():
- You use 
_animation‘s progress to evaluate eachTween-interpolatedFadeAwayobject instead of directly using the_animationvalue in your widgets. - You move and fade the temperature using 
temperatureCurrentValue. - You do the same with 
detailsCurrentValue. 
Also, notice that you declare temperatureTween and detailsTween as Animatable instead of TweenSequence. Both Tween and TweenSequence are Animatable, so you can use any of them without any impact on TodayDetailsWidget.
Remember that Tweens are not animated — only _animation changes over time. Here, you get their interpolated value thanks to evaluate().
Hot restart and play the animation.

Congratulations! You’ve completed all the planned animations.
Adding More Animations
Use the next exercises to test your new skills.
Add an Animation to BottomCard
Try to animate BottomCard by letting it rotate, bounce or scale when the theme changes, for instance.
Make a Startup Animation
You might need two AnimationController for this: one for the day/night transition, one for the startup animation. In this case, don’t forget to change SingleTickerProviderStateMixin to TickerProviderStateMixin.
You may add the following in your initState():
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
  // Do something on startup
});
It will launch at the startup of your widget, which is useful for a startup animation.
Improve CloudyWidget
Try to add some movements to the clouds, like you did for the sun and the moon. Here, consider using .repeat() instead of forward() on your AnimationController. TweenSequence is also a good fit for this animation.
Make It Rain or Snow
Since you know how to animate widgets, you can also simulate rain and snow. Make rectangles for the rain falling and white circles for the snow using Container. Then, animate them with Tween or TweenSequence that you’ll repeat().
Solution
A complete solution is available in the challenges project of this tutorial’s resources.
Build and run, then watch the animations take place. :]

Where to Go From Here?
Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.
This tutorial introduced you to staggered animations. After reviewing their main components, you can now chain many animations or make them overlap each other.
Check out the tutorial Flutter Canvas API: Getting Started to make even more custom animations. It teaches how to draw custom shapes, and even animate them!
You can also achieve great results with implicit animations. Learn how to do a radial menu in Implicit Animations in Flutter: Getting Started.
We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!