Flutter中key的作用

2023-12-19

在这里插入图片描述

flutter中key的作用

key的定义

Key Class官方介绍:

A [Key] is an identifier for [Widget]s, [Element]s and [SemanticsNode]s. A new widget will only be used to update an existing element if its key is the same as the key of the current widget associated with the element. {@youtube 560 315 https://www.youtube.com/watch?v=kn0EOS-ZiIc } Keys must be unique amongst the [Element]s with the same parent. Subclasses of [Key] should either subclass [LocalKey] or [GlobalKey].

翻译过来:

一个Key是Widget,Element以及SemanticsNode的标识。 一个新widget将仅用来更新一个已存在的element假如它的key和当前widget关联的元素一致。 官方介绍视频 https://www.youtube.com/watch?v=kn0EOS-ZiIc 在有着相同父节点的element中,Key必须是唯一的。 Key的子类要么是LocalKey,要么是GlobalKey。

Key 官方介绍:

Controls how one widget replaces another widget in the tree. If the runtimeType and key properties of the two widgets are operator== , respectively, then the new widget replaces the old widget by updating the underlying element (i.e., by calling Element.update with the new widget). Otherwise, the old element is removed from the tree, the new widget is inflated into an element, and the new element is inserted into the tree. In addition, using a GlobalKey as the widget’s key allows the element to be moved around the tree (changing parent) without losing state. When a new widget is found (its key and type do not match a previous widget in the same location), but there was a widget with that same global key elsewhere in the tree in the previous frame, then that widget’s element is moved to the new location. Generally, a widget that is the only child of another widget does not need an explicit key.

翻译过来:

控制一个小部件如何替换树中的另一个小部件。 如果两个widget的 runtimeTypekey 属性分别是相等的( == ),则新widget通过更新基础element(即,通过使用新的widget调用 Element.update )来替换旧widget。否则,将从树中删除旧element,将新widget放大为一个element,然后将新element插入到树中。 另外,使用 GlobalKey 作为窗口小部件的 key 允许该element在树上移动(更改父级)而不会丢失状态。当找到新的widget(其键和类型与相同位置的先前widget不匹配),但是在前一帧的树中其他位置有一个具有相同全局键的widget时,该widget的element将移至新位置。 通常,作为另一个widget的唯一child的widget不需要显式key。

Key的作用

大多数时候并不需要使用key。

当需要在一个 StatefulWidget 集合中进行添加、删除、重排序等操作时,才是key登场的时候。

无状态组件

下面这段代码在一个Row中展示了两个彩色方片(StatelessContainer),当点击按钮时,会交换两个方片的位置:

img_key_screen

img_key_screen

代码如下

import 'dart:math';
import 'package:flutter/material.dart';

class StatelessContainer extends StatelessWidget {
  final Color color = Color.fromRGBO(
      Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      color: color,
    );
  }
}

class Screen extends StatefulWidget {
  @override
  _ScreenState createState() => _ScreenState();
}

class _ScreenState extends State<Screen> {
  List<Widget> widgets = [
    StatelessContainer(),
    StatelessContainer(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: widgets,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: switchWidget,
        child: Icon(Icons.undo),
      ),
    );
  }

  switchWidget() {
    widgets.insert(0, widgets.removeAt(1));
    setState(() {});
  }
}
有状态组件

有状态组件的状态信息(如颜色)通常是存储在state中的,而state是存储在element树中的。

那么Key到底应该用到哪呢? 我们再来一个例子,我们把色块用Padding包装一下。运行之后会发现,色块并没有交换,而是以随机的形式在变换颜色。为什么呢?

import 'dart:math';
import 'package:flutter/material.dart';

class Screen extends StatefulWidget {
  Screen({Key key}) : super(key: key);

  @override
  _ScreenState createState() => _ScreenState();
}

class _ScreenState extends State<Screen> {
  List<Widget> widgets = [
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulContainer(key: UniqueKey()),
    ),
    Padding(
      padding: const EdgeInsets.all(8.0),
      child: StatefulContainer(key: UniqueKey()),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: widgets,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: switchWidget,
        child: Icon(Icons.undo),
      ),
    );
  }

  switchWidget() {
    widgets.insert(0, widgets.removeAt(1));
    setState(() {});
    print('${widgets[0]}, ${widgets[1]}');
  }
}

class StatefulContainer extends StatefulWidget {
  StatefulContainer({Key key}) : super(key: key);

  @override
  _StatefulContainerState createState() => _StatefulContainerState();
}

class _StatefulContainerState extends State<StatefulContainer> {
  final Color color = Color.fromRGBO(
      Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      width: 100,
      height: 100,
    );
  }
}

结合我们上面的理论,我们分析一下这次的Widget Tree 和 Element Tree,当我们交换元素后,Flutter element-to-widget matching algorithm,(元素-组件匹配算法),开始进行对比,算法每次只对比一层,即Padding这一层。显然,Padding并没有发生本质的变化。

于是开始进行第二层对比,在对比时Flutter发现元素与组件的Key并不匹配,于是,把它设置成不可用状态,但是这里所使用的Key只是本地Key(Local Key),Flutter并不能找到另一层里面的Key(即另外一个Padding Widget中的Key)所以,Flutter就创建了一个新的Widget,而这个Widget的颜色就成了我们看到的『随机色』。

通过上面的示例,我们能明显的看出,我们的Key要设置到组件树的 顶层 ,而这一层在改变时,才能复用或者更新状态。

修正版本:

import 'dart:math';

import 'package:flutter/material.dart';

class Screen extends StatefulWidget {
  Screen({Key key}) : super(key: key);

  @override
  _ScreenState createState() => _ScreenState();
}

class _ScreenState extends State<Screen> {
  List<Widget> widgets = [
    Padding(
      key: UniqueKey(),
      padding: const EdgeInsets.all(8.0),
      child: StatefulContainer(),
    ),
    Padding(
      key: UniqueKey(),
      padding: const EdgeInsets.all(8.0),
      child: StatefulContainer(),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: widgets,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: switchWidget,
        child: Icon(Icons.undo),
      ),
    );
  }

  switchWidget() {
    widgets.insert(0, widgets.removeAt(1));
    setState(() {});
    print('${widgets[0]}, ${widgets[1]}');
  }
}

class StatelessContainer extends StatelessWidget {
  final Color color = Color.fromRGBO(
      Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      width: 100,
      height: 100,
    );
  }
}

class StatefulContainer extends StatefulWidget {
  StatefulContainer({Key key}) : super(key: key);

  @override
  _StatefulContainerState createState() => _StatefulContainerState();
}

class _StatefulContainerState extends State<StatefulContainer> {
  final Color color = Color.fromRGBO(
      Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      width: 100,
      height: 100,
    );
  }
}

Key的分类

img_key

img_key

  • ValueKey:以一个值为key。
  • ObjectKey:以一个对象为key。
  • UniqueKey:生成唯一的随机数作为key。
  • PageStorageKey:专用于存储页面滚动位置的key。
  • GlobalKey:见后文。

何时使用key

ValueKey

如果您有一个 Todo List 应用程序,它将会记录你需要完成的事情。我们假设每个 Todo 事情都各不相同,而你想要对每个 Todo 进行滑动删除操作。

这时候就需要使用 ValueKey!

return TodoItem(
    key: ValueKey(todo.task),
    todo: todo,
    onDismissed: (direction){
        _removeTodo(context, todo);
    },
);
ObjectKey

如果你有一个生日应用,它可以记录某个人的生日,并用列表显示出来,同样的还是需要有一个滑动删除操作。

我们知道人名可能会重复,这时候你无法保证给 Key 的值每次都会不同。但是,当人名和生日组合起来的 Object 将具有唯一性。

这时候你需要使用 ObjectKey!

UniqueKey

如果组合的 Object 都无法满足唯一性的时候,你想要确保每一个 Key 都具有唯一性。那么,你可以使用 UniqueKey。它将会通过该对象生成一个具有唯一性的 hash 码。

不过这样做,每次 Widget 被构建时都会去重新生成一个新的 UniqueKey,失去了一致性。也就是说你的小部件还是会改变。(还不如不用????)

PageStorageKey

当你有一个滑动列表,你通过某一个 Item 跳转到了一个新的页面,当你返回之前的列表页面时,你发现滑动的距离回到了顶部。这时候,给 Sliver 一个 PageStorageKey!它将能够保持 Sliver 的滚动状态。

GlobalKey

每个globalkey都是一个在整个应用内唯一的key。

globalkey相对而言是比较昂贵的,如果你并不需要globalkey的某些特性,那么可以考虑使用Key、ValueKey、ObjectKey或UniqueKey。

用途1

允许widget在应用程序中的任何位置更改其parent而不丢失其状态。应用场景:在两个不同的屏幕上显示相同的widget,并保持状态相同。

用途2

GlobalKey 能够跨 Widget 访问状态。 在这里我们有一个 Switcher 小部件,它可以通过 changeState 改变它的状态。

class SwitcherScreenState extends State<SwitcherScreen> {
  bool isActive = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Switch.adaptive(
            value: isActive,
            onChanged: (bool currentStatus) {
              isActive = currentStatus;
              setState(() {});
            }),
      ),
    );
  }

  changeState() {
    isActive = !isActive;
    setState(() {});
  }
}

但是我们想要在外部改变该状态,这时候就需要使用 GlobalKey。

class _ScreenState extends State<Screen> {
  final GlobalKey<SwitcherScreenState> key = GlobalKey<SwitcherScreenState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SwitcherScreen(
        key: key,
      ),
      floatingActionButton: FloatingActionButton(onPressed: () {
        key.currentState.changeState();
      }),
    );
  }
}

这里我们通过定义了一个 GlobalKey 并传递给 SwitcherScreen。然后我们便可以通过这个 key 拿到它所绑定的 SwitcherState 并在外部调用 changeState 改变状态了。

最后

这里也为想要学习Flutter的朋友们准备了两份学习资料《Flutter Dart语言编程入门到精通》《Flutter实战》,从编程语言到项目实战,一条龙服务!!

《Flutter Dart 语言编程入门到精通》

  • 第一章 Dart语言基础

  • 第二章 Dart 异步编程
    在这里插入图片描述

  • 第三章 异步之 Stream 详解

  • 第四章 Dart标准输入输出流
    在这里插入图片描述

  • 第五章 Dart 网络编程

  • 第六章 Flutter 爬虫与服务端
    在这里插入图片描述

  • 第七章 Dart 的服务端开发

  • 第八章 Dart 调用C语言混合编程

  • 第九章 LuaDardo中Dart与Lua的相互调用
    在这里插入图片描述

《Flutter实战:第二版》

  • 第一章:起步
  • 第二章:第一个Flutter应用
  • 第三章:基础组件
  • 第四章:布局类组件
  • 第五章:容器类组件

在这里插入图片描述

  • 第六章:可滚动组件

  • 第七章:功能型组件

  • 第八章:事件处理与通知

  • 第九章:动画

  • 第十章:自定义组件
    在这里插入图片描述

  • 第十一章:文件操作与网络请求

  • 第十二章:Flutter扩展

  • 第十三章:国际化

  • 第十四章:Flutter核心原理

  • 第十五章:一个完整的Flutter应用
    在这里插入图片描述

有需要学习资料的朋友扫描下方二维码即可免费领取!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Flutter中key的作用 的相关文章

随机推荐

  • 使用 Eclipselink Moxy 如何将 xml 内容映射到与值不同的名称?

    在我的 Xml 中我有
  • 【华为数据之道学习笔记】5-9图模型设计

    图模型作为当前流行的信息处理加工技术 自提出以来 迅速在 学术界和工业界得到了普及 在智能推荐 决策分析等方面有着广泛的应用 图模型由节点和边组成 节点表示实体或概念 边则由属性或关 系构成 实体指的是具有可区别性且独立存在的某种事物 如某
  • 压缩炸弹,Java怎么防止

    压缩炸弹 Java怎么防止 什么是压缩炸弹 会有什么危害 什么是压缩炸弹 压缩炸弹 ZIP 一个压缩包只有几十KB 但是解压缩后有几十GB 甚至可以去到几百TB 直接撑爆硬盘 或者是在解压过程中CPU飙到100 造成服务器宕机 虽然系统功能
  • Redis设计与实现之Lua 脚本

    目录 一 Lua 脚本 1 初始化 Lua 环境 2 脚本的安全性 3 脚本的执行 4 EVAL 命令的实现 定义 Lua 函数 执行 Lua 函数 5 EVALSHA 命令的实现 二 小结 一 Lua 脚本 Lua 脚本功能是 Reids
  • 鸿蒙崛起,高校加入培养大军

    前言 近日 华为消费者业务CEO余承东宣布 明年华为将推出鸿蒙原生应用与原生体验的产品 标志着鸿蒙生态正式迈入发展的快车道 与此同时 国内主流的App已经开始着手研发纯鸿蒙系统版本 高校也积极参与 加入鸿蒙的培养大军 鸿蒙 作为华为推动的自
  • Flutter完整开发实战详解(一、Dart语言和Flutter基础)

    前言 在如今的 Fultter 大潮下 本系列是让你看完会安心的文章 本系列将完整讲述 如何快速从0开发一个完整的 Flutter APP 配套高完成度 Flutter 开源项目 GSYGithubAppFlutter 同时也会提供一些Fl
  • 机器学习笔记 - 用于时间序列分析的深度学习技术

    一 简述 过去 时间序列分析采用自回归综合移动平均线等传统统计方法 然而 随着深度学习的出现 研究人员探索了各种神经网络架构来建模和预测时间序列数据 深度学习技术 例如 LSTM 长短期记忆 卷积神经网络和自动编码器 已经在时间序列预测 异
  • 鸿蒙开发入门:deviceConfig内部结构

    deviceConfig内部结构 deviceConfig包含设备上的应用配置信息 可以包含default tv car wearable等属性 default标签内的配置适用于所有通用设备 其他设备类型如果有特殊的需求 则需要在该设备类型
  • 基于springboot的网络阅读与写作平台【论文、源码、开题报告】

    博主介绍 全网个人号和企业号 粉丝40W 每年辅导几千名大学生较好的完成毕业设计 专注计算机软件领域的项目研发 不断的进行新技术的项目实战 热门专栏 推荐订阅 订阅收藏起来 防止下次找不到 千套JAVA实战项目持续更新中 上百套小程序实战项
  • 2023年12月18日-12月24日(项目需求+ue5底层渲染)

    周一 进行了renderdoc dx11试验 发现是汇编模式 周二 7 23 8 00ue5底层渲染02A19 02B03
  • Apache Flink(十五):Flink任务提交模式

    个人主页 IT贫道 大数据OLAP体系技术栈 Apache Doris Clickhouse 技术 CSDN博客 私聊博主 加入大数据技术讨论群聊 获取更多大数据资料 博主个人B栈地址 豹哥教你大数据的个人空间 豹哥教你大数据个人主页 哔哩
  • js刷新当前页面

    js刷新当前页面 大家好 我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3 0的小编 也是冬天不穿秋裤 天冷也要风度的程序猿 深入解析JS刷新当前页面 在网页世界中畅游的技巧 作为Web开发者 我们时常需要通过JavaScript来操作页
  • easyrecovery软件2025免费版电脑数据恢复软件

    easyrecovery14是easyrecovery系列软件的新版本 也是目前行业领先的数据恢复软件 具备更快捷 更高效 更便捷三大特色 能够帮助用户轻松恢复电脑丢失的数据 目前软件支持恢复不同存储介质数据 包括硬盘 光盘 U盘 移动硬盘
  • Docker与云计算平台集成:AWS、Azure、GCP完全指南

    Docker和云计算平台的结合 如AWS Amazon Web Services Azure Microsoft Azure 和GCP Google Cloud Platform 为现代应用的构建和部署提供了巨大的便利性 本文将深入研究如何
  • 华为OD机试 Python 【最大载货量】

    描述 在火车站旁的货运站 小明负责调度2K辆中转车 其中K辆用于干货 K辆用于湿货 每批到站的货物来自不同的供货商 需要按照顺序装入中转车 注意 一个供货商的货物只能装在一辆车上 不能分开 但是 一辆车可以放多个供货商的货物 问题是 要让所
  • 汽车UDS诊断——SecureDataTransmission 加密数据传输(0x84)

    诊断协议那些事儿 诊断协议那些事儿专栏系列文章 本文介绍诊断和通讯管理功能单元下的84服务SecureDataTransmission 在常规诊断通信中 数据极易被第三方获取 所以在一些特殊的数据传输时 标准定义了加密数据传输的服务 简而言
  • php中文乱码或html中文乱码

    参考gpt 一 在PHP中解决中文乱码问题的常见方案有以下几种 设置字符编码 在你的PHP代码中 可以使用 header 函数设置正确的字符编码 常见的字符编码是UTF 8 可以使用以下代码将页面的字符编码设置为UTF 8 header C
  • OpenCV4工业缺陷检测的六种方法【文末送书】

    目录 1 机器视觉 2 缺陷检测 三 工业上常见缺陷检测方法 方法一 基于简单二值图像分析实现划痕提取 效果如下 方法二 复杂背景下的图像缺陷分析 基于频域增强的方法实现缺陷检测 运行截图 方法三 复杂背景下的图像缺陷分析 基于空域增强实现
  • 机器人制作开源方案 | 智能水果分拣机器人

    作者 史振鹏 岳欣宇 仲祝伟 单位 邢台学院 指导老师 王承林 魏亚清 一 场景调研 智能水果分拣机器人是基于探索者设计的一款可搬运可分拣以及移动的一款轻便机器人 集成了语音控制 分拣 搬运 识别 抓取等功能 全部是使用探索者标准件 通过控
  • Flutter中key的作用

    flutter中key的作用 key的定义 Key Class官方介绍 A Key is an identifier for Widget s Element s and SemanticsNode s A new widget will