Flutter:key的作用原理(LocalKey ,GlobalKey)
第一段代码实现的内容:创建了3个块,随机3个颜色,每次点击按钮时,把第一个块删除
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
,当删除发生时看看效果
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
// 第一段代码中:生成三个有状态的块
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
给删除了呢??
源码中,StatelessWidget
和StatefulWidget
都继承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
当删除第一个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
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使用
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
数据共享。
原文地址:https://blog.csdn.net/qq_40745143/article/details/143812543
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!