Flutter - 为什么推荐使用 Widget 而非 Helper Method

2,216 阅读3分钟

开头

相信大家都遇见过下面这种情况:

图1 无论 Columnchildren 中高度相似的部分有多复杂或者不复杂,聪明的你,应该知道有两种方式来拆分:

  1. 直接将代码拆成一个函数:

image.png 哇,看起来好多了,并且还能任意使用变量!

  1. 将代码收入一个单独的 Widget中:

image.png 看起来也好了许多,但代码量变多了!

该选择使用哪个呢?

虽然两种方式,用户都能正常使用各位优秀的工程师构建的应用程序,但答案是另创 Widget 是更好的,而且还是有重大因素的。

好处 1 - Class 有 const constructor

如果我们在创建 Text 对象时将其声明为 const,无论 build 被重复调用多少次,都不会重新创建 Text 对象,而是会使用同一个 Text 实例。而且没有创建自然也就不用回收,虽然单一 Widget 本身的创建和回收非常简单,但如果我们有一个相当复杂的 Widget 树,并且可能要每秒重建60次(例如动画中),还是应该尽可能地把可以声明为 const 的地方都设置成 const。所以不选择使用 Helper Method。

好处 2 - Flutter 不认识 Helper Method

当我们使用 Helper Method 时,Widget 树为这样:

image.png

而当我们拆出单独的 Widget 时,Widget 树为这样:

image.png 这会有什么问题呢?其实就是如图所示的这样,虽然在示例中没差多少,但当我们的 Widget 树复杂了之后,我们还是希望能更清楚的看到我们的每个 Widget 的结构的。同样的道理可以套用在 Crash 后查看 stack trace 时。

好处 3 - 可以更细致的 rebuild

请记住,当 Widget 内部调用 setState() 方法时,所属 build 方法整体都会重新运行。也就是说,如果你在一个很大型的 UI 界面的一隅执行了 setState() 方法,而这个改变只会影响很小一个部分的 UI。则 Flutter 需要重新构建整个包装的 Widget。可能有人会说,Flutter 使用多重树结构来构建用户界面,而 Widget 树结构只是顶层而已,于此层树结构新增小组件几乎不会拖慢应用程序。但是反观,在非必要刷新的地方进行 rebuild,可能导致 Element 树和 RenderObject 树这类更深层的树结构浪费掉宝贵的 CPU 时间。无谓的重绘宝贵用户的界面的频率会高达每秒 60 次,但实际上只需要绘制需要改变区域内的像素即可。

所以说回来,与其将你需要 setState() 方法的部分,放在 Helper Method 或一个很大型的 UI 界面内,不如为此创建新的 Widget。以便你在刷新状态的时候,Flutter 能够精确锁定你需要重新渲染的内容。同样为了取得最佳效果,尽可能使用常量构造函数(const constructor)。

好处 4 - 易于测试

这是最容易理解的了,Helper Method 不方便测试。

好处 5 - 不易产生和 BuildContext 相关的 Bug

我们来看如下代码:

image.png

build 方法中有 contextinnerContext,这样很可能在使用的时候会去使用旧的并且不稳定的 BuildContext,因此重建一个新 Widget 可以避免这样的问题:

image.png

综上

下次要拆解庞杂的 build 方法,不要忌惮去使用专门的 Widget!

开发过程中多费心一些,便能减少使用上卡顿的几率。

StackOverFlow 上对于该问题的讨论