Flutter有两个常用的状态类:
- StatelessWidget:无状态类,没有状态更新,界面一经创建无法更改。
-
StatefulWidget:有状态类,当状态有改变时,调用setState(),方法会触发StatefulWidget的UI更新。
那么setState是如何刷新的呢?我们来看一下setState()的内部逻辑:
在State类中定义了setState方法:
@protected
void setState(VoidCallback fn) {
_element.markNeedsBuild();
}
标记为dirty,执行的markNeedsBuild,定义在Element类中:
void markNeedsBuild() {
if (!_active)
return;
if (dirty)
return;
_dirty = true;// 标记为dirty(脏的)
owner.scheduleBuildFor(this);
}
当前Element节点被标记为dirty,同时调用owner的scheduleBuildFor方法:
将element元素添加到全局的“脏”链表里。
void scheduleBuildFor(Element element) {
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled();// 回调
}
_dirtyElements.add(element);// 添加到“脏”链表里
element._inDirtyList = true;
}
BuildOwner用来管理哪些需要更新的Widget。这个owner最开始被初始化的地方在WidgetsBinding的initInstances方法中,随后初始化了onBuildScheduled方法,对应执行的是_handleBuildScheduled,定义在WidgetsBinding类中:
void _handleBuildScheduled() {
ensureVisualUpdate();
}
ensureVisualUpdate 方法定义在SchedulerBinding类中:
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
在提交下一帧绘制的时候会调用到scheduleFrame方法,提交给引擎绘制,看看scheduleFrame方法,也定义在SchedulerBinding类中:
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled)
return;
ensureFrameCallbacksRegistered();
window.scheduleFrame();//native 'Window_scheduleFrame'
_hasScheduledFrame = true;
}
@protected
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
提交给引擎绘制之后,会收到onDrawFrame的回调,最终执行到_handleDrawFrame方法中,对应的是handleDrawFrame方法,定义在SchedulerBinding类中:
void handleDrawFrame() {
Timeline.finishSync(); // end the "Animate" phase
try {
// PERSISTENT FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
// POST-FRAME CALLBACKS
_schedulerPhase = SchedulerPhase.postFrameCallbacks;
final List localPostFrameCallbacks =
List.from(_postFrameCallbacks);
_postFrameCallbacks.clear();
for (final FrameCallback callback in localPostFrameCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
} finally {
_schedulerPhase = SchedulerPhase.idle;
Timeline.finishSync(); // end the Frame
_currentFrameTimeStamp = null;
}
}
在RendererBinding的initInstances方法中添加了一个回调到这个List中,对应的是RenderBinding的drawFrame方法,对应的节点进行绘制渲染操作。
WidgetsBinding中的drawFrame方法:
@override
void drawFrame() {
TimingsCallback firstFrameCallback;
if (_needToReportFirstFrame) {
firstFrameCallback = (List timings) {
if (!kReleaseMode) {
developer.Timeline.instantSync('Rasterized first useful frame');
developer.postEvent('Flutter.FirstFrame', {});
}
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
firstFrameCallback = null;
_firstFrameCompleter.complete();
};
// Callback is only invoked when [Window.render] is called. When
// [sendFramesToEngine] is set to false during the frame, it will not
// be called and we need to remove the callback (see below).
SchedulerBinding.instance.addTimingsCallback(firstFrameCallback);
}
try {
if (renderViewElement != null)
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
} finally {
}
if (!kReleaseMode) {
if (_needToReportFirstFrame && sendFramesToEngine) {
developer.Timeline.instantSync('Widgets built first useful frame');
}
}
_needToReportFirstFrame = false;
if (firstFrameCallback != null && !sendFramesToEngine) {
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
}
}
看看这里的buildScope方法,定义在BuildOwner方法中。在上面 scheduleBuildFor 方法介绍中有提到:"scheduleBuildFor 是把一个 element 添加到 _dirtyElements 链表,以便当[WidgetsBinding.drawFrame]中调用 buildScope 的时候能够重构 element。onBuildScheduled()是一个 BuildOwner 的回调"。在 drawFrame 中调用 buildOwner.buildScope(renderViewElement)更新 elements。
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
Timeline.startSync('Build', arguments: timelineWhitelistArguments);
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
try {
//while 循环进行元素重构
_dirtyElements[index].rebuild();
} catch (e, stack) {
}finally {
for (final Element element in _dirtyElements) {
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
}
}
}
_dirtyElements列表在遍历的过程中执行rebuild方法,此时将所有标记为dirty的Element节点依次执行rebuild,preformRebuild,build,updateChild,update方法,执行界面更新。完成build,update操作完成之后,后续会将需要绘制的RenderObject添加到需要layout的列表中,等待绘制渲染。所有绘制完成之后将_dirtyElments列表清空,_inDirtyList标记位置为false。
提交给引擎绘制渲染
看看super.drawFrame(),这里就执行到了RendererBinding类中,定义如下:
void drawFrame() {
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
这里就是将最终需要绘制渲染的画面提交给引擎的地方了,绘制完成之后就在界面显示更新后的视图了。