Basic Animations in Flutter

Joshua Hall

While Flutter has an enormous amount of amazing packages for creating animation effects for your apps, there are also built-in methods for manually creating more fine-tuned animations.

Prerequisites

For our cross-screen animations I’ll be assuming that you already know how to create basic routes, just to keep things brief. You can review the docs here if you’re not comfortable with that just yet.

Linear Animations

The main three parts of our animations are the ticker to control our time, the controller to register our parameters like our duration, and then the values we want changed. Before our widget is rendered, in our initState, we can set our controller to its parameters, set its direction, and add a listener to reset our widgets state with every change.

By default a controller will move change from 0 to 1 in the time we set for our duration, we can print our controller.value in our listener to watch this happen. We can change our default start and end values by setting the upperBound or lowerBound properties.

main.dart

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;

  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 1),
      vsync: this); // Links this controller to this widget so it won't run if the parent widget isn't rendered, to save on resources.

    controller.forward();
    controller.addListener(() => setState(() {}));
  }
}

To use our animation we just need to set whatever we want, like opacity, to controller.value.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Opacity(
          opacity: controller.value,
          child: Container(width: 50, height: 50, color: Colors.red),
        ),
      ),
    );
  }

Curved Animations

Instead of boring linear animations we can use different curved variations to control exactly how our controller changes. To do this we can user CurvedAnimation to create a kind of wrapper over our original controller. This wrapper will take its parent, our controller, and the curve we want to apply. When using a curved animation we can’t have lower and upper bounds besides 0 and 1, so when we apply it we can just multiply its value. There’s a really extensive list of options over in the docs.

Another useful method on controller is addStatusListener, which will allow us to tap into its lifecycle. Our controller triggers a completed or dismissed event whenever it’s value has reached its upper or lower bound. We can use this with its forward and reverse methods to loop our animation infinitely.

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

  void initState() {
    super.initState();
    controller = AnimationController(
        duration: Duration(seconds: 1), 
        vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);

    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: Colors.red),
      ),
    );
  }
}

Tweens

Instead of just working with number values, we can also work with ranges of other things, like colors, using tweens (short for between). Like with our curved animation, it will act as a wrapper for our controller or animation and we can set our color to its value.

main.dart

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;
  Animation changeColor;

  void initState() {
    super.initState();
    controller =
        AnimationController(duration: Duration(seconds: 1), vsync: this);
    animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);
    changeColor = ColorTween(begin: Colors.red, end: Colors.blue).animate(animation);
    controller.forward();
    animation.addListener(() => setState(() {}));

    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) controller.reverse(from: 400);
      else if (status == AnimationStatus.dismissed) controller.forward();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          margin: EdgeInsets.only(bottom: animation.value * 400),
          width: 50,
          height: 50,
          color: changeColor.value),
      ),
    );
  }
}

Cross Screen

Another awesome technique is to use the Hero widget when we want a transition between widgets on different screens. We just need to wrap each in its own Hero widget with matching tags. The tags must be unique and there cannot be more than one on the screen at any time.

firstScreen

Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: <Widget>[
      SizedBox(height: 1),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: <Widget>[
          Hero(tag: 'icon', child: Icon(Icons.add)),
        ]),
      Navbar()
  ]));
}

secondScreen

Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        SizedBox(height: 1),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            SizedBox(width: 1),
            Hero(
              tag: 'icon',
              child: Icon(Icons.add, color: Colors.red, size: 75)),
          ]),
        Navbar()
      ]),
  );
}

Conclusion

With most frontend web technologies from the past, animations were left as an after-thought, but the flutter team did an amazing job at keeping animations in mind when developing this amazing framework. We really only scratched the surface of what Flutter is capable of in terms of animations.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#