Linuxword Global
当前位置: Linux技术 > 理解flutter中的Key

我们知道,flutter有三颗树,widget树在每次setState的时候都会重建,而element树不会,而是会通过diff算法,计算出哪些element需要重建,哪些element可以重用,我们通过一个例子来开始,例如

import 'dart:math';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}

final _random = Random();

class _HomePageState extends State<HomePage> {
var _items = [
ListItem(title: "aaa"),
ListItem(title: "bbb"),
ListItem(title: "ccc"),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("key demo"),
),
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: _items,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
// 删除第一个元素
_items.removeAt(0);
setState(() {});
},
),
);
}
}

/// 定义一个item
class ListItem extends StatelessWidget {
final String title;
// color放在widget
final Color color = Color.fromARGB(
255, _random.nextInt(256), _random.nextInt(256), _random.nextInt(256));

ListItem({this.title});
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: Text(
this.title,
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: this.color,
width: 100,
height: 100,
);
}
}

flutter-key-stateless

运行正常,接下来我们把ListItem换成stateful
class ListItem extends StatefulWidget {
final String title;

ListItem({this.title});
@override
_ListItemState createState() => _ListItemState();
}

class _ListItemState extends State<ListItem> {
// color放在state
final Color color = Color.fromARGB(
255, _random.nextInt(256), _random.nextInt(256), _random.nextInt(256));

@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
child: Text(
widget.title,
style: TextStyle(color: Colors.white, fontSize: 20),
),
color: this.color,
width: 100,
height: 100,
);
}
}

flutter-key-stateful

从上图可以看出,颜色和文字混了,这是由于element树判断增量更新重用element导致的

flutter-widget-element-tree

当widget重建的时候,element通过对比新旧两个widget是否需要更新,从而判断是否重用,默认的逻辑是对比runtimeTypekey,我们上面的例子中显然会返回true(我们没有定义key),则表示可以element可以直接使用新的widget

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

由于颜色是state持有的,没有变化,所以蓝色的element,直接使用了灰色的widget,而最后一个绿色的element,没有用到,会被释放

通过canUpdate方法可以看到,我们可以设置key来标识element是否可以直接更新widget,flutter中的key有两种

LocalKey
GlobalKey
LocalKey
LocalKey有下面三种,其成员key用于比较,使用起来类似

ValueKey: 使用一个泛型数据作为key
ObjectKey: 使用一个对象作为key
UniqueKey: 自动生成key,并且保证唯一,比较少用
// 在StatefulWidget构造方法添加参数key,并传给super
class ListItem extends StatefulWidget {
final String title;

ListItem({this.title, Key key}) : super(key: key);
@override
_ListItemState createState() => _ListItemState();
}

// 使用ListItem的时候,传入key
var _items = [
ListItem(title: "aaa", key: ValueKey<String>("aaa")),
ListItem(title: "bbb", key: ValueKey<String>("bbb")),
ListItem(title: "ccc", key: ValueKey<String>("ccc")),
];
这样在判断的时候不同的ListItem会在canUpdate方法就会返回false,就不会重用了

通常我们在自定义StatefulWidget的时候,需要在构造函数添加可选参数key

GlobalKey
GlobalKey可以获取到context(element),widget,和state,通常用于在父widget操作子widget
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
final GlobalKey<_TestWidgetState> _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("global key"),
),
body: Center(
child: TestWidget(
key: _globalKey,
)),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
// 直接操作子widget的state
_globalKey.currentState.increseCount();
},
),
);
}
}

class TestWidget extends StatefulWidget {
TestWidget({Key key}) : super(key: key);

@override
_TestWidgetState createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidget> {
int _count = 0;

increseCount() {
setState(() {
_count = _count + 1;
});
}

@override
Widget build(BuildContext context) {
return Text("$_count");
}
}

「梦想一旦被付诸行动,就会变得神圣,如果觉得我的文章对您有用,请帮助本站成长」

赞(0) 打赏
一分也是爱

支付宝扫一扫打赏

微信扫一扫打赏

上一篇:

下一篇:

相关推荐

博客简介

本站CDN采用VmShell免费提供离中国大陆最近的香港CMI高速网络做支撑,ToToTel打造全球最快速的边沿网络支撑服务,具体详情请见 :https://vmshell.com/ 以及 https://tototel.com/,网站所有的文件和内容禁止大陆网站搬迁复制,谢谢,VPS营销投稿邮箱: admin@linuxxword.com,我们免费帮大家发布,不收取任何费用,请提供完整测试文稿!

精彩评论

友情链接

他们同样是一群网虫,却不是每天泡在网上游走在淘宝和网游之间、刷着本来就快要透支的信用卡。他们或许没有踏出国门一步,但同学却不局限在一国一校,而是遍及全球!申请交换友链

站点统计

  • 文章总数: 2341 篇
  • 草稿数目: 12 篇
  • 分类数目: 6 个
  • 独立页面: 0 个
  • 评论总数: 2 条
  • 链接总数: 0 个
  • 标签总数: 6116 个
  • 注册用户: 139 人
  • 访问总量: 8,666,641 次
  • 最近更新: 2024年4月28日