Flutter Animation动画开发之——AnimatedBuilder

源码分析

在Flutter Animation动画开发之——AnimatedWidget这篇文章中我们介绍了AnimatedWidget的使用方法。今天要介绍的AnimatedBuilder其实是继承AnimatedWidget,所以功能与其类似,也是无需手动调用addListener监听动画然后调用setState来更新UI。其源码很简单,先做个简单介绍:

  • AnimatedBuilder继承AnimatedWidget
  • animation参数不为空,传入一个动画,然后传给AnimatedWidget的listenable,listenable在AnimatedWidget已介绍过,用于监听该动画,然后通知更新UI,就无需手动调用addListener监听动画然后调用setState更新UI
  • build参数不为空,传入一个build方法自定义动画起作用的控件
  • child是可选参数,有传的话,可以用于包裹在build方法返回的控件里面,作用可以详细看源码里child参数的说明,child是动画不起作用的子控件树,child由外部传入的话在每次动画更新重绘的时候就无需重绘该子控件树,可以提高效率
class AnimatedBuilder extends AnimatedWidget {
  /// Creates an animated builder.
  ///
  /// The [animation] and [builder] arguments must not be null.
  const AnimatedBuilder({
    Key key,
    @required Listenable animation,
    @required this.builder,
    this.child,
  }) : assert(animation != null),
       assert(builder != null),
       super(key: key, listenable: animation);

  /// Called every time the animation changes value.
  final TransitionBuilder builder;

  /// The child widget to pass to the [builder].
  ///
  /// If a [builder] callback's return value contains a subtree that does not
  /// depend on the animation, it's more efficient to build that subtree once
  /// instead of rebuilding it on every animation tick.
  ///
  /// If the pre-built subtree is passed as the [child] parameter, the
  /// [AnimatedBuilder] will pass it back to the [builder] function so that it
  /// can be incorporated into the build.
  ///
  /// Using this pre-built child is entirely optional, but can improve
  /// performance significantly in some cases and is therefore a good practice.
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return builder(context, child);
  }
}

示例代码

下面的代码演示了一个绿色方块,中间显示“野猿新一”四个字,方块的宽高从100变成200

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

class AnimationRouteState extends State with SingleTickerProviderStateMixin {

  Animation animation;
  AnimationController controller;

  initState() {
    super.initState();
    // Controller设置动画时长
    // vsync设置一个TickerProvider,当前State 混合了SingleTickerProviderStateMixin就是一个TickerProvider
    controller = AnimationController(
        duration: Duration(seconds: 5),
        vsync: this //
    );
    // Tween设置动画的区间值,animate()方法传入一个Animation,AnimationController继承Animation
    animation = new Tween(begin: 100.0, end: 200.0).animate(controller);
    //启动动画(正向执行)
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: animation,
        builder: (BuildContext ctx, Widget child) {
          return Center(
            child: Container(
              color: Colors.green,
              alignment: Alignment.center,
              width: animation.value,
              height: animation.value,
              child: Text('野猿新一',
                style: TextStyle(
                    color: Colors.black,
                    fontSize: 18.0,
                ),
              ),
            ),
          );
        }
    );
  }

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

方块中的Text('野猿新一')其实是不被动画影响的,因为动画只影响方块的宽高,但是上面代码的写法中当动画每次更新还是会重绘该Text。上面源码解析中我们提到AnimatedBuilder中还有一个child参数,可以传入不被动画影响的子控件树,我们可以把该Text传给child,这样改控件只会绘制一次,可以提高效率。

build方法修改如下,将Text('野猿新一')传给AnimatedBuilder的child参数,然后Container的child参数直接引用AnimatedBuilder的child

Widget build(BuildContext context) {
  return AnimatedBuilder(
      animation: animation,
      child: Text('野猿新一',
        style: TextStyle(
          color: Colors.black,
          fontSize: 18.0,
        ),
      ),
      builder: (BuildContext ctx, Widget child) {
        return Center(
          child: Container(
            color: Colors.green,
            alignment: Alignment.center,
            width: animation.value,
            height: animation.value,
            child: child,
          ),
        );
      }
  );
}

 

 

 

你可能感兴趣的:(Flutter)