Flutter框架分析(笔记)
一、总览和window
1. 渲染流水线 Render pipeline
Vsync
| ScheduleFrame
Animate ------------------->
| <-------------------
Layout Flutter 框架 OnBeginFrame Engine
| <--------------------
Paint onDrawFrame
| --------------------->
scene Scene
2.window Flutter中的window是单例
和javascript 开发一样,Flutter 底层的页面最后也是展示到window上的。主要对上层提供屏幕尺寸,调度接口,图形绘制接口,输入事件回调等核心服务
总体来说,window
集中提供了Flutter引擎中和图形界面相关的接口
二、Flutter 框架的初始化
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
1.runApp入口做了三件事
a:WidgetsFlutterBinding.ensureInitialized() 通过mixin 混入了一些绑定,重要的有以下三个:
每个Binding都是继承自BaseBinding,BaseBinding的构造里面initInstances,还有返回了一个window对象。
举例:可以看到在调用initInstances,内部做的事情其实就是把一些Window对上提供的接口绑定到window对象上。如下面这个就是把处理点击的手势绑定到window并返回一个回调。
mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget { @override void initInstances() { super.initInstances(); _instance = this; window.onPointerDataPacket = _handlePointerDataPacket; }
1⃣️ ScheduleBinding 绑定onBeginFrame和onDrawFrame的回调
mixin SchedulerBinding on BindingBase, ServicesBinding { @override void initInstances() { super.initInstances(); _instance = this; window.onBeginFrame = _handleBeginFrame; window.onDrawFrame = _handleDrawFrame; SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage); }
2⃣️ RenderBinding PipelineOwner 和RenderView 做渲染的绑定
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { @override void initInstances() { super.initInstances(); _instance = this; _pipelineOwner = PipelineOwner( onNeedVisualUpdate: ensureVisualUpdate, onSemanticsOwnerCreated: _handleSemanticsOwnerCreated, onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed, ); window ..onMetricsChanged = handleMetricsChanged ..onTextScaleFactorChanged = handleTextScaleFactorChanged ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged ..onSemanticsAction = _handleSemanticsAction; initRenderView(); _handleSemanticsEnabledChanged(); assert(renderView != null); addPersistentFrameCallback(_handlePersistentFrameCallback); _mouseTracker = _createMouseTracker(); }
3⃣️ WidgetsBinding BuildOwner 主要做build构建
mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { @override void initInstances() { super.initInstances(); _instance = this; buildOwner.onBuildScheduled = _handleBuildScheduled; window.onLocaleChanged = handleLocaleChanged; window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged; SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation); SystemChannels.system.setMessageHandler(_handleSystemMessage); }
b:attachRootWidget(app) 通过attach把在ensureInitialized()绑定的Renderview和Element给关联了起来
void attachRootWidget(Widget rootWidget) { _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>( container: renderView, debugShortDescription: '[root]', child: rootWidget ).attachToRenderTree(buildOwner, renderViewElement); }
c:scheduleWarmUpFrame 给engine申请一帧,把build构建和Layout paint后的scene送给Engine显示到屏幕
void scheduleWarmUpFrame() { ... Timer.run(() { ... handleBeginFrame(null); ... }); Timer.run(() { ... handleDrawFrame(); ... }); }
- 3个重要绑定:
SchedulerBinding
,RendererBinding
和WidgetsBinding
。 - 2个“owner”:
PipelineOwner(渲染流水线)
和BuildOwner(管理widget的重建)
。 - 2颗树的根节点:render tree根节点
RenderView
;element tree根节点RenderObjectToWidgetElement
。
三、Widget Element RenderObject
1.Widget 是Element的描述和配置,StatefulWidget statelessWidget 里面的build()方法实际调用的其实是Element
2.Element ,只关心自己的Element tree , Element 的主要工作都处于渲染流水线都build阶段。
3.RenderObject,负责渲染流程,layout和paint阶段。
widget:
@immutable abstract class Widget extends DiagnosticableTree { const Widget({ this.key }); ... @protected Element createElement(); ... }
StatelessWidget:
abstract class StatelessWidget extends Widget { /// Initializes [key] for subclasses. const StatelessWidget({ Key key }) : super(key: key); @override StatelessElement createElement() => StatelessElement(this); @protected Widget build(BuildContext context); }
StatefullWidget:
abstract class StatefulWidget extends Widget { @override StatefulElement createElement() => StatefulElement(this); @protected State createState(); }
State:
abstract class State<T extends StatefulWidget> extends Diagnosticable { T get widget => _widget; T _widget; BuildContext get context => _element; StatefulElement _element; bool get mounted => _element != null; void initState() { } void didUpdateWidget(covariant T oldWidget) { } void setState(VoidCallback fn) { final dynamic result = fn() as dynamic; _element.markNeedsBuild(); } void deactivate() { } void dispose() { } Widget build(BuildContext context); void didChangeDependencies() { } }
通过State代码可以看到,context =>返回的是_element ,build的方法传入的BuildContext 是_element,也就是Statefullwidget的CreateElement()方法获取到的element
1.State 拥有widget和element
2.context 就是element
3.build()方法传入的就是element
4.mounted ,判断的是element是否为null,也就是State是否有关联到element tree的element
5.setState,只是简单的判断element是不是需要重新Build。如果element为null,setState调用会报错,所以需要用mounted判断一下
6.didChangeDependencies,当依赖的数据发生变化的时候的回调,主要就是inheritedWidget的数据依赖
7.didUpdateWidget,当State里面的widget被更换了的回调。如果类型相同,是可以更换widget的
8.build(),构建。。。。
Element:
abstract class Element extends DiagnosticableTree implements BuildContext { Element _parent; Widget _widget; BuildOwner _owner; dynamic _slot; void visitChildren(ElementVisitor visitor) { } Element updateChild(Element child, Widget newWidget, dynamic newSlot) { } void mount(Element parent, dynamic newSlot) { } void unmount() { } void update(covariant Widget newWidget) { } @protected Element inflateWidget(Widget newWidget, dynamic newSlot) { ... final Element newChild = newWidget.createElement(); newChild.mount(this, newSlot); return newChild; } void markNeedsBuild() { if (dirty) return; _dirty = true; owner.scheduleBuildFor(this); } void rebuild() { if (!_active || !_dirty) return; performRebuild(); } @protected void performRebuild(); }
1.markNeedsBuild,标记element为dirty,需要重build,在渲染下一帧的时候这个element会被重绘。
2.updateChild(),
a:新widget为空,老widget为空,则啥也不做
b:新widget为空,老widget不为空,则widget被移除
c:新widget不为空,老widget为空,则创建一个element
d:新widget不为空,老widget不为空,则调用update,具体由子类实现
3.新element被实例化后,调用mount加入element tree,移除的时候调用unmount移除
4.rebuild 在渲染流水线build阶段被调用,具体的重绘,在performRebuild,由子类实现
abstract class ComponentElement extends Element { ComponentElement(Widget widget) : super(widget); Element _child; @override void performRebuild() { Widget built; built = build(); _child = updateChild(_child, built, slot); } Widget build(); }
performRebuild会调用build方法,实例化一个widget,由子类实现
重点:
statelessElement:
class StatelessElement extends ComponentElement { @override Widget build() => widget.build(this); @override void update(StatelessWidget newWidget) { super.update(newWidget); _dirty = true; rebuild(); } }
1.widget的build方法,实际是由他创建的element的build方法中调用,在performRebuild的时候被执行重绘。而且可以看到widget.build(this),可以知道buildContext传入的context上下文就是这个widget对应的element
statefullelement
class StatefulElement extends ComponentElement { /// Creates an element that uses the given widget as its configuration. StatefulElement(StatefulWidget widget) : _state = widget.createState(), super(widget) { _state._element = this; _state._widget = widget; } @override Widget build() => state.build(this); @override void _firstBuild() { final dynamic debugCheckForReturnedFuture = _state.initState() _state.didChangeDependencies(); super._firstBuild(); } @override void deactivate() { _state.deactivate(); super.deactivate(); } @override void unmount() { super.unmount(); _state.dispose(); _state._element = null; _state = null; } @override void didChangeDependencies() { super.didChangeDependencies(); _state.didChangeDependencies(); } }
1.firstBuild -- _state.initState() -- _state.didChangeDependencies()
2.deactivate() -- _state.deactivate()
3.unmount -- _state.dispose() -- _state.element = null
4.didChangeDependencies -- didChangeDependencies()
RenderObject
负责渲染流水线布局(layout)阶段和绘制(paint)阶段的工作。同时也维护render tree
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { void markNeedsLayout() { ... } void markNeedsPaint() { ... } void layout(Constraints constraints, { bool parentUsesSize = false }) { ... if (sizedByParent) { performResize(); } ... performLayout(); ... } void performResize(); void performLayout(); void paint(PaintingContext context, Offset offset) { } }
四、Flutter框架运行
1.State.setState(),到engine那去请求一帧。主要做到就是标记element为dirty,然后添加到dirtyElement的列表中
2.等待Vsync信号到来,然后渲染流水线开始重建新的一帧,最后送入engine显示。主要是onBeginFrame,onDrawFrame--drawFrame,显示buildOwner buildScope,构建,然后pipelineOwner,flushLayout和flushPaint,执行布局和绘制,最后window render 把scene,请求engine显示到屏幕。
重点:
setState后,执行顺序:也就是setState后,最后window会去engine请求一帧
1.Widget: setState() ->Element: element.markNeedBuild ->Element: owner.scheduleBuildFor dirty = true ... -> BuildOwner: scheduleBuildFor _dirtyElements.add(element) ->
Binding: handleBuildSchedule ensureVisualupdate -> ScheduleBinding: _scheduleFrame -> Window: scheduleFrame 向engine发起请求一帧
build阶段:dirtyElement排序,然后执行rebuild ,performRebuild,最后调用到State的build方法,然后由父到子节点开始rebuild
1.Element重建的时候其子Element也都会重建
2.流程:
_dirtyElements sort 排序 -> element.rebuild -> element.performRebuild -> State.build ===>Widget ===> updateChild -> 循环由父到子Element执行,实现rebuild
五、Flutter框架 ---Animate动画
1.怎么实现动画
a:State混入SingleTickerProviderStateMixin
b:initState初始化AnimationController和Animate=Tween()
c:build里面用到Animate.value或Animate的其他值
d:dispose释放Controller
2.SingleTicker Ticker给动画提供Vsync信号
3.Tween里面用到transform,把AnimationController只能定义0-1的值给转换了
4.simulation,动画的本质,外力作用下不同时间点的运动状态的变化
5.evaluatable.evaluate计算transform的转换
6.Tween,线性插值,还有其他的插值,curveAnimation,非线性插值
7.定义自己的非线性动画,只需重写变换函数
class shakeCurve extends Curve{
@override
double transform(double t){
return sin(t*pi*);
}
}
流程:1 singleTicker 2 Ticker驱动Vsync 3Ticker源码执行ScheduleTick 4ScheduleBinding,加入到Transient 5window onbeginFrame 和onDrawFrame 6以上ticker的有AnimationController.createTicker驱动
8 动画的值,Animation的transform做计算转换
六Flutter框架 Layout 布局
1.一下一上
由下到上传递尺寸size
有上到下传递约束
先layout父,避免重复layout子。===》pipelineOwner.flushLayout,实际是renderBox做的,它是renderVIew的子类
2.Alignment,孩子在父的位置有Alignment决定的
左到右 -1===1
上到下 -1===1
七Flutter框架 Paint 绘制
绘制过程其实就是把Element Tree 转换成Layer Tree 图层树的过程,然后把scene场景送入Engine显示。绘制过程比较复杂
贴上大神的博客地址:https://juejin.im/post/6844903791427321863#comment