The paper


Animation effect will have a significant effect on improving user experience, a beautiful animation effect will make people’s eyes shine, anyway, it is comfortable to use, without the support of animation effect, the whole App will be very awkward and difficult to use. As for the animation of Flutter, as a white person, I am also new to it, so here is how animation is used in Futter.

This article is about how to implement a simple animation in Flutter, assuming that you already have some basic understanding of Flutter or some experience in Flutter development.


Build the foundation


Let’s use margin as an example in the Container component.

First of all, we first create a StatefulWidget stateful components, mixed SingleTickerProviderStateMixin need at the same time, as for SingleTickerProviderStateMixin What that is, we’ll talk about it later, but the code is as follows.

class FlutterAnimationWidget extends StatefulWidget {
  @override
  _FlutterAnimationWidgetState createState() => _FlutterAnimationWidgetState();
}

class _FlutterAnimationWidgetState extends State<FlutterAnimationWidget> with SingleTickerProviderStateMixin {* * * *}Copy the code

We then create a controlled Container and place it in a vertical layout, as shown below. The layout of trigger buttons is not described here and is not the focus of this article.

Container(
  width: 200,
  height: 50,
  color: Colors.orangeAccent,
  margin: EdgeInsets.only(top: 0),),Copy the code


Building animation


We declare the animation controller _animationController and margin’s top state property _marginTop, and transform the Container to make margin controlled by the value of _marginTop. The specific code is shown below.

  double _marginTop;
  AnimationController _animationController;
Copy the code
Container( ... margin: EdgeInsets.only(top: _marginTop), ... ) .Copy the code

Next we need to implement _animationController. Assuming we set the animation running time to 300 ms and the movement range of top to [0, 50], the code looks like this.

_marginTop = 0;
_animationController = AnimationController(duration: Duration(milliseconds: 300), vsync: this).. addListener(() { setState(() { _marginTop = _animationController.value *50.0;
  });
});
Copy the code

Here’s an illustration of the code, First, AnimationController, the animation control class, can only linearly generate values from 0 to 1 within a given period of time (the default range), so we need to map to the value we want to use value This property maps to the value that we want, which in this case is simply 0 to 50.

So can we change the default range? You can, of course, use lowerBound and upperBound. The above code can then be used as an example, with the same effect.

_animationController = AnimationController(duration: Duration(milliseconds: 300), lowerBound: 0, upperBound: 50, vsync: this).. addListener(() { setState(() { _marginTop = _animationController.value; }); });Copy the code

So what does it mean that vsync points to this? In a nutshell, using tickers (instead of timers) to drive animations prevents off-screen animations (when the animation’s UI is not on the current screen, such as when the screen is locked) from consuming unnecessary resources. Remember the article at first state components mixed SingleTickerProviderStateMixin? If there are multiple animation controllers within a component, you can use TickerProviderStateMixin.

Here is an excerpt from the description of Ticker in the Flutter Field.

A SchedulerBinding is used to add callbacks to every screen refresh. Ticker uses a SchedulerBinding to add callbacks to screen refresh. TickerCallback is called every time the screen is refreshed. Using Ticker(instead of Timer) to drive animations prevents off-screen animations (when the animation’s UI is not on the current screen, such as when the screen is locked) from consuming unnecessary resources because the screen refresh in the Flutter notifies the bound SchedulerBinding. The Ticker is driven by SchedulerBinding, and since the screen will stop refreshing after the lock, the Ticker will no longer trigger.

In the case of the addListener() method, a listener callback is added to the AnimationController and will be called once every frame. So this is where we call setState() to recreate the state component UI.

Of course, in addition to the addListener() frame listener callback method, there is also the addStatusListener() animation state listener, with the values shown below.

state instructions
AnimationStatus.dismissed This method is called back when the animation completes its reverse execution from Controller.reverse ()
AnimationStatus.forward Executing controller.forward() reverses this state
AnimationStatus.reverse Executing controller.reverse() calls back this state
AnimationStatus.completed This method is called back at the end of the animation’s forward execution from Controller.forward ()

When used, an example is shown below.

_animationController.addStatusListener((status) { 
  if (status == AnimationStatus.completed) print("Animation completed");
});
Copy the code

The animation controller is built, but don’t forget to release it at the end. The code is shown below.

@override
void dispose() {
  _animationController.dispose();
  super.dispose();
}
Copy the code


Perform the animation


Specifying the animation is relatively simple. The main methods are as follows.

methods instructions
forward() Start animation forward
reverse() Start animation in reverse
reset() Reset the animation to its initial state
dispose() Cancel/stop the animation

Here we just need to call forward() on the trigger time. For details, see the following.

void startEasyAnimation() {
  _animationController.forward();
}
Copy the code


The sample code


Since this article is relatively simple, we will post the sample code in the article and take what we need.

class FlutterAnimationWidget extends StatefulWidget {
  @override
  _FlutterAnimationWidgetState createState() => _FlutterAnimationWidgetState();
}

class _FlutterAnimationWidgetState extends State<FlutterAnimationWidget> with TickerProviderStateMixin {
  AnimationController _animationController;
  double _marginTop;

  @override
  void initState() {
    super.initState();
    _marginTop = 0;
    _animationController = AnimationController(duration: Duration(milliseconds: 300), lowerBound: 0, upperBound: 50, vsync: this).. addListener(() { setState(() { _marginTop = _animationController.value; }); }); _animationController.addStatusListener((status) {if (status == AnimationStatus.completed) print("Animation completed");
    });
  }

  void startEasyAnimation() {
    _animationController.forward();
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
              width: 200,
              height: 50,
              color: Colors.orangeAccent,
              margin: EdgeInsets.only(top: _marginTop),
            ),
            FlatButton(
              onPressed: startEasyAnimation,
              child: Text(
                "Click to execute simplest animation", style: TextStyle(color: Colors.black38), ), ), ], ), ), ); }}Copy the code


conclusion


OK, so much for the simplest animation using Flutter. In the future, SAO Dong will publish more articles about Flutter animation. Please pay attention to the comments.