Our Biggest Cyber Monday Sale — Ever!

Introducing unlimited access to all courses, all books, and our new monthly live professional development series! Just $899 $399 per year during our Cyber Monday event

Ends in... ::
Home Flutter Tutorials

Implicit Animations in Flutter: Getting Started

Learn how to make smooth-flowing and beautiful apps by adding implicit animations to your Flutter project’s buttons, containers and screen transitions.

4.7/5 3 Ratings

Version

  • Dart 2.10, Flutter 1.22, VS Code

Don’t you love apps that give you a rich and fluent experience? They’re such a pleasure to use. Every single transition, from opening the app to switching between pages to expanding an element, feels so good. How cool would it be to make that kind of app yourself? Well, you can! Today, you’ll create a Flutter app with some great effects using implicit animations.

By the end of this tutorial, you’ll learn about the following topics:

  • Animations in Flutter, with a focus on implicit animations.
  • Making a responsive TextField.
  • Adding a cross-fade between widgets.
  • Animating a page transition.
  • Creating an expanding radial menu.
  • Implementing a circular progress bar.
Note: This tutorial assumes that you have a basic knowledge of widgets. For help with the basics, please check the tutorials on Getting Started with Flutter and Responsive Design for Flutter.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Unzip the file after the download is complete. This tutorial uses Visual Studio Code, but you can also use Android Studio. If you choose Android Studio, some instructions may vary, so you’ll need to adapt those.

Open Visual Studio Code and click Open folder… on the welcome page, or select File ▸ Open… from the menu, then browse and choose the Starter folder from your unzipped download.

After opening the starter project, you will see a lot of errors in the code. Get rid of the errors by (1) double-clicking pubspec.yaml on the left-side panel and then (2) clicking the Get Packages button at the top of your screen. Alternatively, you could (3) open the Terminal in Visual Studio Code and (4) type flutter pub get, then click Enter.

get dependencies

Start the emulator of your choice, then build and run the app. At this point, you should see the following:

non-animated app

The starter project is an app with no animations. You’ll change this by giving your app some cool animations. Before taking that journey, here’s a short introduction to the app you’ll make:

The company Green Stationery promises to plant a tree to help the environment after a customer buys a certain number of products. This app sets the planting goals for you based on the stationery items you buy.

Understanding the Starter Project

The important files for this tutorial are:

  • lib/screens/greetings_screen.dart: This is the very first screen that you see when opening the app. It includes the text field to take the user name and a greeting card.
  • lib/screens/overview_screen.dart: This screen provides the details related to your consumption of stationery items and assigned planting goals.
  • lib/widgets/stationery_list.dart: This is a list widget that shows the consumption value of stationery types.
  • lib/widgets/statistics_widget.dart: This contains a stack widget with several other widgets as its children. It contains all the widgets shown on the overview screen except the stationery list widget.

Before doing any coding, though, it’s important to understand how animations work in Flutter.

Animations in Flutter

It’s important to understand a few things about motion and your smartphone screen’s refresh rate before starting. According to the Wikipedia article Frame rate, “The human visual system can process 10 to 12 images per second and perceive them individually, while higher rates are perceived as motion.”

Therefore, if you see more than 12 successive images of a moving ball every second, you’ll perceive them as motion rather than individual images. The more images per second, the smoother the motion. Today’s smartphones have a refresh rate of 60 frames per second or higher.

Animations in Flutter let you rebuild parts of your widget on every refresh of your device screen. To animate the widget in Flutter, you provide details such as when the animation should happen and how the properties of your widget should vary. The following demo will give you a better understanding.

Seeing an Animation in Slow Motion

Open the file lib/screens/demo/demo_screen.dart and replace the code with the following:

//1
import 'dart:async';
import 'package:flutter/material.dart';

class DemoScreen extends StatefulWidget {
  const DemoScreen({Key key}) : super(key: key);
  @override
  State createState() {
    return DemoScreenState();
  }
}

class DemoScreenState extends State {
  int steps = 0;

  void update() {
    //2
    Timer.periodic(
      const Duration(seconds: 1),
       //3
      (timer) => setState(
        () {
          steps < 20 ? steps = steps + 1 : timer.cancel();
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Animations Mystery!'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Stack(
          children: [
            Center(
              child: Container(
                //4
                width: 50.0 + steps * 5,
                height: 50.0 + steps * 5,
                color: Colors.blue[900],
              ),
            ),
            Align(
              alignment: Alignment.bottomRight,
              child: FloatingActionButton(
                  child: const Icon(Icons.add), onPressed: update),
            )
          ],
        ),
      ),
    );
  }
}

Here's what this code does:

  1. First you import the dart:async library, which supports asynchronous programming. It allows you to use Timer.
  2. Next, you're calling Timer.periodic(), which allows you to repeatedly trigger a callback function until you call Timer.cancel().
  3. This is the callback function that you're supplying to Timer.periodic(). It will be called repeatedly.
  4. Finally, these are the properties of the widget that you'll animate.

To see this code in action, open lib/main.dart and replace home: const GreetingsScreen() with:

home: const DemoScreen(),

Now, import the demo_screen.dart file as follows:

import 'package:green_stationery/screens/demo/demo_screen.dart';

Save the project, then build and run:

animation education using demo animation

When you click the floating button, the container size increases linearly every second. Animations in Flutter work similarly, but much faster and in a way more visually pleasing to the user.

The demo was temporary, so while you're still in lib/main.dart, replace home: const DemoScreen() with the following, as it was before:

home: const GreetingsScreen(),

Remove the unused import:

import 'package:green_stationery/screens/demo/demo_screen.dart';

Build and run the app again to make sure it works as before:

non-animated app

Still works. It's time to learn more about the types of animations that Flutter has.

Explicit and Implicit Animations

Broadly speaking, there are two types of animations in Flutter:

  • Drawing-based animation: These animations looks like someone drew them. They're hard to implement purely via code.
  • Code-based animation: These animations are widget-focused. They can be easily implemented purely via code. You'll look into a subtype in this tutorial.

Code-based animations have two types, as shown below:

code-based animation in Flutter

  • Implicit animations: These are animations that are already programmed. This means that you don’t need to write the animation code yourself; you only need to change some properties or values.
  • Explicit animations: These are animations that involve building customized animations using the AnimatedBuilder widget. This means that you can customize some properties or animation elements.
Note: This tutorial will focus on implicit animations. Read more about explicit animations in Flutter's official documentation.

Implicit animations are, once again, divided into two types:

  • AnimatedFoo: AnimatedFoo widgets are built-in implicit animation widgets. The Foo in AnimatedFoo represents the property you want to animate. For example, AnimatedSize animates the size. This type of animation is the easiest to implement.
  • Custom implicit animation: If you can't find a built-in implicit animation that meets your needs, you can use TweenAnimationBuilder to create a custom implicit animation.

Animating Buttons

AnimatedContainer is an animated version of Container. It's a powerful animation widget that allows animations on most properties of Container. You'll use this widget to change the color of the Let's go button on the initial screen from grey to green in an animated way, instead of having the same color all the time.

Open greetings_screen.dart in the screens folder. Scroll down until you find _userNameInput. Inside that method, you'll see the onChanged callback of TextFieldForm with a // TODO comment. Directly below that comment, add the following code:

if (v.length > 2) {
  setState(() {
    _color = Colors.green;
  });
} else {
  setState(() {
    _color = Colors.grey;
  });
}

The text field calls the onChanged callback whenever the user changes the text in the field. In this case, the _color field will set to green if there's more than two characters in the textfield. If there's less than or equal to two characters it'll be set to grey. You're using setState to tell the Flutter framework that it should rebuild the user interface.

While still in greetings_screen.dart, replace _getButton with the following code:

Widget _getButton() {
  // 1
  return AnimatedContainer(
    margin: const EdgeInsets.only(top: 10.0),
    // 2
    duration: const Duration(milliseconds: 900),
    padding: const EdgeInsets.all(16.0),
    decoration: BoxDecoration(
      // 3
      color: _color,
      borderRadius: const BorderRadius.all(
        Radius.circular(15.0),
      ),
      boxShadow: [
        BoxShadow(
            color: Colors.black.withOpacity(0.2),
            offset: const Offset(1.1, 1.1),
            blurRadius: 10.0),
      ],
    ),
    child: InkWell(
      onTap: () {
        if (name.length > 2) {
          if (!_currentFocus.hasPrimaryFocus) {
            _currentFocus.unfocus();
          }
          setState(() {
            // TODO
            _showNameLabel = true;
          });
        }
      },
      child: const Padding(
        padding: EdgeInsets.symmetric(horizontal: 6.0, vertical: 3.0),
        child: Text('Let\'s go'),
      ),
    ),
  );
}

Here is the explanation for the numbered comments:

  1. AnimatedContainer is the name of the animated version of Container.
  2. The duration parameter is a required parameter for any animation widget. It takes a Duration object, which tells the animation widget how long the animation should take to complete.
  3. You're assigning _color to the color parameter. A change in this variable triggers the animation. The animated widget then animates the color for the given duration.

Finally, change the initial color of the button to gray. Update the _color declaration at the top of GreeetingsScreenState to the following:

Color _color = Colors.grey;

Hot reload the app to see the difference:

Animated button using AnimatedContainer

Since you're using the AnimatedContainer class, any property changes on the AnimatedContainer will automatically animate without you having to specify any messy animation code. Pretty nifty, huh?

Making a Responsive TextField

Making a responsive button is good, but the user interface could be better. It's time to make the text field more responsive so that whenever the user selects it to type something, its width increases to the maximum.

To achieve this, you'll use AnimatedSize. This animation widget changes its size over a given duration of time whenever its child widget changes its size. All you need to do to use this widget is provide it a duration and a child.

To prepare for that change, first replace this line:

class GreetingsScreenState extends State<GreetingsScreen> {

with the following:

class GreetingsScreenState extends State<GreetingsScreen>
    with TickerProviderStateMixin {

You'll use TickerProviderStateMixin to provide a Ticker to your animation.

Add the following property to GreetingsScreenState:

bool _textFieldSelected = false;

Now, you're ready to add the animation. You're going to wrap the text field with AnimatedSize. To do that, replace _userNameInput with the following code:

Widget _userNameInput() {
  return AnimatedSize(
    // 1
    duration: const Duration(milliseconds: 500),
    // 2
    vsync: this,
    child: Container(
      width: _textFieldSelected ? double.infinity : 3 * _screenWidth / 4,
      padding: const EdgeInsets.symmetric(horizontal: 16.0),
      child: TextFieldForm(
        onTap: () {
          setState(() {
            // 3
            _textFieldSelected = true;
          });
        },
        onChanged: (v) {
          name = v;
          if (v.length > 2) {
            setState(() {
              _color = Colors.green;
            });
          } else {
            setState(() {
              _color = Colors.grey;
            });
          }
        },
      ),
    ),
  );
}

Here's an explanation of the code:

  1. duration sets the time the animation takes to move to full width and back again.
  2. vsync is a TickerProvider that will provide the Ticker to your animation widget. These objects are responsible for listening to the screen refresh rate of your smartphone and providing a refresh rate for the animations. Since you added TickerProviderStateMixin to this class, this handles it.
  3. When _textFieldSelected changes, the animation starts.

For the last piece of the puzzle, you'll use a gesture detector to detect when you click outside the text field.

Go to build in GreetingsScreenState and locate the onTap parameter of GestureDetector. Add the following lines to the top of the onTap callback:

setState(() {
  _textFieldSelected = false;
});

Tapping anywhere outside the text field will let the animator know it needs to return to its original state.

Build and run again:

responsive text form using AnimatedSize

This time, everything works as expected. Nice! :]

Using the Cross-fade Effect Between Widgets

The next animation widget you'll work with is AnimationCrossFade. This widget is a simple and fast way to get a cross-fade effect between widgets. Using it with widgets of the same size is straightforward but, if you have different widget sizes, you might need a custom layoutBuilder to avoid sudden jumps in animation.

You'll use AnimationCrossFade to fade in the greetings box and fade out the text field. Add the following to GreetingsScreenState:

bool _showWelcomeMessage = false;

Widget _getAnimatedCrossFade() {
  return AnimatedCrossFade(
    //1
    firstChild: _userInputField(),
    secondChild: _welcomeMessage(),
    //2
    crossFadeState: _showWelcomeMessage
        ? CrossFadeState.showSecond
        : CrossFadeState.showFirst,
    //3
    duration: const Duration(milliseconds: 900),
  );
}

Here's what you just did:

  1. The parameters firstChild and secondChild take the widgets you want to animate between.
  2. crossFadeState takes the child that will be visible after the animation has completed. When _showWelcomeMessage is true, it'll show the _welcomeMessage widget. Otherwise, _userInputField would be visible after the end of the animation.
  3. You define how long the animation will take.

Now, you need to make a couple of adjustments. Go to _getButton and find the // TODO comment. Replace _showNameLabel = true; with the following line:

_showWelcomeMessage = true;

Next, go to build, find the line !_showNameLabel ? _userInputField() : _welcomeMessage(), and replace it with the following:

_getAnimatedCrossFade(),

Now instead of abruptly switching between showing the name input field or the welcome message, you'll do a nice controlled cross fade from one to the other.

That's it. Save the file and re-run your app. You'll be able to see the cross-fade effect:

CrossFade effect using AnimationCrossFade

Animating Page Transitions

A smooth, unique transition from one screen to another provides a good user experience and can make your app stand out. It's preferable to have a smooth transition from one screen to another, rather than an abrupt jump.

In Flutter, you have a variety of options to customize page transitions. To keep things simple for this tutorial, you'll work with CupertinoPageRoute to add a sliding animation to the app.

While still in greeting_screen.dart, import the cupertino library:

import 'package:flutter/cupertino.dart';

Inside _welcomeMessage, find the // TODO: navigation comment inside onPressed. Replace the navigator there with the following:

Navigator.of(context).pushReplacement(
  CupertinoPageRoute(
    builder: (context) => const OverviewScreen(),
  ),
);

Your app now has an iOS-like slide transition. Save your file and hot reload the app to see the changes:

Cupertino page slide transition implicit animation

Creating a Radial Menu

A radial menu is a button that you can press that then expands to show menu options in a circular pattern out from the button. You're going to add one to the app now!

Radial menus are a great way to make your app more appealing. Some nice benefits of using a radial menu include:

  • You don't need to scroll too long to look for all the options, since they're equally spaced and accessible at the same time.
  • The actions are natural for touchscreen-based devices.
  • Radial menu actions are more convenient than traditional methods. Over time, interacting with the menu takes little effort.

Start off by updating the floating action button in the app to toggle between open and closed states.

Open overview_screen.dart. Delete _radialMenuCenterButton and replace it with the following:

bool _opened = false;

Widget _radialMenuCenterButton() {
  //1
  return InkWell(
    //2
    key: UniqueKey(),
    child: Padding(
      padding: const EdgeInsets.all(8.0),
      child: Container(
        height: 80.0,
        width: 80.0,
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(40.0),
            color: Colors.green[600]),
        child: Center(
          child: Icon(_opened == false ? Icons.add : Icons.close,
              color: Colors.white),
        ),
      ),
    ),
    onTap: () {
      //3
      setState(() {
        _opened == false ? _opened = true : _opened = false;
      });
    },
  );
}

Here's what you just did:

  1. You created a responsive area with the help of InkWell.
  2. UniqueKey() provides a unique key every time this widget rebuilds. You'll learn why you need this shortly.
  3. Whenever a user clicks the RadialMenuCenterButton, the value of the Boolean _opened flips. The framework will then rebuild this widget and its child widgets.

Adding AnimatedSwitcher to Your Radial Menu

To implement the radial menu, you'll use another powerful implicit animation widget — AnimatedSwitcher. It animates switching between two widgets. In this case, you're going to be switching between two versions of the floating action button - one that shows an add icon, and one that shows a close icon.

By default, the transition between the widgets is a cross-fade, which is the same animation that you get from AnimationCrossFade. You can read about other transitions in the Animation and motion widgets documentation. Look for FooTransition, where Foo is the property you're animating.

To implement AnimatedSwitcher, add _getRadialMenu to OverviewScreenState in overview_screen.dart, as shown below:

Widget _getRadialMenu() {
  return AnimatedSwitcher(
    // 1
    duration: const Duration(milliseconds: 300),
    // 2
    transitionBuilder: (child, animation) {
      return ScaleTransition(child: child, scale: animation);
    },
    // 3
    child: radialMenuCenterButton(),
  );
}

Here's what this code does:

  1. Similar to the other widgets, you assigned the duration over which the animation will happen.
  2. One thing that makes AnimatedSwitcher better than AnimationCrossFade is you can choose which type of transition you need when switching between widgets. Here, you use ScaleTransition.
  3. The child contains the widget that displays onscreen. When this widget changes, the transition triggers. For the animation to happen, both the widgets need to be different. Since you have the same kind of widget here, you use the UniqueKey() to differentiate between the two states.

You're almost done; just one more step. Go to build and look for the comment // TODO: Radial menu. Replace the line radialMenuCenterButton(); with the following code:

Align(
  alignment: Alignment.bottomRight,
  child: _getRadialMenu(),
),

Save and hot restart the app to see the changes:

radial menu center button using AnimationSwitcher

Note that the icon switches between add and close.

Creating the Radial Menu Widget

Next up, you're going to add the actual radial menu buttons. You're almost there!
Begin by creating a new file named radial_button.dart in widgets and add the following:

import 'package:flutter/material.dart';

class RadialButton extends StatefulWidget {
  final double hiddenHorizontalPlacement;
  final double hiddenVerticalPlacement;
  final double visibleHorizontalPlacement;
  final double visibleVerticalPlacement;
  final Color color;
  final IconData image;
  final Function onTap;
  final bool opened;
  const RadialButton(
      {Key key,
      this.hiddenHorizontalPlacement,
      this.hiddenVerticalPlacement,
      this.visibleHorizontalPlacement,
      this.visibleVerticalPlacement,
      this.color,
      this.image,
      this.onTap,
      this.opened})
      : super(key: key);
  @override
  State createState() {
    return RadialButtonState();
  }
}

class RadialButtonState extends State<RadialButton> {
  @override
  Widget build(BuildContext context) {
    return Positioned(
      left: widget.opened == false
          ? widget.hiddenHorizontalPlacement
          : widget.visibleHorizontalPlacement,
      bottom: widget.opened == false
          ? widget.hiddenVerticalPlacement
          : widget.visibleVerticalPlacement,
      child: InkWell(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Container(
              height: 60.0,
              width: 60.0,
              decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(40.0),
                  color: widget.color),
              child: Center(
                child: Icon(widget.image, color: Colors.white),
              ),
            ),
          ),
          onTap: widget.onTap),
    );
  }
}

That's a big chunk of code! Ultimately, RadialButton accepts a set of positions as constructor arguments that can be used to position the button somewhere on the screen. It also accepts some styling attributes as well as a tap listener and a boolean to tell whether it's opened or not.

Next, head back to overview_screen.dart and add the following imports:

import 'package:green_stationery/data/services/plant_statiotionery_convertor.dart';
import 'package:green_stationery/widgets/radial_button.dart';
import 'package:flutter_icons/flutter_icons.dart';

You'll add radial buttons as children of the Stack widget right before the Align widget that you added previously. You'll find the place by looking in build for the // TODO: Radial menu comment.

RadialButton(
  // 1
  hiddenHorizontalPlacement: _screenWidth - 90.0,
  visibleHorizontalPlacement: _screenWidth - 250.0,
  // 2
  hiddenVerticalPlacement: 10.0,
  visibleVerticalPlacement: 10.0,
  color: Colors.green[400],
  image: FontAwesome.book,
  opened: _opened,
  onTap: () {
    setState(() {
      //3
      PlantStationeryConvertor.addStationery(stationeryType: 'Books');
    });
  },
),

RadialButton(
  hiddenHorizontalPlacement: _screenWidth - 90.0,
  visibleHorizontalPlacement: _screenWidth - 90.0 - 139.0,
  hiddenVerticalPlacement: 10.0,
  visibleVerticalPlacement: 10.0 + 80,
  color: Colors.green[400],
  image: Entypo.brush,
  opened: _opened,
  onTap: () {
    setState(() {
      PlantStationeryConvertor.addStationery(stationeryType: 'Pens');
    });
  },
),

RadialButton(
  hiddenHorizontalPlacement: _screenWidth - 90.0,
  visibleHorizontalPlacement: _screenWidth - 90.0 - 80.0,
  hiddenVerticalPlacement: 10.0,
  visibleVerticalPlacement: 10.0 + 139.0,
  color: Colors.green[400],
  image: SimpleLineIcons.notebook,
  opened: _opened,
  onTap: () {
    setState(() {
      PlantStationeryConvertor.addStationery(
          stationeryType: 'Notebooks');
    });
  },
),

RadialButton(
  hiddenHorizontalPlacement: _screenWidth - 90.0,
  visibleHorizontalPlacement: _screenWidth - 90.0,
  hiddenVerticalPlacement: 10.0,
  visibleVerticalPlacement: 10.0 + 160.0,
  color: Colors.green[400],
  image: FontAwesome.archive,
  opened: _opened,
  onTap: () {
    setState(() {
      PlantStationeryConvertor.addStationery(
          stationeryType: 'Assiting Materials');
    });
  },
),

This may seem like a lot of code, but all that you're doing is adding four RadialButtons placed in an arc pattern around the floating action button.

Note the numbered comments in that code:

  1. Horizontal placement implies the distance from the left based on the width of the screen. Vertical placement implies the distance from the bottom based on the height of the screen.
  2. Being visible implies the radial button appears on the screen. Hidden implies that the radial button hides below the radial menu button.
  3. PlantStationeryConvertor.addStationery takes the stationeryType argument and increments the item number by one.

Build and run the app. Navigate to the overview screen and then tap the floating action button. You should see four additional buttons appear around the floating action button.

Adding AnimatedPositioned to the Radial Menu

Your Radial Menu is looking pretty good, but it needs an animation to match the rest of the application. To animate the menu buttons appearing, you're going to use the AnimatedPositioned widget.

AnimatedPositioned is the animated version of the Positioned widget. Use this widget to change the position and size of the widget. Here are a couple points to keep in mind while working with this widget:

  • It'll only work if its parent widget is Stack.
  • Although it's very useful, AnimatedPositioned is expensive, since it triggers a re-layout on every frame.

Navigate back to the radial_button file and replace the Positioned widget at the top of Build with an AnimatedPositioned widget. Then, add two new arguments to the new AnimatedPositioned:

duration: const Duration(milliseconds: 500),
curve: Curves.elasticIn,

The duration argument sets the duration for the animation.

The curve argument is more interesting. You can make your animation more realistic by adjusting its rate of change over time. This is where Curves comes in handy in Flutter.

That's it, you've finally implemented this complex animation! Save the project and build and run:

radial menu button using AnimatedPositioned

Implementing a Circular Progress Bar

As mentioned earlier, you may come across a situation where you want to develop a basic animation, but Flutter doesn't have the animated version of that property or widget. In these situations, try the custom implicit animation builder TweenAnimationBuilder. As you'll see, you don't need to call setState for this animation widget, unlike the other implicit animation widgets.

You'll use the following animation widget to create a circular progress bar in the app. Open statistics_widget.dart in widgets. Add the following method to StatisticsWidgetState:

Widget _tweenBuilder({
  double endValue,
  String statsName,
  Color color,
  double textSize,
}) {
  return TweenAnimationBuilder(
    // 1
    tween: Tween(begin: 0.0, end: endValue),
    // 2
    duration: const Duration(milliseconds: 1500),
    // 3
    curve: Curves.easeOutBack,
    // 4
    child: _labelWidget(statsName, textSize),
    // 5
    builder: (
      context,
      value,
      child,
    ) {
      return _circularProgressBar(
            color: color,
            value: value,
            labelWidget: child);
    },
  );
}

Here's an explanation of the numbered comments:

  1. The code passes a Tween object. Tween is a linear interpolation between the beginning value and the end value. You then pass these interpolated values to the property of the soon-to-be-animated widget.
  2. The code defines the duration of the widget's animation.
  3. Curves.easeOutBlack varies the rate of animation over the duration.
  4. In TweenAnimationBuilder, you can pass a widget in the child parameter that doesn't depend on the animation and is a child of the animated widget. This is a good practice since the child widget only builds once, unlike the animated widget, and results in better perfomance.

Before moving further, here are a few points that will be helpful while working with TweenAnimationBuilder:

  • You can trigger the animation any time in TweenAnimationBuilder by providing a new Tween.end value.
  • The new animation triggered by changing the value of Tween.end runs from the animation value at that moment to the new end value.

While still in StatisticsWidgetState, go to build and find the //TODO: tweenBuilder comment.

Delete this code:

_circularProgressBar(
  value: 0.4,
  color: Colors.blue,
  labelWidget: _labelWidget('Stationery Purchased', _textSize)),
_circularProgressBar(
  value: 0.3,
  color: Colors.green,
  labelWidget: _labelWidget('Planting Goal', _textSize)),

Replace the previous code with the following:

_tweenBuilder(
    statsName: 'Stationery Purchased',
    endValue: PlantStationeryConvertor.plantFraction,
    color: Colors.blue,
    textSize: _textSize),
_tweenBuilder(
    statsName: 'Planting Goal',
    endValue: PlantStationeryConvertor
            .plantingStatus.getPlantationGoal / 10,
    color: Colors.green,
    textSize: _textSize),

That's it. Save the project, and build and run to review all the things that you accomplished today! :]

circular progress bar using TweenAnimationiBuider

Where to Go From Here?

You can download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.

There are a few implicit animations that aren't covered in this tutorial. You can find them in Common implicitly animated widgets. Try adding more features into the app or replace used animation widgets with their alternatives, if possible.

Now is an ideal time to learn about the 12 Principles of Animations by the Disney animators Ollie Johnston and Frank Thomas. This will enable you to create more realistic and appealing animations in the future.

Also, check out the implicit animations video tutorial to get even more animation goodness in your life!

We hope you liked this tutorial. If you want more articles like this or have any questions, let us know by using the comments section below!

More like this

Contributors

Comments