InheritedWidget可以跨组件(同一个InheritedWidget祖先)获取数据,同时InheritedWidget重新build时,会触发所有使用inheritFromWidgetOfExactType获取数据的Widget进行build,原理是在该函数中将这些Widget添加到依赖列表中,build时触发其didChangeDependencies函数,该函数会调用markNeedsBuild触发buildancestorInheritedElementForWidgetOfExactType如果使用该函数获取数据,则不会将context添加到依赖列表,不会触发didChangeDependencies
setState相关
setState()会调用markNeedsBuild触发context即本Element重绘——rebuild,而rebuild会调用performRebuild,这个函数不同类型的Widget有不同的实现,如StatefulWidget对应的ComponentElement,Center对应的SingleChildRenderObjectElement,Row对应的MultiChildRenderObjectElement
- 大原则:尽量减少重新生成
Element,因为这是个复杂对象,基类Element的updateChild实现如下:
/// flutter的注释特别多,基本就是一份官方教程,这里解释了Widget的更新操作
/// The following table summarizes the above:
///
/// | | **newWidget == null** | **newWidget != null** |
/// | :-----------------: | :--------------------- | :---------------------- |
/// | **child == null** | Returns null. | Returns new [Element]. |
/// | **child != null** | Old child is removed, returns null. | Old child updated if possible, returns child or new [Element]. |
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
if (newWidget == null) {
if (child != null)
////2
deactivateChild(child);
return null;
}
if (child != null) {
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
if (Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
...
return child;
}
deactivateChild(child);
}
return inflateWidget(newWidget, newSlot);
}
不是一刀切的直接调用inflateWidget新建Element,而是分情况处理,尽量减少新建操作
oldWidget==null且newWidget==null,无操作;oldWidget!=null且newWidget==null,删除节点;oldWidget==null且newWidget!=null,新增节点;oldWidget!=null且newWidget!=null,前3种都是常规操作,这种才是大多数会遇到的情况,这里又分类进行处理- 返回
Widget一致,一般情况下我们写的代码Widget都是new出来的,如Center(),这种不符合,必须是完全一个对象才可以,这种直接返回就可以; - 符合
Widget.canUpdate,大多数的情况是这种,canUpdate有两个条件,类型与key,一般用Widget时不会使用key,只要类型一致即可满足条件,此时,执行其update函数,这个函数也是因类型而异,如StatelessElement只是将自己标记为需要重绘,同时调用rebuild,也就是说StatelessElement只是将rebuild的指令传递了下去,给了孩子节点。 - 实在没办法了,只能重建了
- 返回
举一个简单例子:
import 'package:flutter/material.dart';
class TestWidget extends StatefulWidget {
@override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
int count= 0;
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('$count'),
RaisedButton(
onPressed: (){
setState(() {
count+=1;
});
},
child: Text('Add'),
)
],
);
}
}
说一下点击按钮后,Text('$count')的重绘路径
也就是说,setState后,整个Widget触发重绘,Flutter框架会将轻量的Widget重建,而尽量减少Element的重建,保证性能。但总的来说,如果是大Widget如很长的列表之类,重绘还是很耗性能的操作。如果想要局部刷新,最好还是用StatefulBuilder或Provider之类的插件来实现。
本文深入探讨了Flutter中Widget更新机制,重点分析InheritedWidget如何跨组件共享数据,以及setState如何触发Widget重绘。详细解释了Element更新流程,以及如何减少Element重建以提升性能。
1180

被折叠的 条评论
为什么被折叠?



