🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月28日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
- Key详解
- LocalKey和GlobalKey
- GlobalKey获取子组件
- Widget Tree、Element Tree和RenderObject Tree
- AnimatedList组件
- AnimatedList介绍
- AnimatedList实现动态列表
Key详解
我们平时一定接触过很多的 Widget
,比如 Container
、Row
、Column
等,它们在我们绘制界面的过程中发挥着重要的作用。但是不知道你有没有注意到,在几乎每个 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
的实例,Widget
与 Element
的关系可以是一对多,一份配置可以创造多个 Element
实例。
属性 | 描述 |
---|---|
Widget | Widget 就是一个类, 是 Element 的配置信息。与 Element 的关系可以是一对多,一份配置可以创造多 个Element 实例 |
Element | Widget 的实例化,内部持有 Widget 和 RenderObject |
RenderObject | 负责渲染绘制 |
默认情况下,当 Flutter
同一个 Widget
的大小、顺序变化的时候,FLutter 不会改变 Widget
的 state
。
AnimatedList组件
AnimatedList介绍
AnimatedList
和 ListView
的功能大体相似,不同的是, AnimatedList
可以在列表中插入或删除节点时执行一个动画,在需要添加或删除列表项的场景中会提高用户体验。
AnimatedList
是一个 StatefulWidget
,它对应的 State
类型为 AnimatedListState
,添加和删除元素的方法位于 AnimatedListState
中:
void insertItem(int index, { Duration duration = _kDuration });
void removeItem(int index, AnimatedListRemovedItemBuilder builder, { Duration duration = _kDuration }) ;
AnimatedList
常见属性:
属性 | 描述 |
---|---|
key | globalKey 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),);})),);}
}