【Flutter】KeyAnimatedList组件

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月28日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

目录

  • Key详解
    • LocalKey和GlobalKey
    • GlobalKey获取子组件
    • Widget Tree、Element Tree和RenderObject Tree
  • AnimatedList组件
    • AnimatedList介绍
    • AnimatedList实现动态列表

Key详解

我们平时一定接触过很多的 Widget,比如 ContainerRowColumn 等,它们在我们绘制界面的过程中发挥着重要的作用。但是不知道你有没有注意到,在几乎每个 Widget 的构造函数中,都有一个共同的参数,它们通常在参数列表的第一个,那就是 Key

Flutter 中,Key 是不能重复使用的,所以 Key 一般用来做唯一标识。组件在更新的时候,其状态的保存主要是通过判断组件的类型或者 key 值是否一致。因此,当各组件的类型不同的时候,类型已经足够用来区分不同的组件了,此时我们可以不必使用 key。但是如果同时存在多个同一类型的控件的时候,此时类型已经无法作为区分的条件了,我们就需要使用到 key

LocalKey和GlobalKey

Flutter key子类包含 LocalKey 和 GlobalKey

  • 局部键(LocalKey):ValueKey、ObjectKey、UniqueKey
    • ValueKey:把一个值作为key
    • UniqueKey:程序会生成唯一的Key,当我们不知道如何指定 ValueKey 的时候就可以使用 UniqueKey
    • ObjectKey:把一个对象实例作为key
  • 全局键(GlobalKey): GlobalKey、GlobalObjectKey
    • GlobalObjectKey:全局 Objec key,和 ObjectKey 有点类似

LocalKey的使用

下面程序的功能是,界面中有 3 个颜色不同的按钮,点击按钮,其上面的数字会增加,点击右下角的浮动按钮,按钮的顺序会被打乱。如果没有给 3 个按钮赋予不同的 key 值,当被打乱后,改变的只有颜色,其上的数字并不会改变。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const HomePage(),);}
}class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {List<Widget> list = [const Box(key: ValueKey('666'),color: Colors.red,),Box(key: UniqueKey(),color: Colors.blue,),const Box(key: ObjectKey(Box(color: Colors.blue)),color: Colors.yellow,),];Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {list.shuffle(); //shuffle:打乱list元素的顺序});},),appBar: AppBar(title: const Text('LocalKey'),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: list,),),);}
}class Box extends StatefulWidget {final Color color;const Box({super.key, required this.color});State<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;Widget build(BuildContext context) {return Container(margin: const EdgeInsets.all(10),height: 100,width: 100,child: ElevatedButton(style: ButtonStyle(backgroundColor: MaterialStateProperty.all(widget.color)),onPressed: () {setState(() {_count++;});},child: Text("$_count",style: Theme.of(context).textTheme.displayLarge,),),);}
}

初始:

在这里插入图片描述

随意点击:

在这里插入图片描述

打乱:

在这里插入图片描述

GlobalKey的使用

运行下面这个程序会发现,当我们将手机改为横屏后,按钮的状态恢复到初始状态,这是因为外层组件从 Column 变成了 Row,组件的类型已经改变,状态自然无法再保存。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const HomePage(),);}
}class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {List<Widget> list = [const Box(key: ValueKey('666'),color: Colors.red,),Box(key: UniqueKey(),color: Colors.blue,),const Box(key: ObjectKey(Box(color: Colors.blue)),color: Colors.yellow,),];Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.refresh),onPressed: () {setState(() {list.shuffle(); //shuffle:打乱list元素的顺序});},),appBar: AppBar(title: const Text('GlobalKey'),),body: Center(// //判断屏幕为竖屏还是横屏child: MediaQuery.of(context).orientation == Orientation.portrait? Column(mainAxisAlignment: MainAxisAlignment.center,children: list,): Row(mainAxisAlignment: MainAxisAlignment.center,children: list,),),);}
}class Box extends StatefulWidget {final Color color;const Box({super.key, required this.color});State<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;Widget build(BuildContext context) {return Container(margin: const EdgeInsets.all(10),height: 100,width: 100,child: ElevatedButton(style: ButtonStyle(backgroundColor: MaterialStateProperty.all(widget.color)),onPressed: () {setState(() {_count++;});},child: Text("$_count",style: Theme.of(context).textTheme.displayLarge,),),);}
}

下面使用 GlobalKey 来对其改进,我们定义了 3 个 GlobalKey,然后在 initState 里面对 Box 进行初始化。

在这里插入图片描述

竖屏:

在这里插入图片描述

横屏:

在这里插入图片描述

GlobalKey获取子组件

globalKey.currentState 可以获取子组件的状态,执行子组件的方法,globalKey.currentWidget 可以获i取子组件的属性,_globalKey.currentContext!.findRenderObject() 可以获取渲染的属性。

import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const HomePage(),);}
}//父Widget
class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {final GlobalKey _globalKey = GlobalKey();Widget build(BuildContext context) {return Scaffold(floatingActionButton: FloatingActionButton(child: const Icon(Icons.add),onPressed: () {//1、获取currentState Widget的属性(记住)var boxState = _globalKey.currentState as _BoxState;print(boxState._count);setState(() {boxState._count++; //可以设置获取的属性});//调用currentState Widget的方法boxState.run();//2、获取子Widget (了解)var boxWidget = _globalKey.currentWidget as Box;print(boxWidget.color); //值:MaterialColor(primary value: Color(0xfff44336))// 3、获取子组件渲染的属性(了解)var renderBox =_globalKey.currentContext!.findRenderObject() as RenderBox;print(renderBox.size); //值:Size(100.0, 100.0)},),appBar: AppBar(title: const Text('Global获取子组件'),),body: Center(child: Box(key: _globalKey, color: Colors.red),),);}
}//子Widget
class Box extends StatefulWidget {final Color color;const Box({Key? key, required this.color}) : super(key: key);State<Box> createState() => _BoxState();
}class _BoxState extends State<Box> {int _count = 0;void run() {print("我是box的run方法");}Widget build(BuildContext context) {return SizedBox(height: 100,width: 100,child: ElevatedButton(style: ButtonStyle(backgroundColor: MaterialStateProperty.all(widget.color)),onPressed: () {setState(() {_count++;});},child: Text("$_count",style: Theme.of(context).textTheme.headlineLarge,),),);}
}

在这里插入图片描述

Widget Tree、Element Tree和RenderObject Tree

Flutter 应用是由是 Widget Tree、Element Tree 和 RenderObject Tree组成的。Widget 可以理解成一个类,Element 可以理解成 Widget 的实例,WidgetElement 的关系可以是一对多,一份配置可以创造多个 Element 实例。

属性描述
WidgetWidget 就是一个类, 是 Element 的配置信息。与 Element 的关系可以是一对多,一份配置可以创造多 个Element 实例
ElementWidget 的实例化,内部持有 Widget 和 RenderObject
RenderObject负责渲染绘制

默认情况下,当 Flutter 同一个 Widget 的大小、顺序变化的时候,FLutter 不会改变 Widgetstate

AnimatedList组件

AnimatedList介绍

AnimatedListListView 的功能大体相似,不同的是, AnimatedList 可以在列表中插入或删除节点时执行一个动画,在需要添加或删除列表项的场景中会提高用户体验。

AnimatedList 是一个 StatefulWidget,它对应的 State 类型为 AnimatedListState,添加和删除元素的方法位于 AnimatedListState 中:

void insertItem(int index, { Duration duration = _kDuration });
void removeItem(int index, AnimatedListRemovedItemBuilder builder, { Duration duration = _kDuration }) ;

AnimatedList 常见属性:

属性描述
keyglobalKey final globalKey = GlobalKey();
initialItemCount子元素数量
itemBuilder方法 (BuildContext context, int index, Animation animation) {}

关于GlobalKey

  • 每个 Widget 都对应一个 Element ,我们可以直接对 Widget 进行操作,但是无法直接操作 Widget 对应的 Element 。而 GlobalKey 就是那把直接访问 Element 的钥匙。通过 GlobalKey 可以获取到 Widget 对应的 Element

AnimatedList实现动态列表

import 'dart:async';
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const HomePage(),);}
}class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {final _globalKey = GlobalKey<AnimatedListState>();bool flag = true;List<String> list = ["第一条", "第二条"];Widget _buildItem(index) {return ListTile(key: ValueKey(index),title: Text(list[index]),trailing: IconButton(icon: const Icon(Icons.delete),onPressed: () {//执行删除_deleteItem(index);},),);}_deleteItem(index) {if (flag == true) {flag = false;//执行删除_globalKey.currentState!.removeItem(index, (context, animation) {//animation的值是从1到0var removeItem = _buildItem(index);list.removeAt(index); //数组中删除数据return ScaleTransition(// opacity: animation,scale: animation,child: removeItem, //删除的时候执行动画的元素);});//解决快速删除的bugTimer.periodic(const Duration(milliseconds: 500), (timer) {flag = true;timer.cancel();});}}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('AnimatedList动态列表'),),floatingActionButton: FloatingActionButton(child: const Icon(Icons.add),onPressed: () {list.add("我是新增的数据");_globalKey.currentState!.insertItem(list.length - 1);},),body: AnimatedList(key: _globalKey,initialItemCount: list.length,itemBuilder: ((context, index, animation) {//animation的值是从0到1return FadeTransition(opacity: animation,child: _buildItem(index),);})),);}
}

在这里插入图片描述

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

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

相关文章

10个最佳人物素材网站推荐,免费获取第一个PNG文件!

人物素材是设计中应用最广泛的元素之一。无论是网页设计还是移动终端设计&#xff0c;人物素材的插画设计都比文字信息更容易吸引用户的注意力。作为内容呈现&#xff0c;还可以增加设计的艺术属性。为了节省大家寻找人物素材的时间成本&#xff0c;本文立即为大家整理了10个宝…

Java 实验12 线程同步与通信

&#xff08;一&#xff09;实验目的 1、掌握JAVA中多线程的实现方法&#xff1b; 2、重点掌握多线程的同步与通信机制&#xff1b; 3、熟悉JAVA中有关多线程同步与通信的方法 &#xff1b; 4、能使用多线程机制解决实际应用中的线程同步与通信问题。 &#xff08;二&…

行为设计模式之职责链模式

文章目录 概述原理代码实现小结 概述 职责链模式(chain of responsibility pattern) 定义: 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求.将接收请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止. 在职责链模式中&…

宝塔:如何在宝塔面板做301重定向

如何在宝塔面板做301重定向?301重定向对于网站来说非常重要。如果你的网站以www开头&#xff0c;我们应该把没有www的域名重定向到有www的域名&#xff0c;反之亦然。 1、我们进入宝塔管理后台 2、登录面板并单击添加站点。既然要把xxx.com 301发到www.xxx.com&#xff0c;我…

JS 中怎么删除数组元素?有哪几种方法?

正文开始之前推荐一位宝藏博主免费分享的学习教程,学起来! 编号学习链接1Cesium: 保姆级教程+源码示例2openlayers: 保姆级教程+源码示例3Leaflet: 保姆级教程+源码示例4MapboxGL: 保姆级教程+源码示例splice() JavaScript中的splice()方法是一个内置的数组对象函数, 用于…

05.配置tomcat管理功能

认证失败&#xff0c;需要配置tomcat-users.xml文件 配置用户信息 [rootweb01 /application/tomcat/conf\]# tail tomcat-users.xml <role rolename"admin-gui"/> <role rolename"host-gui"/><role rolename"mana…

洛谷 CF1209D Cow and Snacks

题目来源于&#xff1a;洛谷 题目本质&#xff1a;并查集 解题思路&#xff1a; 我们以每种化为一个点&#xff0c;以每个客人喜欢的两朵花给两朵花连一条无向边。则会出现一定数目的连通块&#xff0c;连通块总个数为 ans。 对每个连通块进行分析&#xff1a;第一个客人买走…

QGraphicsView实现简易地图17『涟漪效果』

前文链接&#xff1a;QGraphicsView实现简易地图16『爆炸效果』 模仿水波荡漾时的涟漪效果&#xff0c;参考了echarts中的散点图 支持设置散点大小、颜色、涟漪线条宽度。 动态演示效果 静态展示图片 核心代码 #pragma once #include "../AbstractGeoItem.h" #incl…

452. 用最少数量的箭引爆气球(中等)

452. 用最少数量的箭引爆气球 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;452. 用最少数量的箭引爆气球 2.详细题解 引爆所有气球&#xff0c;弓箭数要最少&#xff0c;那么每支弓箭尽量多的引爆气球&#xff0c;采用贪心策略。对于…

ThingsBoard网关在燃气泄漏监测中的应用

据不完全统计&#xff0c;全国城市燃气企业的供销差率大约在3%~4%&#xff0c;也就意味着越多的天然气销量就有越多的天然气损失。城市燃气企业计量管理已经接近最不利的状态&#xff0c;开展有效的计量管理势在必行。 智慧燃气综合管理系统 在燃气管网中部署智能传感器、数据采…

AI分析SP和pk进行sk分析

SP原始表行标题代表题目序号&#xff0c;列代表学生&#xff0c;如果学生答对题目为1&#xff0c;否则为0。问题知识点矩阵这个文件横轴代表每个知识点&#xff0c;列标题代表每个题目序号&#xff0c;如果题目包含这个知识点则该处值为1。通过两个文件判断学生对于每个知识点的…

Web安全:文件上传漏洞详解,文件上传漏洞原理、绕过方式和防御方案。

「作者简介」&#xff1a;2022年北京冬奥会网络安全中国代表队&#xff0c;CSDN Top100&#xff0c;就职奇安信多年&#xff0c;以实战工作为基础对安全知识体系进行总结与归纳&#xff0c;著作适用于快速入门的 《网络安全自学教程》&#xff0c;内容涵盖系统安全、信息收集等…

Java实现链表

链表 前言一、链表的概念及结构二、链表的分类三、链表的实现无头单向非循环链表实现无头双向链表实现具体代码 四、链表习题五、顺序表和链表的区别 前言 推荐一个网站给想要了解或者学习人工智能知识的读者&#xff0c;这个网站里内容讲解通俗易懂且风趣幽默&#xff0c;对我…

51单片机-实机演示(单多个数码管)

仿真链接&#xff1a; http://t.csdnimg.cn/QAPhx 目录 一.引脚位置 二.多个显示 三 扩展 一.引脚位置 注意P00 - >A ; 这个多个的在左边,右边的A到B是控制最右边那个单个的. 接下来上显示单个的代码 #include <reg52.h> #include <intrins.h> #define u…

大字体学生出勤记录系统网页HTML源码

源码介绍 上课需要一个个点名记录出勤情况&#xff0c;就借助AI制作了一个网页版学生出勤记录系统&#xff0c; 大字体显示学生姓名和照片&#xff0c;让坐在最后排学生也能看清楚&#xff0c;显示姓名同时会语音播报姓名&#xff0c; 操作很简单&#xff0c;先导入学生姓名…

用源码建站可能涉及知产侵权,建站的注意!

近日普推知产老杨看到央视报道一家公司用了某建站源码涉及知产侵权&#xff0c;起诉了全国八千多家公司&#xff0c;某梦自从创始人因病转给某公司后&#xff0c;也在大量起诉用其建站代码公司侵权&#xff0c;他们也都是申请了相关的著作权。 有的中小企业在运营中会涉及建站…

c++ 将指针转换为 void* 后,转换为怎么判断原指针类型?

当将指针转换为void后&#xff0c;擦除了指针所指向对象的类型信息&#xff0c;因此无法通过void指针来判断原始指针的类型。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个…

PXI总线测试模块-6939 矢量信号发生器

频率范围&#xff1a;250kHz&#xff5e;6GHz 6939 矢量信号发生器 6939矢量信号发生器提供频率范围250kHz~6GHz的多制式信号&#xff0c;单边带相位噪声优于-120dBc/Hz10kHz&#xff08;载波1GHz&#xff09;、EVM优于1.0%&#xff0c;能够实现高纯连续波输出、模拟调制信号输…

Linux: network: TCP: zero window size/window full 示例

最近遇到一个问题,当前机器的CPU使用率非常高,然后导致其中一个程序处理socket的数据过慢,然后出现下面的zero的示例。 下面是在接收buff用光的时候,发出的 TCP zeroWindows的消息 这种问题就是内存,CPU,网速之间的性能取舍。具体解决的话,需要看具体的需要是什么样的?…

参数高效微调PEFT(一)快速入门BitFit、Prompt Tuning、Prefix Tuning

参数高效微调PEFT(一)快速入门BitFit、Prompt Tuning、Prefix Tuning 目前&#xff0c;模型最全的网站是HuggingFace&#xff0c;但是国内需要魔法流量才能访问。另外&#xff0c;现在大模型权重文件都较大&#xff0c;也会浪费不少流量&#xff0c;因此这里推荐使用魔搭社区下…