For example, the width of the Container can only change from 50.0 to 200.0, and the transparency can only change from 0 to 1. However, if you want it to repeat the process all the time, you can’t do it. So today explicit animation (manually controlled animation).

Explicit animation

Explicit animation requires the use of animation controller AnimaterController to achieve animation effects, you can start, stop, loop at any time, or multiple controls together to complete a move, zoom and other effects, to achieve a variety of animation effects, such controls are generally named xxxTransition. For example, RotationTransition, SlideTransition, FadeTransition, and so on. Also, if the built-in controls don’t meet your needs, You can also use AnimatedBuilder to create the animation you want, or if implicit animation and display animation are not enough, you can also use CustomPainter to create the animation you want using Canvan

1, RotationTransition

Let’s start with a simple rotation animation

@override Widget build(BuildContext context) {return Scaffold(appBar: appBar (title: Text(' explicit animation ')), body: Center( child: RotationTransition( turns: _controller, child: Container( width: 200, height: 150, color: Colors.orange, ) ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: (){ _controller.forward(); },),); }Copy the code

Turns turns turns turns turns turns turns turns turns turns turns turns turns Duration is passed in when the instance is initialized, or can be passed in or modified later if needed

You can also see in the code that we passed onevsync:thisAnd we’ve mixed in a class:SingleTickerProviderStateMixinThe vsync is vertical synchronous signal, into the SingleTickerProviderStateMixin source

We see that The Flutter helps us to create a Ticker. To literally explain that a Ticker is a clock, we know that some devices are 60Hz and some are 90Hz, and 60Hz devices render 60 times per second. A Ticker is a callback to each render. So the Controller will also refresh the control on every frame callback, and then the Controller will fill in some values for us between 0 and 1 (the default is 0 to 1). In the Builder we can get these values and complete the animation render interval. We can add a listener to print the value of the controller_controller.addListener(() { print(_controller.value); });Ticker also made some optimizations for us. For example, during the animation, we press Home to return to the device’s home screen, the animation stops, and when we return to the page the animation starts again. It should be noted that the Controller needs to be disposed when the page is destroyed, that is, dispose of the controller in the dispose method, call the forward() method of the controller to make the animation move. If you want to keep the animation rotating, The repeat() method is called. Reset means to reset, return to the original position, and stop means to stop. I said controller’s default animation is 0 to 1, what if we want it to go a quarter of the way around? There are two properties to set lowerBound and upperBound:

_controller = AnimationController(lowerBound: 0, upperBound: 0.25, duration: duration (seconds: 1), vsync: this);Copy the code

If our page defines multiple controller, cannot use SingleTickerProviderStateMixin, need to use TickerProviderStateMixin, in this case may run slower

2, FadeTransition

FadeTransition fades in and out in the same manner as the rotation above, except that it now passes a opacity property,

FadeTransition(
            opacity: _controller,
            child: Container(
              width: 200,
              height: 150,
              color: Colors.orange,
            )
)
Copy the code

3、 ScaleTransition

In the same way that moving SlideTransition scaling requires passing a position property, we find that we can’t pass SlideTransition the required position directly, because the default lowerBound for controller is 0, Turns: Tween(begin: 0.0, end: 1) upperBound turns: Tween(begin: 0.0, end: 1) 1). Animate (_controller), or: Turns: _controller.drive(begin: 0.0, end: 1.0)), it seems that the first of the two types is more common on the web, so the position of the moving animation can be written like this:

SlideTransition(position: Tween(begin: Offset(0, 0), end: Offset(0, 0.5)). Animate (_controller), child: Container( width: 200, height: 150, color: Colors.orange, ) )Copy the code

Here we pass the Tween end value to Offset(0, 0.5), which is 0.5 times its height, but not 0.5 unit length

Turns: Tween(begin: 0.0, end: 1.0). Animate (_controller) turns: Tween(begin: 0.0, end: 1.0).

SlideTransition(
            position: Tween(begin: Offset(0, 0), end: Offset(0, 1))
            .chain(CurveTween(curve: Curves.easeIn))
                .animate(_controller),
            child: Container(
              width: 200,
              height: 150,
              color: Colors.orange,
            )
        )
Copy the code

Curve setting An Interval can be set: Interval(0, 0.5) means that the animation is executed from 0 to 0.5 times the given time, followed by 0.5 to 1.0 times the idle time. When is this Interval used? Here’s a quick example:

double _interval = 0; @override Widget build(BuildContext context) {return Scaffold(appBar: appBar (title: Text(' explicit animation ')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: List.generate(5, (index) {_interval+= 0.2; return MoveBox(controller: _controller, color: Colors.orange[100 * (index + 1)], interval: The Interval (_interval - 0.2, _interval),); }), ), ), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: (){ _controller.repeat(reverse: true); // _controller.forward(); },),); }Copy the code
class MoveBox extends StatelessWidget { const MoveBox({ Key key, @required AnimationController controller, this.color, this.interval, }) : _controller = controller, super(key: key); final AnimationController _controller; final Color color; final Interval interval; @override Widget build(BuildContext context) { return SlideTransition( position: Tween(begin: Offset(0, 0), end: Offset(0.1, 0)). Chain (CurveTween(curve: Curves. interval)) .animate(_controller), child: Container( width: 300, height: 80, color: color, ) ); }}Copy the code

Interval is used to control when the animation starts.

In the previous implicit Animation article, we mentioned that you can use TweenAnimationBuilder if you want to customize the animation without using the controls that come with Flutter. You can also use AnimatedBuilder to customize the animation for display animation:

body: Center( child: AnimatedBuilder( animation: _controller, builder: (BuildContext context, Widget child) { return Opacity( opacity: _controller.value, child: Container(width: 150, height: 150, color: Colors.orange), ); })),Copy the code

Get a flicker animation

Copy the code

Opacity: _controller.value: opacity: 0; value: 0; 1.0).animate(_controller).value After you animate(_controller).value

Finally, let’s talk about Ticker in detail. When initializing controller, we need to mix a Ticker class and vsync:this to complete our animation. We can also implement a Ticker to complete the animation without binding Ticker:

@override
  void initState() {

    Ticker ticker = Ticker((_){
      setState(() {
        _height+=5;
        if (_height > 300) _height = 0;
      });
    });
    ticker.start();
    super.initState();
  }
Copy the code
body: Center(
        child: Container(
          width: 200,
          height: _height,
          color: Colors.orange,
        ),
      )
Copy the code

_height the initial value is 0

However, Flutter does not advise us to operate Ticker directly in this way, so it created a mixin class for us and made many optimizations for us. This animation takes 1 second on a 60Hz device and 0.5 seconds on a 120Hz device. So let’s use the ticker written by the system.