InheritedWidget
可以跨组件(同一个InheritedWidget
祖先)获取数据,同时InheritedWidget
重新build
时,会触发所有使用inheritFromWidgetOfExactType
获取数据的Widget
进行build
,原理是在该函数中将这些Widget
添加到依赖列表中,build
时触发其didChangeDependencies
函数,该函数会调用markNeedsBuild
触发build
ancestorInheritedElementForWidgetOfExactType
如果使用该函数获取数据,则不会将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
之类的插件来实现。