Flutter 中 key 的原理及作用
Key 的原理
图 1 图 2static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
如图 1 所示,当我们生成一个 Widget
树的时候也会对应生成 Element
树,Widget
与 Element
一一对应。但是当我们移除 Widget1
的时候会调用 Element
的 canUpdate
方法,canUpdate
方法中会判断旧的 Widget
与新的 Widget
的 runtimeType
是否相等,且 key
是否相等。当我们不使用 key
时候,就会默认它们相等,就会如图 2 所示,Element1
就会比较 oldWidget.runtimeType == newWidget.runtimeType
,因为 Element1
曾经指向的类型与 Widget2
类型相同所以 Widget2
就会复用 Element1
,同理 Widget3
就会复用 Element2
,依次比较,当 Element3
没有指向的时候就会被移除。所以 Key
的作用可以用来跟 Widget
做绑定,判断 canUpdate
是否执行。
Key
本身是一个抽象类,有一个工厂程构造方法ValueKey()
。- 直接子类主要有:
LocalKey
跟GlobalKey
。LocalKey
是增量算法的核心,决定哪个Element
要保留,哪个Element
要删除。以下是LocalKey
的三个子类。
ValueKey
:以值作为参数(数字、字符串)ObjectKey
:以对象作为参数UniqueKey
:创建唯一标识GlobalKey
对应某一个Widget
或者State
或者Element
。
GlobalKey 的使用
class GlobalKeyDemo extends StatelessWidget {
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.currentContext
// _globalKey.currentWidget
_globalKey.currentState?.setState(() {
_globalKey.currentState?.data = '666';
_globalKey.currentState?.count++;
});
},
child: const Icon(Icons.add),
),
);
}
}
class ChildPage extends StatefulWidget {
const ChildPage({Key? key}) : super(key: key);
@override
_ChildPageState createState() => _ChildPageState();
}
class _ChildPageState extends State<ChildPage> {
int count = 0;
String data = 'hello';
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
Text(count.toString()),
Text(data)
],
),
);
}
}
如案例所示,当 FloatingActionButton
中 onPressed
闭包函数执行的时候我们想修改 _ChildPageState
中 count
及 data
的值,我们可以在 GlobalKeyDemo
中定义 _globalKey = GlobalKey()
,在初始化 ChildPage
的时候把 _globalKey
作为参数传递。这时候我们可以通过 _globalKey.currentContext
、_globalKey.currentWidget
、_globalKey.currentState
获取我们想拿到的部件,这里我们通过 _globalKey.currentState
就能获取到 _ChildPageState
修改 data
及 count
属性,并调用 setState
方法。个人感受 Flutter
中的 GlobalKey
有点类似 iOS
中 UIView
的 tag
属性,可以通过 tag
来获取到对应的 UIView
控件。