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

第一段代码实现的内容:创建了3个块,随机3个颜色,每次点击按钮时,把第一个块删除

js 复制代码
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_one/demo.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: KeyDemo(),
    );
  }
}

class KeyDemo extends StatefulWidget {
  const KeyDemo({Key? key}) : super(key: key);
  @override
  State<KeyDemo> createState() => _KeyDemoState();
}

class _KeyDemoState extends State<KeyDemo> {
  // 生成三个无状态的块
  List<Widget> items = [
    StlItem('1'),
    StlItem('2'),
    StlItem('3')
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('KeyDemo'),
        centerTitle: true,
      ),
      body: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: items,
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: (){
          setState(() {
            items.removeAt(0); // 点击按钮把第一个删除
          });
        }
      ),
    );
  }
}

先调用无状态的StatelessWidget ,当删除发生时看看效果

js 复制代码
class StlItem extends StatelessWidget {
  final String title;
  StlItem(this.title,{Key? key}) : super(key: key);

  // 随机的颜色
  final color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);

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

发生删除时:

删除后

总结发现,如果是无状态的StatelessWidget 即使不传key:StlItem(this.title,{Key? key}) : super(key: key);

也能正常删除。

下面看下有状态的StatelessWidget,不传key会出现什么BUG

js 复制代码
// 第一段代码中:生成三个有状态的块
List<Widget> items = [
  StfulItem('1'),
  StfulItem('2'),
  StfulItem('3')
];

// 有状态
class StfulItem extends StatefulWidget {
  final String title;
  StfulItem(this.title,{Key? key}) : super(key: key);
  @override
  State<StfulItem> createState() => _StfulItemState();
}

class _StfulItemState extends State<StfulItem> {
  // 随机的颜色
  final color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      child: Text(widget.title),
      color: color,
    );
  }
}

删除前

删除后

发现问题了:我删除的是第一条数据,发现文字1正常删除,但是颜色怎么是把颜色3给删除了呢??

源码中,StatelessWidgetStatefulWidget都继承Widget

复制代码
abstract class StatefulWidget extends Widget{}

而在Widget中有这样一个方法,Flutter的增量渲染就是通过canUpdate来判断哪里需要更新数据。

复制代码
static bool canUpdate(Widget oldWidget, Widget newWidget) {
  return oldWidget.runtimeType == newWidget.runtimeType
      && oldWidget.key == newWidget.key;
}

Flutter中的3棵树中,Widget树和Element树

每创建一个Widget,都会有对应的Element

当删除第一个WidgetElement就会调用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

js 复制代码
List<Widget> items = [
  StfulItem('1',key: const ValueKey('1'),),
  StfulItem('2',key: const ValueKey('2'),),
  StfulItem('3',key: const ValueKey('3'),)
];

key的原理

复制代码
Key本身是一个抽象类,有一个工厂构造方法,创建ValueKey
直接子类主要有:LocalKey 和 GlobalKey

GlobalKey:帮助我们访问某个Widget的信息


LocalKey :它用来区别哪个Element保留,哪个Element要删除
	ValueKey 以值作为参数(数字、字符串)
	ObjectKey:以对象作为参数
	UniqueKey:创建唯一标识

GlobalKey使用

js 复制代码
import 'package:flutter/material.dart';
class GlobalKeyDemo extends StatelessWidget {
  // 定义:GlobalKey<拿谁的数据> 变量 = GlobalKey();
  final GlobalKey<_childPageState> _globalKey = GlobalKey();
  GlobalKeyDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('GlobalKeyDemo'),
      ),
      body: childPage(
        key: _globalKey,
      ),
      floatingActionButton: FloatingActionButton(onPressed: (){
      	// _globalKey  就能访问到 _childPageState 中的属性,进行修改
        _globalKey.currentState!.setState((){
          _globalKey.currentState!.data = 'hello word';
          _globalKey.currentState!.count++;
        });
      },
      child: const Icon(Icons.add),),
    );
  }
}

class childPage extends StatefulWidget {
  const childPage({Key? key}):super(key: key);

  @override
  State<childPage> createState() => _childPageState();
}

class _childPageState extends State<childPage> {
  int count = 0;
  String data = 'heelo';
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(count.toString()),
        Text(data),
      ],
    );
  }
}

除了定义GlobalKey外,还可以使用InheritedWidget数据共享。

相关推荐
晔子yy11 分钟前
【JAVA探索之路】从头开始讲透、实现单例模式
java·开发语言·单例模式
遇见你...2 小时前
TypeScript
前端·javascript·typescript
Highcharts.js2 小时前
Highcharts Grid 中文站正式上线:表格数据处理的全新选择
前端·javascript·数据库·表格数据·highcharts·可视化图表·企业级图表
阿正的梦工坊5 小时前
JavaScript 微任务与宏任务完全指南
开发语言·javascript·ecmascript
知行合一。。。6 小时前
Python--05--面向对象(属性,方法)
android·开发语言·python
青梅橘子皮6 小时前
C语言---指针的应用以及一些面试题
c语言·开发语言·算法
浅时光_c6 小时前
3 shell脚本编程
linux·开发语言·bash
Evand J7 小时前
【三维轨迹目标定位,CKF+RTS,MATLAB程序】基于CKF与RTS平滑的三维非线性目标跟踪(距离+方位角+俯仰角)
开发语言·matlab·目标跟踪
今天又在写代码7 小时前
java-v2
java·开发语言
competes8 小时前
慈善基金投资底层逻辑应用 顶层代码低代码配置平台开发结构方式数据存储模块
java·开发语言·数据库·windows·sql