美文网首页Flutter记录自学flutter点点滴滴
Flutter 学习之旅(三十七) Flutter 动画(四)

Flutter 学习之旅(三十七) Flutter 动画(四)

作者: Tsm_2020 | 来源:发表于2020-09-23 11:03 被阅读0次

    前面介绍AnimatedWidget的时候虽然省略了一部分代码,但是如果自己封装动画的话,还是非常麻烦,这就需要引入新的封装,ImplicitlyAnimatedWidget , ImplicitlyAnimatedWidgetState,

    ImplicitlyAnimatedWidget

    abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
      /// Initializes fields for subclasses.
      ///
      /// The [curve] and [duration] arguments must not be null.
      const ImplicitlyAnimatedWidget({
        Key key,
        this.curve = Curves.linear,
        @required this.duration,
        this.onEnd,
      }) : assert(curve != null),
           assert(duration != null),
           super(key: key);
    
      /// The curve to apply when animating the parameters of this container.
      final Curve curve;
    
      /// The duration over which to animate the parameters of this container.
      final Duration duration;
    
      /// Called every time an animation completes.
      ///
      /// This can be useful to trigger additional actions (e.g. another animation)
      /// at the end of the current animation.
      final VoidCallback onEnd;
    
      @override
      ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState();
    
      @override
      void debugFillProperties(DiagnosticPropertiesBuilder properties) {
        super.debugFillProperties(properties);
        properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
      }
    }
    

    ImplicitlyAnimatedWidget 是一个抽象类,入参包括druation curve 和一个回调函数 onEnd

    ImplicitlyAnimatedWidgetState

    abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
      /// The animation controller driving this widget's implicit animations.
      @protected
      AnimationController get controller => _controller;
      AnimationController _controller;
    
      /// The animation driving this widget's implicit animations.
      Animation<double> get animation => _animation;
      Animation<double> _animation;
      
    
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController(
          duration: widget.duration,
          debugLabel: kDebugMode ? widget.toStringShort() : null,
          vsync: this,
        );
        _controller.addStatusListener((AnimationStatus status) {
          switch (status) {
            case AnimationStatus.completed:
              if (widget.onEnd != null)
                widget.onEnd();
              break;
            case AnimationStatus.dismissed:
            case AnimationStatus.forward:
            case AnimationStatus.reverse:
          }
        });
        _updateCurve();
        _constructTweens();
        didUpdateTweens();
      }
      
    
    ///更新控件
      @override
      void didUpdateWidget(T oldWidget) {
        super.didUpdateWidget(oldWidget);
        if (widget.curve != oldWidget.curve)
          _updateCurve();
        _controller.duration = widget.duration;
        if (_constructTweens()) {
          forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
            _updateTween(tween, targetValue);
            return tween;
          });
          _controller
            ..value = 0.0
            ..forward();
          didUpdateTweens();
        }
      }
    
    
    //更新curve
      void _updateCurve() {
        if (widget.curve != null)
          _animation = CurvedAnimation(parent: _controller, curve: widget.curve);
        else
          _animation = _controller;
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
    //是否需要启动动画,
      bool _shouldAnimateTween(Tween<dynamic> tween, dynamic targetValue) {
        return targetValue != (tween.end ?? tween.begin);
      }
      
    //更新tween
      void _updateTween(Tween<dynamic> tween, dynamic targetValue) {
        if (tween == null)
          return;
        tween
          ..begin = tween.evaluate(_animation)
          ..end = targetValue;
      }
    
      //创建Tween 
      bool _constructTweens() {
        bool shouldStartAnimation = false;
        forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
          if (targetValue != null) {
            tween ??= constructor(targetValue);
            if (_shouldAnimateTween(tween, targetValue))
              shouldStartAnimation = true;
          } else {
            tween = null;
          }
          return tween;
        });
        return shouldStartAnimation;
      }
    
      
      @protected
      void forEachTween(TweenVisitor<dynamic> visitor);
    
    
      @protected
      void didUpdateTweens() { }
    }
    

    这里值得分析的方法是initState

        _controller = AnimationController(
          duration: widget.duration,
          debugLabel: kDebugMode ? widget.toStringShort() : null,
          vsync: this,
        );
        _controller.addStatusListener((AnimationStatus status) {
          switch (status) {
            case AnimationStatus.completed:
              if (widget.onEnd != null)
                widget.onEnd();
              break;
            case AnimationStatus.dismissed:
            case AnimationStatus.forward:
            case AnimationStatus.reverse:
          }
        });
        _updateCurve();
        _constructTweens();
        didUpdateTweens();
    

    先创建AnimationController ,添加状态监听,如果动画执行完毕,并且有onEnd 回调,则执行onEnd回调,更新Curve ,如果设置了Curve ,通过Curve 创建Animation,如果没设置,则将AnimationController 变为Animation<double>,_constructTweens 这个方法是最有意思的方法,

       //是否会启用动画
      bool _constructTweens() {
        // 默认不启动
        bool shouldStartAnimation = false;
        //初始化Tween ,tween的职责就是规范动画的初始状态和结束状态,
        //他的其实状态分为两种情况,
        //1.tween 为空的时候,使用constructor 创建 这个是用户自定义的,begin 和end 用户定义
        //2.tween不为空,更新tween,而他的开始值begin是 tween.transform(animation.value),end 是targetValue  也是用户定义的
        //这也就解释了ImplicitlyAnimatedWidgetState中_updateTween 这个方法的作用
        forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
          if (targetValue != null) {
            tween ??= constructor(targetValue);
            if (_shouldAnimateTween(tween, targetValue))
              shouldStartAnimation = true;
          } else {
            tween = null;
          }
          return tween;
        });
        return shouldStartAnimation;
      }
    

    改变背景色例子

    class  TsmColorAnimatedDecorated  extends ImplicitlyAnimatedWidget  {
      final BoxDecoration decoration;
      final Widget child;
    
      TsmColorAnimatedDecorated({
        Key key,
        @required this.decoration,
        this.child,
        Curve curve = Curves.linear, //动画曲线
        @required Duration duration, // 正向动画执行时长
      }) : super(
        key: key,
        curve: curve,
        duration: duration,
      );
    
      @override
      ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState()=> _TsmColorAnimatedDecoratedState();
    }
    
    
    class _TsmColorAnimatedDecoratedState extends ImplicitlyAnimatedWidgetState<TsmColorAnimatedDecorated>  {
    
      DecorationTween _tween;
    
      @override
      void initState() {
        super.initState();
      }
    
    
    
      @override
      Widget build(BuildContext context) {
        return DecoratedBox(
          decoration: _tween.transform(animation.value),
          child: widget.child,
        );
      }
    
      @override
      void forEachTween(visitor) {
        _tween = visitor(_tween, widget.decoration,
                (value) => DecorationTween(begin: value));
      }
    }
    

    使用了ImplicitlyAnimatedWidget 后代码就非常简单了,

    Flutter 还定义了其他动画属性,方便大家使用

    AnimatedPadding 在padding发生变化时会执行过渡动画到新状态

    AnimatedPositioned 配合Stack一起使用,当定位状态发生变化时会执行过渡动画到新的状态。

    AnimatedOpacity 在透明度opacity发生变化时执行过渡动画到新状态

    AnimatedAlign 当alignment发生变化时会执行过渡动画到新的状态。

    AnimatedContainer 当Container属性发生变化时会执行过渡动画到新的状态。

    AnimatedDefaultTextStyle 当字体样式发生变化时,子组件中继承了该样式的文本组件会动态过渡到新样式。

    我学习flutter的整个过程都记录在里面了
    https://www.jianshu.com/c/36554cb4c804

    最后附上demo 地址

    https://github.com/tsm19911014/tsm_flutter

    相关文章

      网友评论

        本文标题:Flutter 学习之旅(三十七) Flutter 动画(四)

        本文链接:https://www.haomeiwen.com/subject/rwyfyktx.html