不知不觉,发现距离上一次发文章已经一年多了,自从换了新公司之后,每天都是挺充实的,额。。别找理由。。就是懒~所以今年重新捡起这个博客,继续不间断更新最近都在研究Flutter相关的东西,接下来的文章应该都围绕Flutter
平时我们在使用Widget的时候,构造方法都会有一个Key这么个参数,但是我们一般都不会传这个参数,那么这个参数到底有什么用呢? 下面就来研究下这个东东:
简介
我们先来看下官方对Key的介绍:
- 它是 Widgets, Elements and SemanticsNodes 的标识符
- 当新Widget的Key和Element相关联的当前Widget的Key相等时,才会将Element关联的Widget更新成最新的Widget
- 具有相同Parent的Elements,Key必须唯一
- 它有两个子类 LocalKey 和 GlobalKey
- 它决定一个Widget如何替换另一个Widget
- 如果两个Widget的runtimeType和key属性都相等的情况下,则新的widget通过更新Element(即通过使用新的Widget调用 [Element.update])来替换旧的Widget。否则,旧的element将从树中被移除,新的Widget将被扩充到一个新的Element中,这个新的Element将被插入树中。
- 用GlobalKey作为Widget的Key是时,Element可以在Tree周围移动(即改变Parent)而不丢失状态。当找到新的Widget(它的key和type与相同位置的先前Widget前不匹配),但在前一帧的树中的其他位置存在具有相同GlobalKey的Widget时,则将该Widget的Element移动到新的位置
- 一个Widget只有一个child的时候,这个child是不需要Key的
种类
通过上面的介绍我们大概能知道Key的作用,但是在Flutter中,有这么一些Key,我们先来看到底有哪些key:
Key
1 | abstract class Key { |
LocalKey继承自Key,它也是一个抽象类,只有一个构造方法
ValueKey
1 | class ValueKey<T> extends LocalKey { |
ValueKey集成自LocalKey,构造方法需要传入一个value,这个value是一个泛型类,重写了==方法和hash方法,只有runtimeType跟value都相等的情况下,ValueKey才会被认为相等
PageStorageKey
1 | class PageStorageKey<T> extends ValueKey<T> { |
PageStorageKey是继承自ValueKey,传入一个value,它是用于保存Scrollable的偏移
Scrollable(实际是ScrollPositions)使用PageStorage保存它们的滚动偏移,每次滚动完成时,存储的滚动信息都会更新。
PageStorage用于保存和恢复比widget生命周期更长的值,这些值存储在per-route Map中,它的key由widget及它的祖先的PageStorageKeys定义
ObjectKey
1 | class ObjectKey extends LocalKey { |
ObjectKey也是集成自LocalKey,而且构造方法也是需要传入一个value,但是这个value是Object类型的,也就是说可以传任意类型,identical 方法返回的是两个Object的hashCode是否相等,当runtimeType跟value.hashCode都相等的情况下,ObjectKey才会被认为相等,它跟ValueKey的区别在于它比较的是value的引用,而ValueKey是直接比较值
UniqueKey
1 | /// A key that is only equal to itself. |
UniqueKey也是继承自LocalKey, 而且只有一个构造方法,注释也写的很清楚了,它会生成一个唯一的hash,只跟自己相等
GlobalKey
简单介绍下:
- GlobalKey唯一标识Elements,它提供了与Element相关联的访问,如BuildContext、State(对于StatefulWidget)
- 不要在两个Widget中使用相同的GlobalKey
- Global keys 是很昂贵的,如果你不需要访问BuildContext、Element、State这些的话,请尽量使用[Key], [ValueKey], [ObjectKey] 或者 [UniqueKey]
1 | abstract class GlobalKey<T extends State<StatefulWidget>> extends Key { |
来看下实现,我去掉了一些用于debug和异常判断的代码,为了方便查看,GlobalKey继承自Key,一个工厂构造方法(可以传入一个用于debug的string,内部使用LabeledGlobalKey)和一个常量构造方法,内部有几个属性,意味着你可以访问到currentContext、currentContext和currentState(如果是StatefullWidget),有一个_register和_unregister方法,他们都在什么时候被调用呢?
在Element被mount到树上的时候调用_register,如果是类型是GlobalKey,那么Element就会加入到一个静态Map里,unmount的时候调用_unregister被移除
LabeledGlobalKey
1 | class LabeledGlobalKey<T extends State<StatefulWidget>> extends GlobalKey<T> { |
GlobalObjectKey继承自GlobalKey,提供一个value值,在构造的时候可以传入任意一个对象,当runtimeType跟value.hashCode都相等的时候GlobalObjectKey被认为相等
用法
ValueKey
当你要使用请求数据或者id作为唯一标识的时候就可以使用ValueKey
ObjectKey
当有一个对象的属性值有可能相同时,这时候你无法保证给 Key 的值每次都会不同,但是,当这些属性组合起来的 Object 将具有唯一性,这时你可以使用ObjectKey
UniqueKey
如果 ObjectKey 都无法满足唯一性的时候,又想要确保每一个 Key 都具有唯一性,那么,你可以使用 UniqueKey,它将会通过该对象生成一个具有唯一性的 hashCode
不过这样做,每次 Widget 被构建时都会去重新生成一个新的 UniqueKey
GlobalKey
当你想要跨Widget访问状态时,或者Element,就可以使用GlobalKey,但是它使用比较昂贵,一般情况下能用以上三种Key解决的,尽量不要使用它,特别是在列表中使用GlobalKey,这会造成性能的损失
PageStorageKey
可以记录滚动Widget的滚动偏移,如果你需要保存滚动的位置的时候可以使用PageStorageKey
最后更新: 2023年03月25日 22:39:55
本文链接: http://aeronxie.github.io/post/1ef5fef8.html
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可,转载请注明出处!