目录
LocalKey
一、说明
图中的 widgeta 和 widgetb的代码如下:
class StfulItem extends StatefulWidget {
// ignore: prefer_const_constructors_in_immutables
StfulItem(this.title, {Key? key}) : super(key: key);
// 注意 title 是widget的属性
final String title;
final _colorInWidget = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
@override
_StfulItemState createState() => _StfulItemState();
}
class _StfulItemState extends State<StfulItem> {
// 注意:color是state的属性
final _colorInState = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1.0);
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
// color: widget._colorInWidget,
color: _colorInState,
child: Text(widget.title),
);
}
}
注意:
1、title是widget的属性。
2、color是state的属性
widget不稳定,每次build都会创建新的
element稳定,并且element持有widget和state。
flutter系统会根据需要来更新element持有的widget或者销毁当前element,创建新的element
是更新widget还是重新创建取决与一个方法:
//这个是widget的静态方法
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
当更新element时,调用:Widget.canUpdate()方法
当widget创建没有指定key时,oldWidget.key == newWidget.key 会被认为是true.
如图中:
当我们删除掉widgeta时,更新element时会拿element树的第一个节点和widget树的第一个节点比较
调用Widget.canUpdate(elementa.widget,widgetb),因为没有指定key,所以返回true
这时会将widgetb.title更新到elementa.widget,但color不是widgit的属性,color是state的属性而state属于element
所以颜色是红色的b
二、ChatGPT关于该方法的说明:
当 canUpdate
方法返回 true
时,Flutter 框架会执行以下操作来处理:
-
复用现有的
Element
:
Flutter 框架会尝试复用现有的Element
实例,而不是重新创建新的Element
。
Element
是与Widget
相关联的 UI 元素,负责构建和管理实际的渲染树。
由于canUpdate
方法返回了true
,表示新的Widget
实例的数据可以更新到现有的Widget
实例,因此不需要创建新的Element
。 -
更新
Widget
实例:
Flutter 框架会将新的Widget
实例中的属性更新到现有的Widget
实例中。这样,现有的Widget
实例就能反映出新的配置和属性。
这一步骤是在Element.update()
方法中完成的。在这个方法中,Flutter 框架会检查新的Widget
实例与现有Widget
实例之间的差异,并根据差异更新实际的渲染树。 -
更新 UI 树:
当Element.update()
方法完成后,Flutter 框架会更新 UI 树,以反映出Widget
的更新。
这将触发所需部分的重绘,并将更改反映在应用程序的界面上。
通过复用现有的 Element
并更新 Widget
实例,Flutter 避免了不必要的 UI 重建,从而提高了应用程序的性能和效率。
这是 Flutter 框架中的一个重要优化机制,它能够在 UI 更新过程中避免不必要的渲染,从而更加高效地管理应用程序的界面。
需要注意的是,当 canUpdate
返回 true
时,通常意味着新旧 Widget
实例的配置和属性相同,这样才能保证更新是安全和正确的。
如果新的 Widget
实例与旧的实例在配置或属性上有显著差异,那么 canUpdate
应该返回 false
,以确保正确的 UI 更新行为。
当 canUpdate
方法返回 false
时,Flutter 框架会执行以下操作来处理:
-
删除旧的
Element
:
Flutter 框架会将旧的Widget
实例关联的Element
从渲染树中删除。
这个过程是通过调用Element.unmount()
方法来实现的。
Element.unmount()
方法会从父级Element
中移除当前Element
,并递归地将其从渲染树中移除。 -
创建新的
Element
:
由于canUpdate
方法返回了false
,表示新的Widget
实例与旧的实例在配置或属性上有显著差异,因此不能复用现有的Element
。
因此,Flutter 框架会创建一个新的Element
来代表新的Widget
实例。
创建新的Element
是通过调用Widget.createElement()
方法实现的。 这个方法由Widget
类定义,通常在Element
的子类中被重写。 -
构建新的 UI 树:
当新的Element
创建完毕后,Flutter 框架会执行Element.build()
方法来构建新的 UI 树。 这个方法由Element
的子类实现,用于构建Widget
对应的渲染树。
在Element.build()
方法中,Flutter 框架会递归地创建新的子元素,并构建整个 UI 树。 -
更新 UI 树:
当新的 UI 树构建完成后,Flutter 框架会将其插入到正确的位置,以反映出新的Widget
实例的配置和属性。
这将触发所需部分的重绘,并将更改反映在应用程序的界面上。
通过删除旧的 Element
并创建新的 Element
,Flutter 可以确保 UI 树与新的 Widget
实例的配置和属性保持同步。
这样,无论是新的 Widget
实例还是旧的实例,都能正确地显示在应用程序的界面上。
当 canUpdate
返回 false
时,通常意味着新的 Widget
实例与旧的实例在配置或属性上有显著差异,需要进行全新的 UI 构建。
因此,canUpdate
方法的返回值在决定 Flutter 是否重新构建 UI 时起到了重要的作用。
GlobalKey
一、说明
GlobalKey在创建的时候可以指定一个State
final GlobalKey<_ChildPageState> _globalKey = GlobalKey();
创建widget时将key传入
ChildPage(key: _globalKey)
在任何地方我们都可以拿到state的数据了
_ChildPageState? state = _globalKey.currentState;
if (state != null) {
state.setState(() {
state.data = 'old:' + state.count.toString();
state.count++;
});
}
二、使用场景
在创建Widget树时,我们尽量将需要更新的树(statefullWidget)位于树的叶子节点处,这样可以使用局部更新来提高性能
但这样存在一个问题,statefullWidget的setState方法有可能获取不到就没办法进行局部更新,这个时候就可以使用GlobalKey
class GlobalKeyDemo extends StatelessWidget {
GlobalKeyDemo({Key? key}) : super(key: key);
final GlobalKey<_ChildPageState> _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GlobalKeyDemo'),
),
body: ChildPage(key: _globalKey),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
_ChildPageState? state = _globalKey.currentState;
if (state != null) {
state.setState(() {
state.data = 'old:' + state.count.toString();
state.count++;
});
}
},
),
);
}
}
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: <Widget>[
Text(count.toString()),
Text(data),
],
),
);
}
}
行者常至,为者常成!