Flutter:key的作用原理(LocalKey ,GlobalKey)

打印 上一主题 下一主题

主题 1951|帖子 1951|积分 5853

第一段代码实现的内容:创建了3个块,随机3个颜色,每次点击按钮时,把第一个块删除
  1. import 'dart:math';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_one/demo.dart';
  4. void main() {
  5.   runApp(const App());
  6. }
  7. class App extends StatelessWidget {
  8.   const App({Key? key}) : super(key: key);
  9.   @override
  10.   Widget build(BuildContext context) {
  11.     return const MaterialApp(
  12.       home: KeyDemo(),
  13.     );
  14.   }
  15. }
  16. class KeyDemo extends StatefulWidget {
  17.   const KeyDemo({Key? key}) : super(key: key);
  18.   @override
  19.   State<KeyDemo> createState() => _KeyDemoState();
  20. }
  21. class _KeyDemoState extends State<KeyDemo> {
  22.   // 生成三个无状态的块
  23.   List<Widget> items = [
  24.     StlItem('1'),
  25.     StlItem('2'),
  26.     StlItem('3')
  27.   ];
  28.   @override
  29.   Widget build(BuildContext context) {
  30.     return Scaffold(
  31.       appBar: AppBar(
  32.         title: const Text('KeyDemo'),
  33.         centerTitle: true,
  34.       ),
  35.       body: Row(
  36.         mainAxisAlignment: MainAxisAlignment.center,
  37.         children: items,
  38.       ),
  39.       floatingActionButton: FloatingActionButton(
  40.         child: Icon(Icons.add),
  41.         onPressed: (){
  42.           setState(() {
  43.             items.removeAt(0); // 点击按钮把第一个删除
  44.           });
  45.         }
  46.       ),
  47.     );
  48.   }
  49. }
复制代码
  先调用无状态的StatelessWidget ,当删除发生时看看效果
  1. class StlItem extends StatelessWidget {
  2.   final String title;
  3.   StlItem(this.title,{Key? key}) : super(key: key);
  4.   // 随机的颜色
  5.   final color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
  6.   @override
  7.   Widget build(BuildContext context) {
  8.     return Container(
  9.       width: 100,
  10.       height: 100,
  11.       child: Text(title),
  12.       color: color,
  13.     );
  14.   }
  15. }
复制代码
发生删除时:

删除后

总结发现,如果是无状态的StatelessWidget 纵然不传key:StlItem(this.title,{Key? key}) : super(key: key);
也能正常删除。
   下面看下有状态的StatelessWidget,不传key会出现什么BUG
  1. // 第一段代码中:生成三个有状态的块
  2. List<Widget> items = [
  3.   StfulItem('1'),
  4.   StfulItem('2'),
  5.   StfulItem('3')
  6. ];
  7. // 有状态
  8. class StfulItem extends StatefulWidget {
  9.   final String title;
  10.   StfulItem(this.title,{Key? key}) : super(key: key);
  11.   @override
  12.   State<StfulItem> createState() => _StfulItemState();
  13. }
  14. class _StfulItemState extends State<StfulItem> {
  15.   // 随机的颜色
  16.   final color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
  17.   @override
  18.   Widget build(BuildContext context) {
  19.     return Container(
  20.       width: 100,
  21.       height: 100,
  22.       child: Text(widget.title),
  23.       color: color,
  24.     );
  25.   }
  26. }
复制代码
删除前

删除后

发现问题了:我删除的是第一条数据,发现笔墨1正常删除,但是颜色怎么是把颜色3给删除了呢??
源码中,StatelessWidget和StatefulWidget都继续Widget
  1. abstract class StatefulWidget extends Widget{}
复制代码
而在Widget中有如许一个方法,Flutter的增量渲染就是通过canUpdate来判定哪里需要更新数据。
  1. static bool canUpdate(Widget oldWidget, Widget newWidget) {
  2.   return oldWidget.runtimeType == newWidget.runtimeType
  3.       && oldWidget.key == newWidget.key;
  4. }
复制代码
  Flutter中的3棵树中,Widget树和Element树
  每创建一个Widget,都会有对应的Element

当删除第一个Widget,Element就会调用canUpdate更新数据,Element是按顺序判定,它会拿Element111和删除后的Widget222进行对比
oldWidget.runtimeType == newWidget.runtimeType 旧的部件类型和新的部件类型是一样的,oldWidget.key == newWidget.key;旧的没有传key和新的也没传key,效果那就是true,增量渲染发现可以复用,Element111就指向了Widget222
最后对比到Element333,发现Widget树中已经没有了,Element333就被删除了。
那么颜色为什么会错了,由于颜色是生存在State中,State是生存在Element中,所以最后一个颜色canUpdate时被删除了。

   加上key之后办理这个BUG
  1. List<Widget> items = [
  2.   StfulItem('1',key: const ValueKey('1'),),
  3.   StfulItem('2',key: const ValueKey('2'),),
  4.   StfulItem('3',key: const ValueKey('3'),)
  5. ];
复制代码
  key的原理
  1. Key本身是一个抽象类,有一个工厂构造方法,创建ValueKey
  2. 直接子类主要有:LocalKey 和 GlobalKey
  3. GlobalKey:帮助我们访问某个Widget的信息
  4. LocalKey :它用来区别哪个Element保留,哪个Element要删除
  5.         ValueKey 以值作为参数(数字、字符串)
  6.         ObjectKey:以对象作为参数
  7.         UniqueKey:创建唯一标识
复制代码
  GlobalKey利用
  1. import 'package:flutter/material.dart';
  2. class GlobalKeyDemo extends StatelessWidget {
  3.   // 定义:GlobalKey<拿谁的数据> 变量 = GlobalKey();
  4.   final GlobalKey<_childPageState> _globalKey = GlobalKey();
  5.   GlobalKeyDemo({Key? key}) : super(key: key);
  6.   @override
  7.   Widget build(BuildContext context) {
  8.     return Scaffold(
  9.       appBar: AppBar(
  10.         title: const Text('GlobalKeyDemo'),
  11.       ),
  12.       body: childPage(
  13.         key: _globalKey,
  14.       ),
  15.       floatingActionButton: FloatingActionButton(onPressed: (){
  16.               // _globalKey  就能访问到 _childPageState 中的属性,进行修改
  17.         _globalKey.currentState!.setState((){
  18.           _globalKey.currentState!.data = 'hello word';
  19.           _globalKey.currentState!.count++;
  20.         });
  21.       },
  22.       child: const Icon(Icons.add),),
  23.     );
  24.   }
  25. }
  26. class childPage extends StatefulWidget {
  27.   const childPage({Key? key}):super(key: key);
  28.   @override
  29.   State<childPage> createState() => _childPageState();
  30. }
  31. class _childPageState extends State<childPage> {
  32.   int count = 0;
  33.   String data = 'heelo';
  34.   @override
  35.   Widget build(BuildContext context) {
  36.     return Column(
  37.       children: [
  38.         Text(count.toString()),
  39.         Text(data),
  40.       ],
  41.     );
  42.   }
  43. }
复制代码
除了定义GlobalKey外,还可以利用InheritedWidget数据共享。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

自由的羽毛

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表