如果你一直在Flutter中使用动画,你可能已经注意到,一些小工具同时使用child 和builder 参数。
这里有一个例子,使用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 里面即时创建它。
除了
TweenAnimationBuilder和AnimatedBuilder,其他Flutter小工具也遵循同样的惯例。例如ValueListenableBuilder,当它的参数valueListenable改变时,会自动重建。如果你正在使用某种FooBuilder小组件,请检查它是否有一个child参数。
总结
我们现在知道什么时候应该使用child 参数,什么时候不应该。
再一次,这是一个性能优化。
很多时候,首先测量性能,只有在需要时才优化代码是有意义的。
但是Flutter的动画API很容易使用,所以我们不妨先进行优化,在可以的时候使用child 参数。
编码愉快!