【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;二&…

202305青少年软件编程(Python)等级考试试卷(四级)

第 1 题 【单选题】 有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头 小母牛。问第n 年的时候, 共有多少头母牛? 由递推法可推测, 当年数小于等于 4 的时候, 第几年就是有几头牛, 即 a[1]=1; a[2]=2; a[3]=3; a[4]=4。 当 n 大于 …

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

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

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

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

GB/T 18915.2-2013 低辐射镀膜玻璃检测

低辐射镀膜玻璃是指对红外线有较高反射比的镀膜玻璃&#xff0c;根据工艺的不同分为离线镀膜玻璃和在线镀膜玻璃。 GB/T 18915.2-2013低辐射镀膜玻璃测试项目&#xff1a; 测试项目 测试方法 尺寸 GB/T 18915.2 弯曲度 GB/T 18915.2 外观质量 GB/T 18915.2 光学性能 …

如何从 JavaScript 对象中移除属性?

在 JavaScript 中,移除对象中的属性是一项常见的操作。本文将详细介绍如何使用 delete 操作符以及其他相关方法。 使用 delete 操作符 delete 操作符用于从对象中移除属性。例如: const obj = {foo: "bar" };delete obj.foo; console.log(obj.hasOwnProperty(&q…

大模型微调系列 --关于`transformer`中的`pipeline`是什么

大模型微调系列 --关于transformer中的pipeline是什么 pipeline就是一个帮你做数据预处理以及一些模型加载&#xff0c;模型预测的步骤的工具&#xff0c;提供各种接口 定义数据预处理函数 对输入的文本进行预处理 转换为小写去除标点符号去除特殊字符 def preprocess_tex…

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…

Java中集合中对象的某一个属性转为set集合

在Java中&#xff0c;如果你想要将一个集合&#xff08;比如List<YourObject>&#xff09;中对象的某个属性&#xff08;假设为String类型&#xff09;提取出来并放入一个Set<String>集合中&#xff0c;以确保去重&#xff0c;你可以通过Java 8的Stream API来轻松地…

Codeforces Round 946 (Div. 3)

Codeforces Round 946 &#xff08;div3&#xff09; A. Phone Desktop题意&#xff1a;题解&#xff1a;代码&#xff1a; Symmetric Encoding题意&#xff1a;题解&#xff1a;代码&#xff1a; C. Beautiful Triple Pairs题意&#xff1a;题解&#xff1a;代码&#xff1a;…

洛谷 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。通过两个文件判断学生对于每个知识点的…

微服务架构中服务间通信的最佳实践是什么?

在微服务架构中&#xff0c;服务间通信的最佳实践通常是使用轻量级的通信机制&#xff0c;如HTTP或RPC。以下是一些关键的最佳实践&#xff1a; 使用RESTful API或类似的设计风格&#xff1a;RESTful API基于标准的HTTP方法和状态码&#xff0c;使得服务之间的通信更加简单和可…

【一站式学会Kotlin】第十一节:非空断言操作符!!

作者介绍: 百度资深Android工程师T6,在百度任职7年半。 目前:成立赵小灰代码工作室,欢迎大家找我交流Android、微信小程序、鸿蒙项目。= 一:通俗易懂的人工智能教程:https://www.captainbed.cn/nefu/ 点一下,打开新世界的大门。 二:【一站式学会Kotlin】免费领取:作者…

规格说明(Specifications)

1.为什么需要规格说明&#xff1f; &#xff08;1&#xff09;许多程序中的严重错误是由于代码之间接口的行为误解引起的。虽然每个程序员心中都有规格说明&#xff0c;但并非所有程序员都将其写下来。这导致团队中的不同程序员对同一个接口有不同的理解。 &#xff08;2&#…