这一篇想跟大家聊一个细节问题:为什么 build() 不在 StatefulWidget 里面而在 State 里面呢?
通常我们写一个无状态组件 StatelessWidget 是这样的:
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container();
}
}
有状态组件 StatefulWidget 把可变的状态变量放到了 State 对象中,参照 StatelessWidget 的方式,把 build() 方法放到 StatefulWidget 中行不行?比如这样:
class Foo extends StatefulWidget {
@override
Widget build(BuildContext context, State state) {
return Container();
}
}
看起来好像没啥问题,需要状态值就从 state
变量去取就好了,放在 StatefulWidget 中还符合习惯跟 StatelessWidget 是类似的。Fultter 没有这样去做的原因主要是从代码的封装和灵活性来考虑的。
我们知道 Flutter 中的组件 Widget 是可以继承的。一旦我们把某一种通用状态逻辑的 StatefulWidget 子类化,就会发现如果把 build() 放在 StatefulWidget 中将是一个不合理的实现方式。
这里举一个例子,比如显式动画组件的抽象类 AnimatedWidget,它内部的 State 负责处理了动画的监听事件和自动调用 setState 来使组件重绘。开发者在使用时需要重写 build() 来展示自己的 UI,如果像上面代码示例里 build() 在 StatefulWidget 中,那 AnimatedWidget 就必须在 build() 方法中把抽象类的 state 对象传递给子类。但是你应该发现了这里 state 是 AnimatedWidget 父类内部使用和实现的,子类不应该了解细节。而将 build() 放在 State 中则不会出现这样的问题,AnimatedWidget 声明一个 build() 让子类使用者去实现,然后在自己的 State 中去调用 widget.build(),代码的封装和灵活性都明显好多了。
这里再顺带一提官方文档里讲的那个闭包捕获的问题解释,说实话我觉得有点牵强例子没举好。官方文档在这里:https://api.flutter.dev/flutt...
有点意思的是我找到了当时关于这个问题的讨论:https://github.com/flutter/fl...
有人提出了和我一样的疑问:当父更新时,子组件 Widget 会重新创建是个新实例,如果说有闭包也是新的闭包哪来旧闭包问题一说?如下:
提出这个闭包问题的作者回复说这取决于是怎么用这个旧闭包的,有可能这个旧闭包被另外一个对象持有了。也没举出实际的场景和例子,有点牵强。
综上所述,build() 在 State 中确实是合理的。Flutter 中有很多这种设计上的权衡考虑,值得好好学习哇~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。