build方法的设计是为了保持其纯净性,即不带有任何副作用。这是因为许多外部因素可能会触发新的小部件构建,例如:
- 路由弹出/推入
- 屏幕调整大小,通常是由于键盘出现或方向改变
- 父小部件重新创建其子小部件
- 依赖于小部件的InheritedWidget(
Class.of(context)
模式)发生变化
这意味着build
方法不应触发HTTP调用或修改任何状态。
这与问题有什么关系?
你面临的问题是你的构建方法具有副作用/不纯,导致多余的构建调用麻烦。
与其阻止构建调用,你应该使你的构建方法变得纯净,这样它可以随时被调用而不产生影响。
在你的例子中,你应该将你的小部件转换为一个 StatefulWidget,然后将那个 HTTP 调用提取到你的 State 的 initState 中:
class Example extends StatefulWidget {
@override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
Future<int> future;
@override
void initState() {
super.initState();
future = Future.value(42);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future,
builder: (context, snapshot) {
},
);
}
}
我已经知道这个。我来这里是因为我真的想要优化重建。
也可以制作一个小部件,使其能够重新构建而不强制它的子节点进行构建。
当小部件的实例保持不变时,Flutter会有意地不重新构建子节点。这意味着你可以缓存你的小部件树的部分内容,以防止不必要的重建。
最简单的方法是使用dart的`const`构造函数:
@override
Widget build(BuildContext context) {
return const DecoratedBox(
decoration: BoxDecoration(),
child: Text("Hello World"),
);
}
由于那个`const`关键字的存在,即使`build`方法被调用了很多次,`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收到新的值时,即使StreamBuilder/Column重建,
subtree
也不会重建。
这是因为,由于闭包的存在,
MyWidget
的实例没有改变。
这种模式在动画中经常使用。典型的用法有
AnimatedBuilder
和所有的过渡效果,比如
AlignTransition
。
你也可以将
subtree
存储在类的字段中,尽管不推荐这样做,因为它会破坏热重载功能。
.value
与不需要的小部件构建之间的关系,或者为什么build
方法需要是纯函数。 - Suragch