likes
comments
collection
share

Flutter 为什么要将 build 方法放在 State 中?

作者站长头像
站长
· 阅读数 1

本人的 Flutter 知识还在储备当中,文中如有错误还望指正。

我的 Flutter 知识是通过 《Flutter实战》 入门的。书中提到一个问题:为什么要将 build 方法放在 State中,而不是放在 StatefulWidget 中?当然,作者给出了答案:

  1. 状态访问不便;
  2. 继承 StatefulWidget 不便。

详细内容可以查看原文

这个答案我看了数十遍,但是依然不知道为什么,毕竟 SwiftUI 并不需要区分 StatelessWidgetStatefulWidget,更不需要再多出一个 State 类,只需要在属性改变时需要刷新 UI 的属性前面用 @State 修饰一下就可以了。

在多次阅读《Flutter实战》之后,我才恍然大悟。如果让我给出一个答案,那就是:架构决定的。我们来梳理一下。

Widget 和 Element 的关系:一对多

在 Flutter 中真正代表屏幕上显示元素(UI 元素)的类是 Element,Widget 只是用来描述 Element 的配置数据。一个 Widget 可以对应多个 Element。比如,当同一个 Widget 对象被用来描述 UI 树的不同部分,在真正渲染时,UI 树上的这几个 Element 节点都会对应同一个 Widget 对象,也就是说根据同一份配置(Widget),创建了多个实例(Element)。

State 和 Widget 的关系:并非永久不变

State 实例只会在 Widget 第一次插入到 Widget 树中时被创建。对于每一个 State 对象,Flutter framework 只会调用一次 initState 方法。

State 的 widget 属性,表示与该 State 关联的 widget 实例,由 Flutter framework 动态设置。在应用生命周期中,如果 UI 树上的某一个节点的 widget 实例在重新构建时发生变化,Flutter framework 会动态设置 State 的 widget 属性为新的 widget 实例。

所以 State 和 Widget 的关系并是非永久不变的。

比如,我们可以用同一个StatefulWidget表示三个 Element,期间会生成三个 State。

Flutter 为什么要将 build 方法放在 State 中?

在更新时,ElementState 可能保持不变,但是 StatefulWidget 变成了新的。

Flutter 为什么要将 build 方法放在 State 中?

所以,不能在 State 的 initState 方法中获取 Widget 树相关的信息。比如:调用BuildContext.dependOnInheritedWidgetOfExactType,获取在 Widget 树上获取离当前widget 最近的一个父级 InheritFromWidget。因为在初始化完成后,Widget树中的InheritFromWidget 可能会发生变化,所以正确的做法是在 build() 或 didChangeDependencies() 中调用获取 Widget 树的相关信息。

State 的 initState 方法中应该做一些一次性的操作,如状态初始化、订阅子树的事件通知等。

结论

再回到开始的问题:为什么要将 build 方法放在 State 中,而不是放在 StatefulWidget 中?

我认为是架构决定的,最终绘制的 Element 其实是由 State 描述的,外界传递的配置信息(参数)存储在了 StatefulWidget 中,自身控制的配置信息(参数)存储在了 State 中,Flutter framework 负责将 StatefulWidget 作为 State 的 widget 属性,从而让 State 可以访问到所有的配置信息,因此将 build 方法放在了 State 中,理所当然。

为什么 SwiftUI 不用这么麻烦?我想是因为 SwiftUI 将外界传递的配置信息(参数)和自身特有的配置信息放在了一个 View 中,而 Flutter 可能基于性能的考虑(一个 Widget 可以对应多个 Element)将两者分开了,