1、Flutter Tips - Widget Key;
发布于 2022年 02月 14日 16:26
当Widget
在Widget Tree
中移动时,Key
会保留其状态。它们可用于保留用户的滚动位置等信息。
Key的种类
Key
主要分为Local Keys
和Global Keys
;
Local Keys
- ValueKey : ValueKey('value')
- ObjectKey : ObjectKey(MutableRectangle(1,2,3,4))
- UniqueKey : UniqueKey()
- PageStorageKey : PageStorageKey(scrollLocation)
Global Keys
- GlobalKey : GlobalKey()
什么时候使用Key
大多数情况下,我们并不需要使用Key
。但是如果你发现自己需要添加、删除或者重新排序处于某种状态的相同类型的Widget
集合就会用到Key
。
import 'package:flutter/material.dart';
class KeyDemoApp extends StatelessWidget{
@override
Widget build(BuildContext context) => MaterialApp(
home: _HomePage(),
);
}
class _HomePage extends StatefulWidget{
@override
State<StatefulWidget> createState() =>_HomePageState();
}
class _HomePageState extends State<_HomePage>{
List<Widget> _widgets;
@override
void initState() {
super.initState();
_widgets= [
RectStateless(Color.fromARGB(255, 0, 0, 0)),
RectStateless(Color.fromARGB(255, 255, 0, 0))
];
}
@override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Row(children: _widgets,),
),
floatingActionButton: FloatingActionButton(onPressed: swapWidget),
);
void swapWidget(){
setState(() {
_widgets.insert(1, _widgets.removeAt(0));
});
}
}
class RectStateless extends StatelessWidget{
final Color _bg;
RectStateless(this._bg,[Key key]):super(key:key);
@override
Widget build(BuildContext context) {
return Container(color:_bg,
width: 100,
height: 100,
);
}
}
在上面的代码中,如果点击按钮则两个Widget
可以自由的更换位置。如果将两个Widget
更换为StatefulWidget
的Widget
后:
class RectStateful extends StatefulWidget{
final Color _bg;
RectStateful(this._bg,[Key key]):super(key:key);
@override
State<StatefulWidget> createState() =>RectStatefulState(_bg);
}
class RectStatefulState extends State<RectStateful>{
final Color _bg;
RectStatefulState(this._bg);
@override
Widget build(BuildContext context) {
return Container(color:_bg,
width: 100,
height: 100,
);
}
}
当点击时两个色块都不会交换,当添加上Key后则又可以交换位置:
_widgets= [
RectStateful(Color.fromARGB(255, 0, 0, 0),UniqueKey()),
RectStateful(Color.fromARGB(255, 255, 0, 0),UniqueKey())
];
从上面的例子可以看出: 如果集合中的整个小部件子树是无状态的,则不需要使用Key。
Key的运作方式
在Stateless
的例子中,Row
为子Widget提供了一组有序的插槽。对于每个小部件,Flutter都会构建一个相应的Element
,Element tree
只会保存每个Widget类型以及对子Element的引用信息。可以将Element tree
视为Flutter应用程序的骨架,它显示了应用程序的结构。当交换Row
中的Widget时,Flutter会遍历Element tree
来查看骨架结构是否相同,即检查新的Widget是否和旧的Widget的类型和Key是否相同,如果是一样的,它会更新对新Widget的引用,对于其他Widget也是对应的步骤。 在Stateless
的例子中,Widget
没有Key
,Flutter只会检查其类型。
在Stateful
没有Key
例子中,每个Widget都会有一个State对象来保存相应的信息,这些信息而不是存储在小部件本身中。Flutter会检查Row
小部件的类型,类型是一样的会更新引用,第二个Widget也如此。Flutter会遍历Element tree
以及其对应的State
来确定在设备上显示的实际内容。因此,看起来相应的Widget并没有正确的交换。
在有Key
的Stateful
例子中,向小部件添加来key
的属性。在交换Row
小部件时会像之前一样匹配。但是新的Widget的密钥和之前小部件Element
对应的Key不匹配。因此,Flutter会停用这些Element
,从第一个不匹配的Element
开始,然后Flutter会查看不匹配的子项,查找具有相同密钥的Element,它会找到匹配项,并更新对相应Widget
的引用。然后Flutter会为后续的子项做相同的事情。