在创建StatefulWidget时,当数据发生变化时,调用setState()方法后,页面就会做出相应的改变,这是为什么呢?
setState方法里面做了什么
- 执行了入参的callBack方法,这个方法不能是Future类型的
- 执行了element的
markNeedsBuild方法
void setState(VoidCallback fn) {
final Object? result = fn() as dynamic;
_element!.markNeedsBuild();
}
Element的markNeedsBuild方法
- 将element标记为脏,
_dirty = true,一个element在一个绘制帧间隔中只会被标记一次脏 - 将脏的element作为参数,调用
owner的scheduleBuildFor方法,owner就是在WidgetsBinding中创建的,在整个应用生命周期内只会有一个owner实例。
void markNeedsBuild() {
if (dirty) {
return;
}
_dirty = true;
owner!.scheduleBuildFor(this);
}
BuildOwner的scheduleBuildFor方法
BuildScope是在应用启动时调用runApp,在创建rootElement时创建的,在整个应用生命周期内只会有一个buildScope实例。buildScope执行_scheduleBuildFor方法,入参是标记为脏的element。
void scheduleBuildFor(Element element) {
final BuildScope buildScope = element.buildScope;
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled!();
}
buildScope._scheduleBuildFor(element);
}
BuildScope的_scheduleBuildFor方法
将脏element添加到_dirtyElements列表中,并将element._inDirtyList状态设置为true。
void _scheduleBuildFor(Element element) {
assert(identical(element.buildScope, this));
if (!element._inDirtyList) {
_dirtyElements.add(element);
element._inDirtyList = true;
}
if (!_buildScheduled && !_building) {
_buildScheduled = true;
scheduleRebuild?.call();
}
if (_dirtyElementsNeedsResorting != null) {
_dirtyElementsNeedsResorting = true;
}
}
到这里就完成了setState的标记工作,即把待更新的element添加到脏元素列表,并将其标记为脏,等待着下一步的处理。
回头看WidgetsBinding的drawFrame
在ScheduleBinding调度新的一帧时,会调用buildOwner!.buildScope(rootElement!)方法,buildScope(rootElement!)方法会做些什么呢?
void drawFrame() {
try {
if (rootElement != null) {
buildOwner!.buildScope(rootElement!);
}
super.drawFrame();
}
buildOwner!.buildScope
- 更新
buildScope的状态,处理_dirtyElements中的脏元素 element调用自身的rebuild方法,_dirty设置为false,element的子类会重写rebuild,但是其最终都会调用widget或是State的build方法重新更新对应的widget树- 清空
buildScope的_dirtyElements列表,重置buildScope的状态
void buildScope(Element context, [VoidCallback? callback]) {
final BuildScope buildScope = context.buildScope;
if (callback == null && buildScope._dirtyElements.isEmpty) {
return;
}
try {
_scheduledFlushDirtyElements = true;
buildScope._building = true;
if (callback != null) {
callback();
}
buildScope._flushDirtyElements(debugBuildRoot: context);
} finally {
buildScope._building = false;
_scheduledFlushDirtyElements = false;
}
}
void _flushDirtyElements({required Element debugBuildRoot}) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
try {
for (int index = 0; index < _dirtyElements.length; index = _dirtyElementIndexAfter(index)) {
final Element element = _dirtyElements[index];
if (identical(element.buildScope, this)) {
_tryRebuild(element);
}
}
} finally {
for (final Element element in _dirtyElements) {
if (identical(element.buildScope, this)) {
element._inDirtyList = false;
}
}
_dirtyElements.clear();
_dirtyElementsNeedsResorting = null;
_buildScheduled = false;
}
}
void _tryRebuild(Element element) {
......
try {
element.rebuild();
} catch (e, stack) {
......
}
}
最后通过PipleOwner去重新计算布局合成新的UI,至此也就是setState的原理流程。