Flutter Key

2020-09-08  本文已影响0人  Yue_Q

什么是key

Key 能够帮助开发者在 Widget tree 中保存状态。

Flutter | 深入浅出Key 中使用 key 来解决widget交换的问题?

@immutable
abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });
  final Key key;
  ···
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}

我们知道 Widget 只是一个配置且无法修改,而 Element 才是真正被使用的对象,并可以修改。

当新的 Widget 到来时将会调用 canUpdate 方法,来确定这个 Element 是否需要更新。

canUpdate 对两个(新老) WidgetruntimeTypekey 进行比较,从而判断出当前的 Element 是否需要更新。若 canUpdate 方法返回 true 说明不需要替换 Element,直接更新 Widget 就可以了。

StatelessWidget 内部中如何交换 Widget

只比较它们的 runtimeType。这里 runtimeType 一致,canUpdate 方法返回 true,两个 Widget 被交换了位置,StatelessElement 调用新持有 Widget 的 build 方法重新构建,在屏幕上两个 Widget 便被正确的交换了顺序。

  1. Row Widget 为它的子 Widget 提供了一组有序的插槽。对于每一个 Widget,Flutter 都会构建一个对应的 Element。构建的这个 Element Tree 相当简单,仅保存有关每个 Widget 类型的信息以及对子 Widget 的引用。你可以将这个 Element Tree 当做就像你的 Flutter App 的骨架。它展示了 App 的结构,但其他信息需要通过引用原始 Widget 来查找。
image.png

交换色块时,Flutter 遍历Widget 树。它从Row Widget 开始,然后移动到它的子 WidgetElement 树检查 Widget 是否与旧Widget是相同类型和 Key
canUpdate()它会更新对新 widget的引用。这里,Widget 没有设置Key,所以Flutter只是检查类型。它对第二个孩子做同样的事情。所以 Element 树将根据 Widget 树进行对应的更新。

image.png

StatefulWidget 中如何交换

color 的定义放在了State中,Widget 并不保存State,真正 hold State 的引用的是 Stateful Element。

image.png

(1) 交换控件的次序,Flutter 将遍历 Element 树,检查 Widget 树中 Row 控件并且更新Element树中的引用,然后第一个 Tile 控件检查它是相同类型,说明不需要更新 ElementElement 会根据当前 State 展示内容。所以颜色没有发生交换

image.png

StatefullWidget 结合 Key

添加 Key 之后的结构

image.png

(1) 当现在执行 swap 时, Element 数中 StatafulWidget 控件除了比较类型外,还会比较 key 是否相等:

image.png

只有类型和 key 都匹配时,才算找到对应的 Widget。于是在 Widget Tree 发生交换后,Element Tree 中子控件和原始控件对应关系就被打乱了,所以Flutter会重建 Element Tree,直到控件们正确对应上。

image.png

Element 位置被正确更新了

image.png

Where: 在哪设置 Key

如果把 Key 设置到内部会发生什么

@override
void initState() {
  super.initState();
  tiles = [
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulColorfulTile(key: UniqueKey()),
    ),
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulColorfulTile(key: UniqueKey()),
    ),
  ];
}

当点击按钮发生交换之后,可以看到两个色块的颜色会随机改变,但是我的预期是两个固定的颜色彼此交换。

为什么

当Widget 树中两个 Padding 发生了交换,它们包裹的色块也就发生了交换:


image.png

然后 Flutter 将进行检查,以便对 Element 树进行对应的更新: Flutter 的 Elemetn to Widget 匹配算法将一次只检查树的一个层级:

image.png

在第一级,Padding Widget 都正确匹配。


image.png

在第二级,Flutter 注意到 Tile 控件的Key` 不匹配,就停用该 Tile Element,删除 Widget 和 Element 之间的连接

image.png

解决问题的方法,将 Key 添加到 Padding

总结

  1. canUpdate() 方法根据(新老) WidgetruntimeTypekey 来更新 Widget && Element
  2. LocalKey 的意思是: 当 WidgetElement 匹配时,Flutter 只在树中特定级别内查找匹配的 Key。因此 Flutter 无法在同级中找到具有该 KeyTile Widget,所以它会创建一个新 Element 并初始化一个新 State。 就是这个原因,造成色块颜色发生随机改变,每次交换相当于生成了两个新的 Widget

理解 Flutter 中的 Key

上一篇下一篇

猜你喜欢

热点阅读