该构建方法的设计是这样一种方式,它应该是纯/无副作用。这是因为许多外部因素都可以触发新的小部件构建,例如:
- 路线弹出/推入
- 屏幕尺寸调整,通常是由于键盘外观或方向改变
- 父小部件重新创建了它的子级
- 小部件所依赖的InheritedWidget(
Class.of(context)
模式更改)
这意味着该build
方法应该不会触发HTTP调用或修改任何状态。
这与问题有什么关系?
您面临的问题是您的构建方法有副作用/不是纯净的,这使多余的构建调用变得很麻烦。
不应阻止构建调用,而应使构建方法纯净,以便可以在没有影响的情况下随时调用它。
在您的例子中,你会改变你的工具到一个StatefulWidget
然后解压缩HTTP调用initState
你的State
:
class Example extends StatefulWidget {
@override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
Future<int> future;
@override
void initState() {
future = Future.value(42);
super.initState();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future,
builder: (context, snapshot) {
// create some layout here
},
);
}
}
我已经知道了 我来这里是因为我真的很想优化重建
也有可能使小部件能够重建而无需强迫其子代进行构建。
当小部件的实例保持不变时;Flutter故意不会重建孩子。这意味着您可以缓存小部件树的一部分,以防止不必要的重建。
最简单的方法是使用dart const
构造函数:
@override
Widget build(BuildContext context) {
return const DecoratedBox(
decoration: BoxDecoration(),
child: Text("Hello World"),
);
}
多亏了该const
关键字,DecoratedBox
即使调用了数百次构建,的实例也将保持不变。
但是您可以手动实现相同的结果:
@override
Widget build(BuildContext context) {
final subtree = MyWidget(
child: Text("Hello World")
);
return StreamBuilder<String>(
stream: stream,
initialData: "Foo",
builder: (context, snapshot) {
return Column(
children: <Widget>[
Text(snapshot.data),
subtree,
],
);
},
);
}
在此示例中,当StreamBuilder收到新值通知时,subtree
即使StreamBuilder / Column这样做也不会重建。发生这种情况的原因是,由于有了闭包,的实例MyWidget
没有改变。
动画中经常使用这种模式。通常的用途是AnimatedBuilder
和所有过渡,例如AlignTransition
。
您也可以将其存储subtree
到班级的某个字段中,尽管不建议这样做,因为它会破坏热重装功能。