Flutter进阶篇-Local Key和Global Key

news2024/9/28 13:27:51

简介:
key是widget、element和semanticsNode的唯一标识,同一个parent下的所有element的key不能重复,但是在特定条件下可以在不同parent下使用相同的key,比如page1和page2都可以使用ValueKey(1) 。

常用key的UML关系图如上,整体上key分为两大类-LocalKey和GlobalKey,这两个key都是抽象类,LocalKey的实现类有 ValueKey、ObjectKey和UniqueKey,GlobalKey实现类有LabeledGlobalKey和GlobalObjectKey。

 

@immutable
abstract class Key {
  const factory Key(String value) = ValueKey<String>;


  @protected
  const Key.empty();
}

 Key是所有类型key的基类,内部实现了一个工厂构造函数,默认创建String类型的ValueKey。内部还实现了一个empty的构造函数,主要是给子类用的。

 

 外观是跟着Widget(在Flutter框架中Widget是immutable 不可改变的,一旦建立在运行的时候就不可改变它的值)走,State状态(在运行的时候可以改变,每次setState(() {})通知直接换掉旧的Widget而不是修改旧的Widget)是跟着Element Tree Instance 实例对象走,Element负责管理状态

一、Local Key

局部key,包含三种类型的key:ValueKeyObjectKeyUniqueKey

  1. ValueKey : 比较两个ValueKey的值是否相等,比较的是Value值是否相等,(有点类似于java中的equals),比如1,2,3。用在学生的id等唯一信息上。
  2. ObjectKey :以Object对象作为Key,比较两个ValueKey是否相等,比较的是Instance值是否相等,有点类似于c++指针的概念,对比的是在内存中是不是同一个Object,(有点类似于java中的== 恒等于号),通过指针地址来对比。new一个对象,对象的指针地址进行对比。
  3. UniqueKey:UniqueKey唯一的,可以保证Key的唯一性。使用之后就不存在Element的复用了,因为每次都是不同的。如果实在没有唯一标识了,可以使用UniqueKey来标识。
abstract class LocalKey extends Key {
  const LocalKey() : super.empty();
}
class ObjectKey extends LocalKey {
  /// Creates a key that uses [identical] on [value] for its [operator==].
  const ObjectKey(this.value); 
//如果是一个不复杂的String类型的Key时用identical的方式比较内存中两个指针是否相同

  /// The object whose identity is used by this key's [operator==].
  final Object? value;

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    return other is ObjectKey
        && identical(other.value, value);
  }
class Student {
  final String name;
  final String age;

  const Student({required this.name, required this.age});

  @override
  bool operator ==(covariant Student other) {
    //identical 检测在内存中是不是同一个东西
    if (identical(this, other)) return true;

    return
      other.name == name &&
      other.age == age;
  }

  @override
  int get hashCode => name.hashCode ^ age.hashCode;
}

二、Global Key

GlobalKey是全局唯一的,其默认实现是LabeledGlobalKey,所以每次创建的都是新GlobalKey。所有的GlobalKey都保存在BuildOwner类中的一个map里,此map的key为GlobalKey,此map的value则为GlobalKey关联的element。

对于GlobalKey,需要知道如下几点:

当拥有GlobalKey的widget从tree的一个位置上移动到另一个位置时,需要reparent它的子树。为了reparent它的子树,必须在一个动画帧里完成从旧位置移动到新位置的操作。
上面说到的reparent操作是昂贵的,因为要调用所有相关联的State和所有子节点的deactive方法,并且所有依赖InheritedWidget的widget去重建。
不要在build方法里创建GlobalKey,性能肯定不好,而且也容易出现意想不到的异常,比如子树里的GestureDetector可能会由于每次build时重新创建GlobalKey而无法继续追踪手势事件。
GlobalKey提供了访问其关联的Element和State的方法。

abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
  ///这里的debugLabel仅仅为了debug时使用
  factory GlobalKey({ String? debugLabel }) => LabeledGlobalKey<T>(debugLabel);


  ///给子类使用的
  const GlobalKey.constructor() : super.empty();


  Element? get _currentElement => WidgetsBinding.instance!.buildOwner!._globalKeyRegistry[this];


  BuildContext? get currentContext => _currentElement;


  Widget? get currentWidget => _currentElement?.widget;


  T? get currentState {
    final Element? element = _currentElement;
    if (element is StatefulElement) {
      final StatefulElement statefulElement = element;
      final State state = statefulElement.state;
      if (state is T)
        return state;
    }
    return null;
 

其和Key类差不多,也有一个工厂构造函数,默认创建的是LabeledGlobalKey,其构造函数的debugLabel仅仅是为了debug时使用,并不会用来标识element。

如何获取其关联的element?从源码来看,其直接访问的是BuildOwner里用来保存GlobalKey和Element对应关系的map。获取到了其关联的element,那么就能获取到其对应的widget以及state,详细的可以看上面的源码。

需要注意的是其并没有重写和hashCode方法,构造函数也没有被const修饰,这也就使LabeledGlobalKey天然就是全局唯一的

LabeledGlobalKey

这是GlobalKey的默认实现,内部仅有一个debugLabel属性,其他的也没啥。

class LabeledGlobalKey<T extends State<StatefulWidget>> extends GlobalKey<T> {
  // ignore: prefer_const_constructors_in_immutables , never use const for this class
  LabeledGlobalKey(this._debugLabel) : super.constructor();


  final String? _debugLabel;
}

GlobalObjectKey

class GlobalObjectKey<T extends State<StatefulWidget>> extends GlobalKey<T> {
  const GlobalObjectKey(this.value) : super.constructor();


  final Object value;


  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    return other is GlobalObjectKey<T>
        && identical(other.value, value);
  }


  @override
  int get hashCode => identityHashCode(value);

特殊的GlobalKey,重写了==和hashCode方法,内部维护了一个Object对象,通过判断此Object是否指向同一块内存地址来判断两个GlobalObjectKey是否相等。

GlobalKey被要求全局唯一,其默认实现LabeledGloalKey因为其并没有重写==和hashCode方法,也不支持const构造函数,所以天然是全局唯一的。但是GlobalObjectKey不然,如果有两个或者多个地方使用到了拥有同一个Object的GlobalObjectKey,那么就不能保证其全局唯一性,造成程序出错。此时,可以继承GlobalObjectKey,实现一个private的内部类,比如:

class _MyGlobalObjectKey extends GlobalObjectKey {
  const _MyGlobalObjectKey(Object value) : super(value);
}

总结:


Flutter里的key分为两类:

  1. LocalKey,实现类有ValueKey、ObjectKey、UniqueKey;
  2. GlobalKey,实现类有LabeledGlobalKey、GlobalObjectKey。


Key是所有keys类的基类,其默认实现是String类型的ValueKey。


相同parent下的key是不能一样的,比如不能再同一个page里使用VlaueKey(1),但是不同parent下是可以存在一样的key的,比如在两个界面里都使用ValueKey(1)。
UniqueKey只和自己相等,其并没有重写==和hashCode方法,也没有const修饰的构造函数。当调用Element的updateChild方法时,Widget.canUpdate肯定返回false,所以如果你想让widget每次都去创建新的element而不复用old element,那么就给此widget使用UniqueKey。


GlobalKey的默认实现是LabeledGlobalKey,其没有实现==和hashCode方法,也没有const修饰的构造函数,所以肯定能保证其全局唯一性。
所有的GlobalKey都保存在BuildOwner类中,其内部维护了一个map用来保存GlobalKey与其对应的Element。
GlobalObjectKey是特殊的GlobalKey,内部维护了一个Object属性,并实现了== 和hashCode方法,通过判断runtimeType以及Object属性是否一致来判断两个GlobalObjectKey是否相等。
使用GlobalObjectKey时,为了保证GlobalObjectKey的全局唯一性,最佳实践是继承自GlobalObjectKey实现一个private的内部类,可以有效避免多人开发时可能造成的GlobalObjectKey冲突的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/637117.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云安装2019版sql server服务

1、添加 Microsoft 的 yum 存储库 [rootiZ22312ginudnbnifn438Z ~]# curl -o /etc/yum.repos.d/mssql-server.repo https://packages.microsoft.com/config/rhel/7/mssql-server-2019.repo 这里地址一定要选择https://packages.microsoft.com/config/rhel/7/mssql-server-2019…

华为OD机试真题 JavaScript 实现【记票统计】【牛客练习题】

一、题目描述 请实现一个计票统计系统。你会收到很多投票&#xff0c;其中有合法的也有不合法的&#xff0c;请统计每个候选人得票的数量以及不合法的票数。 &#xff08;注&#xff1a;不合法的投票指的是投票的名字不存在n个候选人的名字中&#xff01;&#xff01;&#x…

JavaScript DOM

1、DOM介绍 DOM(Document Object Model)&#xff1a;文档对象模型。 将 HTML 文档的各个组成部分&#xff0c;封装为对象。借助这些对象&#xff0c;可以对 HTML 文档进行增删改查的动态操作。 1.1、Element元素的获取操作 具体方法 方法名说明getElementById (id 属性值)根…

原因分析必知必会的十大要点

原因分析是对选定的现象进行全面深入的研究&#xff0c;找到现象背后的真正原因与深层次原因&#xff0c;然后采取合适的措施纠正问题、预防问题。如果没有找到真正的根因就采取措施&#xff0c;往往事倍功半&#xff0c;浪费了投入。那么在原因分析时&#xff0c;有哪些成功要…

adb 导入导出安卓设备里面的apk和文件

安卓设备上导出apk到电脑 &#xff1a; 第一条指令查&#xff1a;adb shell pm list package -3 //列出所有非系统三方应用如下 package:com.sangfor.vpn.client.phonepackage:com.spd.mdm.other.funcpackage:com.supcon.supplant第二条指令查路径&#xff1a;adb shell pm pa…

容器(第七篇)docker-consul

consul服务器&#xff1a; 1. 建立 Consul 服务 mkdir /opt/consul cp consul_0.9.2_linux_amd64.zip /opt/consul cd /opt/consul unzip consul_0.9.2_linux_amd64.zip mv consul /usr/local/bin/ //设置代理&#xff0c;在后台启动 consul 服务端 consul agent \ -server \…

python安装后的几个默认目录问题

python.exe位置 C:\Users\dao\AppData\Local\Programs\Python\Python311 pip默认安装的文件位置 C:\Users\dao\AppData\Local\Programs\Python\Python311\Lib\site-packages

51单片机数码管秒表仿真设计详解

51单片机数码管秒表仿真 功能说明&#xff1a; 1、4位数码管显示 2、开始暂停清零按钮控制 3、51最小单片机系统电路 4、计时功能&#xff0c;0到99.99秒计时 运行效果 #include<reg52.h>#define uchar unsigned char #define uint unsigned intuchar code Tab0[] …

山西电力市场日前价格预测【2023-06-13】

日前价格预测 预测明日&#xff08;2023-06-13&#xff09;山西电力市场全天平均日前电价为383.16元/MWh。其中&#xff0c;最高日前电价为621.63元/MWh&#xff0c;预计出现在20: 45。最低日前电价为249.69元/MWh&#xff0c;预计出现在12: 45。 以上预测仅供学习参考&#x…

京东购物车分页方案探索和落地 | 京东云技术团队

随着京东购物车应用场景的丰富化和加车渠道的多元化&#xff0c;京东购物车的商品容量从2015年至今一直在逐步增加。 2015年京东购物车由80件扩容到120件&#xff1b;2018年由120件扩容到150件&#xff1b;2020年由150件扩容到180件&#xff1b;2021年京东PLUS会员扩容到了220…

Q1净亏损1.35亿元,4G增长乏力「困扰」车规通讯模组龙头

“汽车前装从4G到5G的切换需要一定的时间&#xff0c;现阶段&#xff0c;5G的价格更高&#xff0c;国内客户从4G向5G的转换会受到一些短期影响。”这是车载通讯模组市场龙头企业—移远通信在近日投资者互动上的公开表态。 4G渗透率趋于稳定&#xff0c;5G增速不及预期&#xff…

SSH科普

参考来源&#xff1a;https://zhuanlan.zhihu.com/p/323322650 目录 1. 什么是SSH&#xff1f;2. SSH登录原理3. SSH基本用法4. SSH远程登录实例5. SSH端口转发1. 转发的参数2. 本地转发3. 远程转发 6. SSH的远程操作7. SSH的本地转发8. SSH的远程转发利用远程转发&#xff0c…

国内唯一!腾讯入选全球零信任厂商全景图

近日&#xff0c;国际权威机构Forrester发布《The Zero Trust Platforms Landscape, Q2 2023》&#xff08;以下简称“报告”&#xff09;&#xff0c;对全球29家零信任方案供应商进行了综合性评估。腾讯凭借iOA零信任安全解决方案入选报告&#xff0c;成为了国内唯一入选的零信…

ChatGPT 未来会对游戏行业带来哪些影响?

ChatGPT的出现&#xff0c;为整个游戏行业带来了新的思考。 ChatGPT是由微软发布的一个大型语言模型&#xff0c;在被训练之后能够进行对话式文本生成&#xff0c;目前已在多个领域取得了不小的进展&#xff0c;包括聊天机器人、智能客服等。 作为一款大型语言模型&#xff0c…

黑客和网络安全工程师有什么区别?如何成为一名网络安全工程师?

经常有小伙伴把黑客和网络安全工程师弄混&#xff0c;黑客和网络安全工程师是两种不同的职业&#xff0c;尽管它们都与计算机安全有关。本篇文章将告诉你黑客和网络安全工程师的区别并且教你如何成为一名网络安全工程师。 黑客和网络安全工程师有什么区别&#xff1f; 黑客通…

PyTorch 深度学习入门

&#x1f482; 个人网站:【 海拥】【萌怪大冒险】【2048】&#x1f91f; 风趣幽默的前端学习课程&#xff1a;&#x1f449;28个案例趣学前端&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼大军】&#x1f4ac; 免费且实用的计算机相关知识题库&…

比亚迪首谈智驾进展:年内量产大算力域控,感知大模型上车

作者 | 马波 编辑 | 德新 前不久&#xff0c;HiEV独家报道了比亚迪在智能驾驶领域的最新量产计划 。 比亚迪将会在今年第三季度&#xff0c;落地基于地平线征程5的高阶智驾方案&#xff0c;首项高阶智驾功能为高速导航辅助驾驶DNP&#xff0c;首发车型大概率是汉。 王传福曾…

[centos]centos7卸载显卡驱动

找到显卡驱动.run文件&#xff1a; sudo ./NVIDIA-Linux-x86_64-460.106.00-k80.run --uninstall 上面是通过run文件卸载可以很容易卸载干净&#xff0c;强烈推荐这个方法&#xff0c;如果没有可以使用命令行 yum remove nvidia-* rpm -qa |grep -i nvid|sort yum remove kmod…

跨域问题详解

本文从web开发者角度&#xff0c;浅谈跨域原理&#xff0c;总结处理方法。为什么会有跨域问题&#xff1f; 简单来说&#xff0c;浏览器不允许访问除当前页面所在源之外的其他源。 协议、域名、端口组成同一源&#xff08;origin&#xff09; 在前后端不分离的单体应用中&#…

97.实战网页构建推荐信部分第二节

上节课&#xff0c;我们的推荐信完成如下 ● 接下来我们就来完成&#xff0c;我们未完成的内容吧&#xff0c;为其添加画廊 ● 我们将图片全部添加上 ● 然后通过grid构建一个3*4的网格摆放图片 .gallery {display: grid;grid-template-columns: repeat(3, 1fr);grid-tem…