Flutter|为什么TweenAnimationBuilder和AnimatedBuilder有一个子参数?

216 阅读3分钟

如果你一直在Flutter中使用动画,你可能已经注意到,一些小工具同时使用childbuilder 参数。

这里有一个例子,使用TweenAnimationBuilder ,将一个Container 的颜色从红色变成绿色。

TweenAnimationBuilder<Color>(
  tween: Tween<Color>(begin: Colors.red, end: Colors.green),
  duration: const Duration(seconds: 1),
  // child is *optional* so we can pass null or omit it
  child: null,
  // builder is *required*
  // note that the third argument is an (optional) child
  builder: (BuildContext context, Color color, Widget? child) {
    return Container(color: color);
  },
)

如果我们愿意,我们可以完全省略child 参数,只要我们在builder 里面返回一个widget,事情还是可以的。

那么,为什么以及什么时候我们应该传递一个孩子?

答案是:性能优化

下面是官方AnimatedBuilder文档中的说法。

如果你的构建器函数包含一个不依赖于动画的子树,那么一次性构建该子树比在每个动画勾选时重建它更有效率。

如果你把预先构建的子树作为子参数传递,AnimatedBuilder 会把它传回给你的构建器函数,这样你就可以把它纳入你的构建。

使用这个预建子树完全是可选的,但在某些情况下可以显著提高性能,因此是一个好的做法。

换句话说,通过在构建器内使用子部件的传递,我们可以保证子部件只被构建一次,而不是在每个动画的勾选中

下面是另一个例子,返回一个旋转的Container

AnimatedBuilder(
  animation: someAnimation,
  // pass a child widget
  child: Container(color: Colors.red),
  // get the child back as a (nullable) Widget object
  builder: (BuildContext context, Widget? child) {
    // this rebuilds on every animation tick
    return Transform(
      alignment: Alignment.center,
      transform: Matrix4.rotationZ(2 * pi * someAnimation.value),
      // this is only built once
      child: child,
    );
  },
)

在这种情况下,我们使用一个AnimatedBuilder ,因为transform 的参数取决于动画的值。

但是Transform widget的子节点只需要建立一次,所以我们可以直接把它传递给AnimatedBuilder ,并在builder 的回调中重用它。

那么我们什么时候可以使用子参数呢?

答案是:当你有一个不依赖动画值的widget时

让我们再检查一下。

child: Container(color: Colors.red)

在这种情况下,Container 永远不会改变,所以它不需要在builder 里面重新构建。


让我们再来看看原来的TweenAnimationBuilder 的例子。

TweenAnimationBuilder<Color>(
  tween: Tween<Color>(begin: Colors.red, end: Colors.green),
  duration: const Duration(seconds: 1),
  builder: (BuildContext context, Color color, Widget? child) {
    return Container(color: color);
  },
)

在这种情况下,我们不能预先创建Container ,因为它的color 作为动画的一部分会发生变化。所以我们必须在builder 里面即时创建它。

除了TweenAnimationBuilderAnimatedBuilder ,其他Flutter小工具也遵循同样的惯例。例如ValueListenableBuilder ,当它的参数valueListenable 改变时,会自动重建。如果你正在使用某种FooBuilder小组件,请检查它是否有一个child 参数。

总结

我们现在知道什么时候应该使用child 参数,什么时候不应该。

再一次,这是一个性能优化

很多时候,首先测量性能,只有在需要时才优化代码是有意义的。

但是Flutter的动画API很容易使用,所以我们不妨先进行优化,在可以的时候使用child 参数。

编码愉快!