赞
踩
1、flutter架构
2、flutter UI系统
Flutter提供了一套Dart API,然后在底层通过OpenGL这种跨平台的绘制库(内部会调用操作系统API)实现了一套代码跨多端。由于Dart API也是调用操作系统API,所以它的性能接近原生。
虽然Dart是先调用了OpenGL,OpenGL才会调用操作系统API,但是这仍然是原生渲染,因为OpenGL只是操作系统API的一个封装库,它并不像WebView渲染那样需要JavaScript运行环境和CSS渲染器,所以不会有性能损失。
我们要开发一个flutter UI界面,需要通过组合其它Widget来实现,在Flutter中,一切都是Widget。当UI要发生变化时,我们不去直接修改DOM,而是通过更新状态,让Flutter UI系统来根据新的状态来重新构建UI。
3、BuildContext
(1)abstract class BuildContext{}
说明:BuildContext是一个抽象类。
(2)无论是StatelessWidget和StatefulWidget的build方法都会传一个BuildContext对象:
Widget build(BuildContext context) {}
(3)那StatelessWidget和StatefulWidget的build方法传入的context对象是哪个实现了BuildContext的类?
以StatelessElement为例:
- class StatelessElement extends ComponentElement {
- ...
- @override
- Widget build() => widget.build(this);
- ...
- }
发现build传递的是this,很明显了,这个BuildContext就是Element类。
总结:BuildContext就是Widget对应的Element,所以我们可以通过context在StatelessWidget和StatefulWidget的build方法中直接访问Element对象。
4、Widget与Element
在Flutter中,Widget描述一个UI元素的配置数据,Widget并不是表示最终绘制在设备屏幕上的显示元素,Flutter中真正代表屏幕上显示元素的类是Element。
5、RenderObject和RenderBox
每个Element都对应一个RenderObject,我们可以通过Element.renderObject 来获取。并且我们也说过RenderObject的主要职责是Layout和绘制,所有的RenderObject会组成一棵渲染树Render Tree。
RenderObject类本身实现了一套基础的layout和绘制协议,但是并没有定义子节点模型(如一个节点可以有几个子节点,没有子节点?一个?两个?或者更多?)。 它也没有定义坐标系统(如子节点定位是在笛卡尔坐标中还是极坐标?)和具体的布局协议(是通过宽高还是通过constraint和size?,或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等)。为此,Flutter提供了一个RenderBox类,它继承自RenderObject,布局坐标系统采用笛卡尔坐标系,这和Android和iOS原生坐标系是一致的,都是屏幕的top、left是原点,然后分宽高两个轴,大多数情况下,我们直接使用RenderBox就可以了,除非遇到要自定义布局模型或坐标系统的情况。
6、命中测试hitTest
一个对象是否可以响应事件,取决于其对命中测试hitTest()的返回,当发生用户事件时,会从根节点(RenderView)开始进行命中测试。我们看看RenderBox默认的hitTest()实现:
- bool hitTest(HitTestResult result, { @required Offset position }) {
- ...
- if (_size.contains(position)) {
- if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
- result.add(BoxHitTestEntry(this, position));
- return true;
- }
- }
- return false;
- }
我们看到默认的实现里调用了hitTestSelf()和hitTestChildren()两个方法,这两个方法默认实现如下:
- @protected
- bool hitTestSelf(Offset position) => false;
- @protected
- bool hitTestChildren(HitTestResult result, { Offset position }) => false;
hitTest 方法用来判断该 RenderObject 是否在被点击的范围内,同时负责将被点击的 RenderBox 添加到 HitTestResult 列表中,参数 position 为事件触发的坐标(如果有的话),返回 true 则表示有 RenderBox 通过了命中测试,需要响应事件,反之则认为当前RenderBox没有命中。在继承RenderBox时,可以直接重写hitTest()方法,也可以重写 hitTestSelf() 或 hitTestChildren(), 唯一不同的是 hitTest()中需要将通过命中测试的节点信息添加到命中测试结果列表中,而 hitTestSelf() 和 hitTestChildren()则只需要简单的返回true或false。
7、flutter显示流程分析
(1)flutter程序(dart程序)启动入口:lib/main.dart的main函数。
(2)runApp分析
- void runApp(Widget app) {
- WidgetsFlutterBinding.ensureInitialized()
- ..attachRootWidget(app)
- ..scheduleWarmUpFrame();
- }
说明:WidgetsFlutterBinding是绑定widget(framework)和flutter engine的桥梁。
(3)WidgetsFlutterBinding分析
- /// A concrete binding for applications based on the Widgets framework.
- ///
- /// This is the glue that binds the framework to the Flutter engine.
- class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
-
- static WidgetsBinding ensureInitialized() {
- if (WidgetsBinding.instance == null)
- WidgetsFlutterBinding();
- return WidgetsBinding.instance;
- }
- }
说明:WidgetsFlutterBinding继承于BindingBase,并混入了很多其他binding。
使用with关键字折叠其他类实现代码重用。
(4)Window
The most basic interface to the host operating system's user interface.
说明:Window是flutter framework连接宿主操作系统UI的接口。
(5)WidgetsBinding.attachRootWidget()方法
- void attachRootWidget(Widget rootWidget) {
- _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
- container: renderView,
- debugShortDescription: '[root]',
- child: rootWidget
- ).attachToRenderTree(buildOwner, renderViewElement);
- }
说明:该方法负责将根widget添加到renderView上。
RenderView是一个RenderObject,而renderViewElement是renderView对应的Element对象,可见该方法主要完成了 根Widget 到根 RenderObject再到根Element的整个关联过程。
(6)scheduleWarmUpFrame()
scheduleWarmUpFrame() 该方法被调用后会立即进行一次绘制,在此次绘制结束前,该方法会锁定事件分发,也就是说在本次绘制结束完成之前Flutter将不会响应各种事件,这可以保证在绘制过程中不会再触发新的重绘。
(7)RenderBinding:负责渲染和绘制逻辑
- @protected
- void drawFrame() {
- assert(renderView != null);
- pipelineOwner.flushLayout();
- pipelineOwner.flushCompositingBits();
- pipelineOwner.flushPaint();
- renderView.compositeFrame(); // this sends the bits to the GPU
- pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
- }
说明:flushPaint不是重绘了所有 RenderObject,而是只重绘了需要重绘的 RenderObject。
参考文档:
https://book.flutterchina.club/chapter14/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。