本文主要介绍Flutter中创建一个Widget到屏幕上渲染出Widget内容的路程.
拾用本文您将获得:
Widget是什么Element是什么RenderObject是什么
附加Buff:
Widget直接渲染成图片- 文本
String的绘制 - 图片
ui.Image的绘制
这一切都要从runApp方法开始说起, 如果你还不知道runApp是什么, 建议从
https://docs.flutter.dev/ui 开始阅读...
runApp
runApp方法就是进入Flutter世界的入口, 方法参数也是唯一的参数就是一个Widget
dart
void runApp(Widget app) {
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
_runWidget(binding.wrapWithDefaultView(app), binding, 'runApp');
}
那么这个名为app的Widget是怎样到界面上的呢? 开始吧...
Element
要想将Widget渲染上屏, 首先就需要将Widget变换成Element.
所以在runApp方法的执行链上, 很容易就能跟踪到下面的代码:
dart
void attachToBuildOwner(RootWidget widget) {
final bool isBootstrapFrame = rootElement == null;
_readyToProduceFrames = true;
// 请注意这里
_rootElement = widget.attach(buildOwner!, rootElement as RootElement?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
关键代码widget.attach(buildOwner!, rootElement as RootElement?)
dart
/// 代码来自[RootWidget.attach]
RootElement attach(BuildOwner owner, [ RootElement? element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
element!.mount(/* parent */ null, /* slot */ null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
上述代码,就是将一个Widget变换成Element的关键代码. 请注意上面的代码, 因为这玩意在另一个类中原封不动的也出现过.
那就是RenderObjectToWidgetAdapter如下:
dart
/// 代码来自[RenderObjectToWidgetAdapter.attachToRenderTree]
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
element!.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
Widget变换成Element同样也是将Widget渲染成图片的关键步骤.
到这一步WidgetsBinding.rootElement就赋值完成了, 接下来就是等待屏幕信号刷新帧,开始渲染了...
上述attachToBuildOwner方法中, 有一行代码SchedulerBinding.instance.ensureVisualUpdate()就是用来安排刷新帧的, 触发之后, 等待屏幕信号即可.
dart
/// 代码来自[SchedulerBinding.ensureVisualUpdate]
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
dart
/// 代码来自[SchedulerBinding.scheduleFrame]
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled) {
return;
}
assert(() {
if (debugPrintScheduleFrameStacks) {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
}
return true;
}());
ensureFrameCallbacksRegistered();
platformDispatcher.scheduleFrame();
_hasScheduledFrame = true;
}
void ensureFrameCallbacksRegistered() {
platformDispatcher.onBeginFrame ??= _handleBeginFrame;
platformDispatcher.onDrawFrame ??= _handleDrawFrame;
}
PlatformDispatcher.onBeginFrame
调用链路SchedulerBinding.ensureVisualUpdate->SchedulerBinding.scheduleFrame->SchedulerBinding._handleBeginFrame->SchedulerBinding.handleBeginFrame->SchedulerBinding._invokeFrameCallback
平时通过SchedulerBinding.scheduleFrameCallback方法安排的帧回调就是在SchedulerBinding._invokeFrameCallback方法中执行的.
PlatformDispatcher.onDrawFrame
调用链路SchedulerBinding.ensureVisualUpdate->SchedulerBinding.scheduleFrame->SchedulerBinding._handleDrawFrame->SchedulerBinding.handleDrawFrame->SchedulerBinding._invokeFrameCallback
平时通过SchedulerBinding.addPersistentFrameCallback和SchedulerBinding.addPostFrameCallback方法安排的帧回调就是在这里进行处理的.
之后Flutter通过无限循环执行PlatformDispatcher.onBeginFrame和PlatformDispatcher.onDrawFrame方法渲染出精美的软件界面.
读到这里, 你是否注意到和WidgetsBinding.rootElement似乎一毛钱关系都没有呢?
不慌, 它在这里...
WidgetsFlutterBinding.ensureInitialized
还记得runApp方法吗?
dart
void runApp(Widget app) {
final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
_runWidget(binding.wrapWithDefaultView(app), binding, 'runApp');
}
这方法头一句就是WidgetsFlutterBinding.ensureInitialized, 来看看它的神秘面纱.
dart
/// 代码来自[WidgetsFlutterBinding.ensureInitialized]
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding._instance == null) {
WidgetsFlutterBinding();
}
return WidgetsBinding.instance;
}
聪明的你, 应该看出来了, 就是创建了一个单例WidgetsFlutterBinding对象. 您可千万不要被它的表象所迷惑, 这玩意可是一个巨兽...
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding
当您创建WidgetsFlutterBinding对象后会执行父类的构造方法BindingBase
dart
BindingBase() {
if (!kReleaseMode) {
FlutterTimeline.startSync('Framework initialization');
}
assert(() {
_debugConstructed = true;
return true;
}());
assert(_debugInitializedType == null, 'Binding is already initialized to $_debugInitializedType');
//注意这里
initInstances();
assert(_debugInitializedType != null);
assert(!_debugServiceExtensionsRegistered);
initServiceExtensions();
assert(_debugServiceExtensionsRegistered);
if (!kReleaseMode) {
developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
FlutterTimeline.finishSync();
}
}
会触发BindingBase.initInstances方法, 此方法会依次执行:
GestureBinding.initInstances主要用来执行屏幕手势回调处理
dart
@override
void initInstances() {
super.initInstances();
_instance = this;
platformDispatcher.onPointerDataPacket = _handlePointerDataPacket;
}
SchedulerBinding.initInstances在开发阶段用来计算帧率时间等
dart
@override
void initInstances() {
super.initInstances();
_instance = this;
if (!kReleaseMode) {
addTimingsCallback((List<FrameTiming> timings) {
timings.forEach(_profileFramePostEvent);
});
}
}
ServicesBinding.initInstances主要用于平台服务支持等
dart
@override
void initInstances() {
super.initInstances();
_instance = this;
_defaultBinaryMessenger = createBinaryMessenger();
_restorationManager = createRestorationManager();
_initKeyboard();
initLicenses();
SystemChannels.system.setMessageHandler((dynamic message) => handleSystemMessage(message as Object));
SystemChannels.accessibility.setMessageHandler((dynamic message) => _handleAccessibilityMessage(message as Object));
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
SystemChannels.platform.setMethodCallHandler(_handlePlatformMessage);
platformDispatcher.onViewFocusChange = handleViewFocusChanged;
TextInput.ensureInitialized();
readInitialLifecycleStateFromNativeWindow();
initializationComplete();
}
PaintingBinding.initInstances没想到吧,Flutter原生就有图片缓存池
dart
@override
void initInstances() {
super.initInstances();
_instance = this;
_imageCache = createImageCache();
shaderWarmUp?.execute();
}
SemanticsBinding.initInstances平台一些语义,无障碍服务等
dart
@override
void initInstances() {
super.initInstances();
_instance = this;
_accessibilityFeatures = platformDispatcher.accessibilityFeatures;
platformDispatcher
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsActionEvent = _handleSemanticsActionEvent
..onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
_handleSemanticsEnabledChanged();
}
RendererBinding.initInstancesFlutter引擎渲染调度核心
dart
@override
void initInstances() {
super.initInstances();
_instance = this;
_rootPipelineOwner = createRootPipelineOwner();
platformDispatcher
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged;
addPersistentFrameCallback(_handlePersistentFrameCallback);
initMouseTracker();
if (kIsWeb) {
addPostFrameCallback(_handleWebFirstFrame, debugLabel: 'RendererBinding.webFirstFrame');
}
rootPipelineOwner.attach(_manifold);
}
注意, 注意, 注意, 这个addPersistentFrameCallback(_handlePersistentFrameCallback)方法就是无限循环渲染的关键. addPersistentFrameCallback方法, 在前面已经介绍过了, 是不是忘记了? 往上翻一翻~~
WidgetsBinding.initInstances一些和Widget有关的操作
dart
@override
void initInstances() {
super.initInstances();
_instance = this;
assert(() {
_debugAddStackFilters();
return true;
}());
// Initialization of [_buildOwner] has to be done after
// [super.initInstances] is called, as it requires [ServicesBinding] to
// properly setup the [defaultBinaryMessenger] instance.
_buildOwner = BuildOwner();
buildOwner!.onBuildScheduled = _handleBuildScheduled;
platformDispatcher.onLocaleChanged = handleLocaleChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.backGesture.setMethodCallHandler(
_handleBackGestureInvocation,
);
assert(() {
FlutterErrorDetails.propertiesTransformers.add(debugTransformDebugCreator);
return true;
}());
platformMenuDelegate = DefaultPlatformMenuDelegate();
}
去掉杂念, 让我们关注 _handlePersistentFrameCallback方法:
dart
/// 代码来自[RendererBinding._handlePersistentFrameCallback]
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_scheduleMouseTrackerUpdate();
}
/// 代码来自[RendererBinding.drawFrame]
@protected
void drawFrame() {
rootPipelineOwner.flushLayout();
rootPipelineOwner.flushCompositingBits();
rootPipelineOwner.flushPaint();
if (sendFramesToEngine) {
for (final RenderView renderView in renderViews) {
renderView.compositeFrame(); // this sends the bits to the GPU
}
rootPipelineOwner.flushSemantics(); // this sends the semantics to the OS.
_firstFrameSent = true;
}
}
注意到drawFrame方法了吗? 此方法在WidgetsBinding.drawFrame被重写了:
dart
@override
void drawFrame() {
assert(!debugBuildingDirtyElements);
assert(() {
debugBuildingDirtyElements = true;
return true;
}());
TimingsCallback? firstFrameCallback;
bool debugFrameWasSentToEngine = false;
if (_needToReportFirstFrame) {
assert(!_firstFrameCompleter.isCompleted);
firstFrameCallback = (List<FrameTiming> timings) {
assert(debugFrameWasSentToEngine);
if (!kReleaseMode) {
// Change the current user tag back to the default tag. At this point,
// the user tag should be set to "AppStartUp" (originally set in the
// engine), so we need to change it back to the default tag to mark
// the end of app start up for CPU profiles.
developer.UserTag.defaultTag.makeCurrent();
developer.Timeline.instantSync('Rasterized first useful frame');
developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
}
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
firstFrameCallback = null;
_firstFrameCompleter.complete();
};
// Callback is only invoked when FlutterView.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 (rootElement != null) {
buildOwner!.buildScope(rootElement!);
}
super.drawFrame();
assert(() {
debugFrameWasSentToEngine = sendFramesToEngine;
return true;
}());
buildOwner!.finalizeTree();
} finally {
assert(() {
debugBuildingDirtyElements = false;
return true;
}());
}
if (!kReleaseMode) {
if (_needToReportFirstFrame && sendFramesToEngine) {
developer.Timeline.instantSync('Widgets built first useful frame');
}
}
_needToReportFirstFrame = false;
if (firstFrameCallback != null && !sendFramesToEngine) {
// This frame is deferred and not the first frame sent to the engine that
// should be reported.
_needToReportFirstFrame = true;
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback!);
}
}
注意到buildOwner!.buildScope(rootElement!);了吗?
Element被丢到BuildOwner对象中, 然后在super.drawFrame方法中调用了PipelineOwner.flushPaint完成一帧渲染的所有流程.
此流程在将Widget直接渲染成图片如出一辙,
直接将Widget渲染成图片代码如下:
dart
Future<ui.Image> buildImage(Widget widget) async {
final repaintBoundary = RenderRepaintBoundary();
final view = ui.PlatformDispatcher.instance.implicitView ??
RendererBinding.instance.renderView.flutterView;
//渲染树根
final renderView = RenderView(
view: view,
child: RenderPositionedBox(
alignment: Alignment.center,
child: repaintBoundary,
),
configuration: ViewConfiguration.fromView(view),
);
//管道
final pipelineOwner = PipelineOwner()..rootNode = renderView;
renderView.prepareInitialFrame();
//管道对象
final buildOwner = BuildOwner(focusManager: FocusManager());
//根元素
final rootElement = RenderObjectToWidgetAdapter<RenderBox>(
container: repaintBoundary,
child: Directionality(
textDirection: TextDirection.ltr,
child: widget,
)).attachToRenderTree(buildOwner);
//构建树
buildOwner
..buildScope(rootElement)
..finalizeTree();
//渲染树
pipelineOwner
..flushLayout()
..flushCompositingBits()
..flushPaint();
final image = await repaintBoundary.toImage();
return image;
}
总结
runApp方法的参数app是怎么到界面上的?
- 使用
RootWidget包裹appWidget - 然后使用
RootWidget.attach方法将Widget转变成RootElement Element被丢到BuildOwner对象中- 然后调用
PipelineOwner.flushPaint完成一帧渲染
到这里, 我们还有一个东西没有介绍RenderObject, 它要来了...
RenderObject
首先, 并不是所有的Element都有RenderObject,
而Element又是由Widget变换来的, 所以并不是所有的Widget都需要RenderObject.
这你纠正一点, 在Element中renderObject是一个get方法, 所以Element能获取到renderObject, 但不一定有值...
dart
/// 代码来自[Element.renderObject]
RenderObject? get renderObject {
Element? current = this;
while (current != null) {
if (current._lifecycleState == _ElementLifecycle.defunct) {
break;
} else if (current is RenderObjectElement) {
return current.renderObject;
} else {
current = current.renderObjectAttachingChild;
}
}
return null;
}
在Flutter系统里面, 只有RenderObjectElement才有renderObject, RenderObjectWidget会默认创建RenderObjectElement, 所以...
dart
/// 代码来自[RenderObjectElement.renderObject]
@override
RenderObject get renderObject {
assert(_renderObject != null, '$runtimeType unmounted');
return _renderObject!;
}
RenderObject? _renderObject; //注意这里
这里顺便说一下, 平时使用的
Text小部件, 文本InlineSpan是通过渲染对象RenderObject->RenderParagraph使用TextPainter绘制出来的.平时使用的
Image小部件, 图片ui.Image是通过渲染对象RenderObject>RenderImage使用paintImage方法绘制出来的
Element会经历->Element.mount->Element.update->Element.unmount可能不全, 但最核心的就这几个生命周期的回调.
RootElement根元素的mount方法在RootWidget.attach中的BuildOwner.buildScope方法中调用, 代码在之前已经提到过, 这里再来一次, 不劳烦翻页了.
dart
/// 代码来自[RootWidget.attach]
RootElement attach(BuildOwner owner, [ RootElement? element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element!.assignOwner(owner);
});
owner.buildScope(element!, () {
//注意这里
element!.mount(/* parent */ null, /* slot */ null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
而之后子元素Element的mount方法就会在inflateWidget方法中调用了:
dart
Element inflateWidget(Widget newWidget, Object? newSlot) {
...
final Element newChild = newWidget.createElement();
...
//注意这里
newChild.mount(this, newSlot);
...
return newChild;
} finally {
if (isTimelineTracked) {
FlutterTimeline.finishSync();
}
}
}
方法调用链:RootElement.mount->RootElement._rebuild->Element.updateChild->Element.inflateWidget->Widget.createElement->Element.mount
火车就这样开起来了...
RenderObjectElement._renderObject对象就是在RenderObjectElement.mount方法中调用RenderObjectWidget.createRenderObject方法赋值的.
dart
/// 代码来自[RenderObjectElement.mount]
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(() {
_debugDoingBuild = true;
return true;
}());
//注意此处
_renderObject = (widget as RenderObjectWidget).createRenderObject(this);
assert(!_renderObject!.debugDisposed!);
assert(() {
_debugDoingBuild = false;
return true;
}());
assert(() {
_debugUpdateRenderObjectOwner();
return true;
}());
assert(slot == newSlot);
attachRenderObject(newSlot);
super.performRebuild(); // clears the "dirty" flag
}
然后RenderObject.paint方法会被调用, 用来绘制, 里面有熟悉的canvas对象, 这对于Android开发的同学, 再熟悉不过了把?
dart
/// 代码来自[RenderObject.paint]
void paint(PaintingContext context, Offset offset) {
final canvas = context.canvas;
}
RenderObject对象中有:
performLayout方法, 用来实现布局(类似Android中的onMeasure和onLayout)paint方法, 用来实现自绘(类似Android中的onDraw)handleEvent方法, 用来处理手势事件(类似Android中的onTouchEvent)
我将在之后的文章中介绍Flutter中的自定义控件:
- 自定义自绘
Widget(类似于自定义Android中的View) - 自定义布局
Widget(类似于自定义Android中的ViewGroup)
总结
Widget是什么?
用来变换成Element的配置对象.
Element是什么?
用来创建最终的RenderObject对象.
RenderObject是什么?
使用Canvas绘制的, 界面上能看到的都是绘制出来的. 其余类其实都是控制在什么地方绘制用的.
至此文章就结束了! 感谢读者的宝贵时间~
群内有各(pian)种(ni)各(jin)样(qun)的大佬,等你来撩.
联系作者

