flutter key的原理和使用
一. 什么是key在常规flutter开发中,widget可以有stateful和stateless两种,key能帮助开发者保存widget的状态key涉及到flutter渲染机制Widget只是一个配置项(color,height,width…),最终生成对应类型element(内部存有state状态)Widget更新时,会匹配默认的对应的生成的element,如果类型相同,且key相同,更新视
·
一. 什么是key
在常规flutter开发中,widget可以有stateful和stateless两种,key能帮助开发者保存widget的状态
key涉及到flutter渲染机制
- Widget只是一个配置项(color,height,width…),最终生成对应类型element(内部存有state状态)
- Widget更新时,会匹配默认的对应的生成的element,如果类型相同,且key相同,更新视图; 如果类型相同,key不相同,element会找widget同级相同的key进行匹配更新
@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
···
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
1.2 图解
// 当没有使用key时,解析flutter更新机制
class Screen extends StatefulWidget {
@override
_ScreenState createState() => _ScreenState();
}
class _ScreenState extends State<Screen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
StatelessContainer(color : Colors.green)
StatelessContainer(color : Colors.blue )
StatelessContainer(color : Colors.red )
],
),
),
);
}
}
class StatefulContainer extends StatefulWidget {
final Color color;
StatefulContainer({Key key , color}) : super(key: key);
@override
_StatefulContainerState createState() => _StatefulContainerState();
}
class _StatefulContainerState extends State<StatefulContainer> {
int count = 0;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(()=>{
count++
})
},
child : Container(
width: 100,
height: 100,
color: widget.color,
child : Text(count.toString())
)
);
}
}
- flutter会根据widget生成对应的element,将element渲染到界面上,
widget只保存ui配置项,element保存state状态
- 动态的把绿色和蓝色的widget互换位置,在进行热跟新
发现两个widget互换之后,结果尽然只是颜色发生了互换,state的值并没有发生互换
原因是,当widget互换位置之后,element tree会依次向左边的widget tree进行匹配,ContainerElement1比对Container2时,类型一致,那么就直接把当前的element指定到了Container2上
- 动态的把第一个蓝色widget移除,在进行热更新
第一个蓝色的widget移除后,视图重新刷新,ContainerElement1先匹配Container1,发现类型匹配上了,那么ContainerElement1指向Container1,依次类推,最后ContainerElement3因为没有匹配项了,会进行销毁处理;
注意 : element匹配,只会匹配同级的widget,不会匹配父级和子级的widget
- 此时在最前面新增一个紫色的widget,过程图如下
- 如果此时给每个widget传入对应的key,基于上面这三个例子,都会很合理的进行渲染;
二. 解决了什么问题
key 的种类
Localkey
- ValueKey : ValueKey(‘String’)
- ObjectKey : ObjectKey(Object)
- UniqueKey : UniqueKey2)
GlobalKey
GlobalKey 使用了一个静态常量 Map 来保存它对应的 Element。 你可以通过 GlobalKey 找到持有该GlobalKey的 Widget,State 和 Element。
注意:GlobalKey 是非常昂贵的,需要谨慎使用。
三. 如何使用
- ValueKey
// 如果您有一个 Todo List 应用程序,它将会记录你需要完成的事情。我们假设每个 Todo 事情都各不相同,而你想要对每个 Todo 进行滑动删除操作。这时候就需要使用 ValueKey!
return TodoItem(
key: ValueKey(todo.task),
todo: todo,
onDismissed: (direction){
_removeTodo(context, todo);
},
);
- ObjectKey
如果你有一个生日应用,它可以记录某个人的生日,并用列表显示出来,同样的还是需要有一个滑动删除操作。
我们知道人名可能会重复,这时候你无法保证给 Key 的值每次都会不同。但是,当人名和生日组合起来的 Object 将具有唯一性。
这时候你需要使用 ObjectKey!。
- UniqueKey
如果组合的 Object 都无法满足唯一性的时候,你想要确保每一个 Key 都具有唯一性。那么,你可以使用 UniqueKey。它将会通过该对象生成一个具有唯一性的 hash 码。
不过这样做,每次 Widget 被构建时都会去重新生成一个新的 UniqueKey,失去了一致性。也就是说你的小部件还是会改变。(还不如不用😂)
- PageStorageKey
当你有一个滑动列表,你通过某一个 Item 跳转到了一个新的页面,当你返回之前的列表页面时,你发现滑动的距离回到了顶部。这时候,给 Sliver 一个 PageStorageKey!它将能够保持 Sliver 的滚动状态。
- GlobalKey
class SwitcherScreenState extends State<SwitcherScreen> {
bool isActive = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Switch.adaptive(
value: isActive,
onChanged: (bool currentStatus) {
isActive = currentStatus;
setState(() {});
}),
),
);
}
changeState() {
isActive = !isActive;
setState(() {});
}
}
// 但是我们想要在外部改变该状态,这时候就需要使用 GlobalKey。
class _ScreenState extends State<Screen> {
final GlobalKey<SwitcherScreenState> key = GlobalKey<SwitcherScreenState>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: SwitcherScreen(
key: key,
),
floatingActionButton: FloatingActionButton(onPressed: () {
key.currentState.changeState();
}),
);
}
}
四. 注意事项
五. 总结
学习到了flutter key的使用,和更新机制原理
六. 参考
更多推荐
已为社区贡献6条内容
所有评论(0)