LocalKey与GlobalKey的区别

LocalKey 也会复用 ElementState。实际上,这正是 LocalKey 最主要、最核心的作用。

GlobalKeyLocalKey 的区别不在于是否 复用 Element/State,而在于在多大的范围内寻找 那个需要被复用的 Element


LocalKey 是如何处理 Element 和 State 的?

为了理解这一点,我们需要简单了解一下 Flutter 的更新机制。

  1. Widget 树 vs. Element 树

    • 你写的代码是 Widget 树,它是一个"配置"或"蓝图",描述了 UI 应该长什么样。Widget 是不可变的(immutable)。
    • Flutter 内部维护一个 Element 树 。Element 是 Widget 的一个实例化对象,它持有对 Widget 和 State(如果是 StatefulWidget)的引用,并且是连接到最终渲染对象的桥梁。Element 是可变的,并且生命周期更长。
  2. 更新过程(Reconciliation / Diffing)

    • 当你调用 setState() 时,Flutter 会标记对应的 Element 为 "dirty" (需要更新)。
    • 在下一帧,Flutter 会重新执行 build 方法,生成一个新的 Widget 树。
    • 然后,Flutter 会遍历新旧 Widget 树(实际上是遍历新的 Widget 树,并与旧的 Element 树进行比较),来决定如何最高效地更新 Element 树。

现在,LocalKey 在这个比较过程中扮演了至关重要的角色。

场景一:没有 Key 的情况

当 Flutter 比较一个父 Widget 的新旧子节点列表时,如果没有 Key,它会按顺序和类型进行匹配。

例子:一个可删除的列表项

假设我们有一个列表,包含三个带状态的 StatefulTile(比如每个都有自己随机生成的颜色)。

初始状态:

  • Widget 树 : [StatefulTile(A), StatefulTile(B), StatefulTile(C)]
  • Element 树 : [Element(A), Element(B), Element(C)] (每个 Element 都持有自己的 State,比如颜色)

操作:删除第一个元素 A

setState 后,新的 build 方法返回:

  • 新 Widget 树 : [StatefulTile(B), StatefulTile(C)]

Flutter 的比对过程 (无 Key):

  1. 比较位置 0:

    • 新 Widget 是 StatefulTile(B)
    • 旧 Element 是 Element(A)
    • 类型匹配吗? 是的,都是 StatefulTile
    • 结果 :Flutter 认为你只是想更新 这个位置的 Widget。于是,它用 StatefulTile(B) 的配置去更新 Element(A)Element(A) 和它内部的 State (A的颜色) 被保留了下来!
  2. 比较位置 1:

    • 新 Widget 是 StatefulTile(C)
    • 旧 Element 是 Element(B)
    • 类型匹配吗? 是的。
    • 结果 :用 StatefulTile(C) 的配置去更新 Element(B)Element(B) 和它的 State (B的颜色) 被保留。
  3. 比较结束:

    • 旧的 Element(C) 没有被匹配到,于是它被销毁。

最终效果: 屏幕上显示了两个 Tile。第一个 Tile 的内容是 B,但颜色是 A 的颜色。第二个 Tile 的内容是 C,但颜色是 B 的颜色。状态错乱了!

场景二:使用 LocalKey 的情况

现在,我们给每个 StatefulTile 一个基于其内容的 LocalKey(比如 ValueKey)。

初始状态:

  • Widget 树 : [StatefulTile(key: ValueKey('A')), StatefulTile(key: ValueKey('B')), ...]
  • Element 树 : [Element(key: ValueKey('A')), Element(key: ValueKey('B')), ...]

操作:删除第一个元素 A

setState 后,新的 build 方法返回:

  • 新 Widget 树 : [StatefulTile(key: ValueKey('B')), StatefulTile(key: ValueKey('C'))]

Flutter 的比对过程 (有 Key):

  1. Flutter 拿到新的 Widget 列表 [Widget(B), Widget(C)] 和旧的 Element 列表 [Element(A), Element(B), Element(C)]
  2. 它不再仅仅按顺序比较,而是优先使用 Key 来查找匹配项
  3. 对于新的第一个 Widget StatefulTile(key: ValueKey('B')),Flutter 会在旧的兄弟节点中 寻找一个 Key 也是 ValueKey('B') 的 Element。
  4. 它找到了!是旧列表中的第二个 Element,Element(B)
  5. 结果 :Flutter 复用 Element(B)(连同它正确的 State),并把它移动到新的位置(位置 0)。
  6. 同理,对于新的第二个 Widget StatefulTile(key: ValueKey('C')),Flutter 找到了旧的 Element(C) 并复用它。
  7. 旧的 Element(A) 在新 Widget 列表中没有找到匹配的 Key,于是它被正确地销毁。

最终效果: 屏幕上正确地显示了 Tile B 和 Tile C,并且它们都保留了自己原有的、正确的状态(颜色)。


LocalKey vs GlobalKey 的复用机制对比

特性 LocalKey (如 ValueKey, ObjectKey) GlobalKey
查找范围 仅在兄弟节点之间 (Siblings)。Flutter 只会在同一个父 Widget 的子节点中查找匹配的 Key。 全局 (Entire App)。Flutter 会在整个应用范围内查找匹配的 Key。
核心目的 在一个集合(如 ListView, Column)内高效地 识别、重排、添加、删除 元素,并正确复用其 State 1. 跨越 Widget 树的层级,从外部访问一个 Widget 的 State。 2. 将一个 Widget 连同其 State 在不同的父节点之间移动
性能 。查找范围小,非常高效。 较低。需要维护一个全局的 Map,查找开销更大。

总结

  • LocalKeyGlobalKey 都会 触发 ElementState 的复用。
  • LocalKey 的作用域是局部的(兄弟节点之间) 。它是 Flutter 在处理列表、集合等动态 UI 时,保证状态正确性和渲染效率的基石。当你遇到列表项重排、删除后状态不正确的问题时,第一个就应该想到使用 LocalKey
  • GlobalKey 的作用域是全局的。它处理的是更特殊的情况,比如跨父级移动 Widget 或从任意位置访问特定 Widget 的状态,这是一种"降维打击",但开销也更大,需要谨慎使用。
相关推荐
lumi.2 分钟前
2.3零基础玩转uni-app轮播图:从入门到精通 (咸虾米总结)
java·开发语言·前端·vue.js·微信小程序·uni-app·vue
南雨北斗2 分钟前
ES6箭头函数的优势
前端
li理3 分钟前
鸿蒙Next组件扩展全面解析:从构建函数到样式复用的完整指南
前端·harmonyos
fly啊11 分钟前
前端 vs 后端请求:核心差异与实战对比
前端
陈哥聊测试16 分钟前
当DevOps落地实施撞上技术债务,如何量化债务突破困局
前端·自动化运维·devops
sorryhc21 分钟前
【AI解读源码系列】ant design mobile——CapsuleTabs胶囊选项卡
前端·javascript·react.js
狗头大军之江苏分军26 分钟前
频繁跳槽和稳定工作有什么区别?真的比稳定工作的人差吗?
前端·后端
木子雨廷29 分钟前
Flutter 局部刷新小组件汇总
前端·flutter
用户527096487449034 分钟前
组件库按需引入改造
前端
CryptoRzz1 小时前
使用Java对接印度股票市场API开发指南
前端·后端