Code first:

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: Material(child: ScaleAnimationRoute()),
    ),
  );
}
// You need to inherit from TickerProvider. If you have multiple AnimationControllers, you should use TickerProviderStateMixin.
class _ScaleAnimationRouteState extends State<ScaleAnimationRoute> with SingleTickerProviderStateMixin {
  Animation<double> animation;
  AnimationController controller;

  initState() {
    super.initState();
    controller = new AnimationController(
      duration: const Duration(seconds: 3),
      vsync: this,);// Image width and height changed from 0 to 300
    animation = new Tween(begin: 0.0, end: 300.0).animate(controller) //tag0
      ..addListener(() { //tag1
        setState(() => {});
      });
    // Start animation (forward execution)
    controller.forward(); //tag2
  }

  @override
  Widget build(BuildContext context) {
    return new Center(
      child: Image.asset(
        "assets/banner1.png",
        width: animation.value,
        height: animation.value,
      ),
    );
  }

  dispose() {
    // Animation resources need to be released during route destruction
    controller.dispose();
    super.dispose(); }}Copy the code

And then the image above:

Please enlarge the larger picture and start analyzing:

  1. fromtag0At the beginning, this step generates oneTween“And setbeginandendTwo properties, and then callanimateMethod, which creates one internally_AnimatedEvaluationObject, this is oneAnimationInstance, and then return the Settings to the referenceanimation, this step is performed at_AnimatedEvaluationInternally preservedTween.animateThe parameter passed to the methodAnimationControllerandTweenTheir own
  2. Then watchtag1, the step is called_AnimatedEvaluationtheaddListenerMethod,_AnimatedEvaluationIs the parent classAnimationWithParentMixinInherits the method in the parent classAnimationWithParentMixinThe implementation of this method is as follows:
  // keep these next five dartdocs in sync with the dartdocs in Animation<T>

  /// Calls the listener every time the value of the animation changes.
  ///
  /// Listeners can be removed with [removeListener].
  void addListener(VoidCallback listener) => parent.addListener(listener);
Copy the code

Parent is the AnimationController passed in by tween. animate, so the addListener method of AnimationController is used to listen for the original instance of the AnimationController that sent the animation data. The raw data is taken from the AnimationController, ranging from 0.0 to 1.0. Then look at the AnimationController class:

class AnimationController extends Animation<double>
  with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {
...
}
Copy the code

Omit unrelated code, only the inheritance structure, AnimationController addListener implementation within the superclass AnimationLocalListenersMixin, So the tag1 step saves the listening method to the AnimationController class 3. Then look at TAG2. After calling the AnimationController’s forward method to start the animation, the _animateToInternal method is executed internally. The animateToInternal process then executes notifyListeners and _checkStatusChanged internally to iterate over the value listeners and status listeners lists. Our listeners are then called back, where we execute the setState method refresh interface. Then we call animation.value in the build method of _ScaleAnimationRouteState to get the changed value. Then we analyze the animation.value step: 3.1 Animation is a deval of _Animatedeval. Therefore, we can refer to the “internal value” method of this class. The source code of the method is as follows:

  @override
  T get value => _evaluatable.evaluate(parent);
Copy the code

_evaluatable looks at tag0 to learn that the instance is Tween, and parent is the AnimationController instance, and then looks at Tween’s internal method, _evaluatable

  /// The current value of this object for the given [Animation].
  ///
  /// This function is implemented by deferring to [transform]. Subclasses that
  /// want to provide custom behavior should override [transform], not
  /// [evaluate].
  ///
  /// See also:
  ///
  ///  * [transform], which is similar but takes a `t` value directly instead of
  ///    an [Animation].
  ///  * [animate], which creates an [Animation] out of this object, continually
  ///    applying [evaluate].
  T evaluate(Animation<double> animation) => transform(animation.value);
Copy the code

The evaluate method internally executes a transform(animation.value), where animation is the AnimationController, The value range of the AnimationController’s animation is 0.0-1.0. Inside the Transform, it actually converts 0.0-1.0 to the corresponding value, that is, the page calls the animation(this is the animation of our test code, Tween’s transform can be deviled while the value can be deviled while the value can be deviled while the value can be deviled while the value can be deviled while the value can be deviled while the value can be deviled while the value can be deviled while the value can be deviled

@override T transform(double T) {if (T == 0.0) return begin; If (t == 1.0) return end; return lerp(t); }Copy the code

The [] in the picture is actually (), but the starUML can’t be typed with the parentheses behind it, so don’t get confused here.