欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Flutter Animation动画开发之——AnimatedBuilder

程序员文章站 2022-03-09 20:18:44
...

源码分析

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<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 //
    );
    // 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,
          ),
        );
      }
  );
}