Flutter框架中Key的用途是什么?使用Key有哪些益处?
我完全懂你的感受——刚接触Flutter的时候,我也觉得Key是个可有可无的“多余参数”,直到踩了几个实实在在的坑,才明白它的价值!
简单来说,Flutter的Widget树和Element树是对应的,当Widget树更新时,Flutter会通过diff算法对比新旧Widget,决定是复用现有Element还是创建新的。Key的作用就是给Widget一个唯一标识,让Flutter能准确匹配新旧Widget对应的Element,避免状态复用错误。
下面是几个必须用到Key的场景,看完你就明白它的益处了:
避免同类型组件的状态混乱
当你在同一个父组件下有多个相同类型的有状态组件时(比如两个TextField、两个自定义开关按钮),如果没有Key,Flutter会根据组件的位置和类型来复用Element。举个例子:你给第一个TextField输入了“Hello”,第二个输入了“World”,如果交换它们的位置,没有Key的话,“Hello”会跑到第二个输入框的位置,“World”跑到第一个——因为Flutter以为只是组件位置变了,复用了原来的Element和它的状态。
给每个组件加上Key后,Flutter会通过Key识别出这是两个不同的组件,状态就会跟着组件走,不会乱套。处理动态列表的更新(排序、增删项)
如果你做的列表支持拖拽排序、或者动态添加/删除项,Key就必不可少了。比如你有一个Todo列表,每个项都有一个勾选状态,如果没有Key,当你拖拽某个项到新位置时,Flutter可能会复用原来位置的Element,导致勾选状态和实际的Todo项不匹配。
用ValueKey绑定每个Todo项的唯一ID,Flutter就能准确识别每个列表项对应的Element,更新时状态会和数据正确对应。跨树复用组件时保持状态
有时候你需要把一个组件从父组件A移到父组件B,没有Key的话,Flutter会销毁原来的Element并创建新的,组件的状态就会丢失。加上Key后,Flutter会把Element从原来的树中移到新的位置,状态得以保留。
举个简单的代码对比:
没有Key的情况(状态会混乱)
Row( children: [ TextField(decoration: InputDecoration(hintText: '第一个输入框')), TextField(decoration: InputDecoration(hintText: '第二个输入框')), ], )
加上Key的情况(状态跟随组件)
Row( children: [ TextField(key: ValueKey('first_input'), decoration: InputDecoration(hintText: '第一个输入框')), TextField(key: ValueKey('second_input'), decoration: InputDecoration(hintText: '第二个输入框')), ], )
总结一下:Key不是必须的,只有当你遇到状态混乱、列表更新异常、组件跨树移动需要保留状态这些场景时才需要用到它——这也是为什么你之前编码没用到它,因为你的场景还没触发这些问题。一旦遇到这些情况,Key就能帮你轻松解决那些看起来莫名其妙的状态问题!
内容的提问来源于stack exchange,提问作者Toni Joe

