Flutter Animation动画开发之——AnimatedWidget

Flutter Animation动画开发之——最简单的动画入门这篇文章中我们介绍了创建一个动画的基本流程,其中每创建一个动画都要通过addListener来监听动画的每一帧,然后调用setState来刷新UI。我们今天要介绍的AnimatedWidget内部帮我们实现了这一步骤,其内部会监听动画然后更新UI。

AnimatedWidget示例

首先我们将需要执行动画的控件单独抽出来,继承AnimatedWidget自定义成一个动画控件,构造方法中的animation参数从外部接收一个Animation动画,记得传给父类构造方法的listenable参数,将动画的监听和更新UI交由父类处理

class MyAnimatedWidget extends AnimatedWidget {
  MyAnimatedWidget({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Center(
      // 这里显示一个绿色方块,随着动画的执行会不断变大
      child: Container(
        color: Colors.red,
        width: animation.value,
        height: animation.value,
      ),
    );
  }
}

然后在布局中使用该自定义的动画控件,传入动画参数,该动画无需再通过addListener监听变化来更新UI

class AnimationRoute extends StatefulWidget {
  @override
  AnimationRouteState createState() => AnimationRouteState();
}

class AnimationRouteState extends State<AnimationRoute> with SingleTickerProviderStateMixin {

  Animation<double> animation;
  AnimationController controller;

  initState() {
    super.initState();
    // Controller设置动画时长
    // vsync设置一个TickerProvider,当前State 混合了SingleTickerProviderStateMixin就是一个TickerProvider
    controller = AnimationController(
        duration: Duration(seconds: 5),
        vsync: this //
    );
    animation=CurvedAnimation(parent: controller, curve: Curves.bounceInOut);
    // Tween设置动画的区间值,animate()方法传入一个Animation,AnimationController继承Animation
    // 这里无需再通过addListener监听动画来更新UI
    animation = new Tween(begin: 50.0, end: 250.0).animate(animation);
    //启动动画(正向执行)
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return MyAnimatedWidget(animation: animation);
  }

  @override
  void dispose() {
    // 释放资源
    controller.dispose();
    super.dispose();
  }
}

AnimatedWidget源码解析

代码如下所示,主要包括AnimatedWidget和_AnimatedState这两个类,代码比较简单,我们主要看listenable这个变量,AnimatedWidget的核心就是通过监听listenable来更新UI的,listenable其实就是我们传入的动画,下面做简要说明

我们先看下AnimatedWidget的源码

  • listenable不能为空,所以自定义的子类必须将动画传给listenable
  • AnimatedWidget是一个有状态的控件,其状态由_AnimatedState管理

再看下_AnimatedState的源码

  • 在initState()方法中调用listenable.addListener(_handleChange)监听动画
  • 在_handleChange()方法中处理动画监听,调用setState()方法通知更新UI
  • 在build()方法中返回widget.build(),也就是自定义AnimatedWidget中重写build方法中自定义的UI
  • 在dispose()方法中调用listenable.removeListener(_handleChange)移除动画监听
abstract class AnimatedWidget extends StatefulWidget {
  /// Creates a widget that rebuilds when the given listenable changes.
  ///
  /// The [listenable] argument is required.
  const AnimatedWidget({
    Key key,
    @required this.listenable,
  }) : assert(listenable != null),
       super(key: key);

  /// The [Listenable] to which this widget is listening.
  ///
  /// Commonly an [Animation] or a [ChangeNotifier].
  final Listenable listenable;

  /// Override this method to build widgets that depend on the state of the
  /// listenable (e.g., the current value of the animation).
  @protected
  Widget build(BuildContext context);

  /// Subclasses typically do not override this method.
  @override
  _AnimatedState createState() => _AnimatedState();

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Listenable>('animation', listenable));
  }
}

class _AnimatedState extends State<AnimatedWidget> {
  @override
  void initState() {
    super.initState();
    widget.listenable.addListener(_handleChange);
  }

  @override
  void didUpdateWidget(AnimatedWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.listenable != oldWidget.listenable) {
      oldWidget.listenable.removeListener(_handleChange);
      widget.listenable.addListener(_handleChange);
    }
  }

  @override
  void dispose() {
    widget.listenable.removeListener(_handleChange);
    super.dispose();
  }

  void _handleChange() {
    setState(() {
      // The listenable's state is our build state, and it changed already.
    });
  }

  @override
  Widget build(BuildContext context) => widget.build(context);
}

 

 

 

 

posted @ 2019-11-06 09:34  野猿新一  阅读(52)  评论(0编辑  收藏  举报