广西seo网站推广,阿里巴巴国际贸易网,素材视频,设置wordpress首页显示文章摘要Flutter是Google推出并开源的跨平台开发框架#xff0c;主打跨平台、高保真、高性能。开发者可以通过Dart语言开发Flutter应用#xff0c;一套代码同时运行在ios和Android平台。不仅如此#xff0c;flutter还支持web、桌面、嵌入应用的开发。flutter提供了丰富的组件、接口主打跨平台、高保真、高性能。开发者可以通过Dart语言开发Flutter应用一套代码同时运行在ios和Android平台。不仅如此flutter还支持web、桌面、嵌入应用的开发。flutter提供了丰富的组件、接口开发者可以很快地为flutter添加native扩展。同时flutter还使用skia引擎(其实Android原生中页面绘制也是由skia引擎来渲染的)渲染视图这无疑为用户提供良好地体验。
Flutter框架地整体架构 简单来讲Flutter 从上到下可以分为三层框架层、引擎层和嵌入层。
框架层
Flutter Framework即框架层。这是一个纯 Dart实现的 SDK它实现了一套基础库自底向上我们来简单介绍一下
底下两层Foundation 和 Animation、Painting、Gestures在 Google 的一些视频中被合并为一个dart UI层对应的是Flutter中的dart:ui包它是 Flutter Engine 暴露的底层UI库提供动画、手势及绘制能力。Rendering 层即渲染层这一层是一个抽象的布局层它依赖于 Dart UI 层渲染层会构建一棵由可渲染对象组成的渲染树当动态更新这些对象时渲染树会找出变化的部分然后更新渲染。渲染层可以说是Flutter 框架层中最核心的部分它除了确定每个渲染对象的位置、大小之外还要进行坐标变换、绘制调用底层 dart:ui 。Widgets 层是 Flutter 提供的一套基础组件库在基础组件库之上Flutter 还提供了 Material 和 Cupertino 两种视觉风格的组件库它们分别实现了 Material 和 iOS 设计规范。
引擎层
Engine即引擎层。毫无疑问是 Flutter 的核心 该层主要是 C 实现其中包括了 Skia 引擎、Dart 运行时Dart runtime、文字排版引擎等。在代码调用 dart:ui库时调用最终会走到引擎层然后实现真正的绘制和显示。
嵌入层
Embedder即嵌入层。Flutter 最终渲染、交互是要依赖其所在平台的操作系统 API嵌入层主要是将 Flutter 引擎 ”安装“ 到特定平台上。嵌入层采用了当前平台的语言编写例如 Android 使用的是 Java 和 C iOS 和 macOS 使用的是 Objective-C 和 Objective-CWindows 和 Linux 使用的是 C。 Flutter 代码可以通过嵌入层以模块方式集成到现有的应用中也可以作为应用的主体。Flutter 本身包含了各个常见平台的嵌入层假如以后 Flutter 要支持新的平台则需要针对该新的平台编写一个嵌入层。
Flutter绘制原理
为了熟悉flutter地绘制原理我们先从屏幕显示图像地基本原理说起我们在买显示器时都会关注显示器地刷新频率那么对于手机屏幕也是一样通常手机屏幕地刷新频率是60Hz当然现在也有不少高刷新频率地手机也在推出如90Hz120Hz。 一般来说计算机系统中CPU、GPU和显示器以一种特定地方式协作CPU将计算好地显示内容提交给GPUGPU渲染后放入帧缓冲区然后视频控制器按照VSync信号从帧缓冲区取帧数据传递给显示器显示。当一帧图像绘制完毕后准备绘制下一帧时显示器会发出一个垂直同步信号(VSync)所以60Hz的屏幕就会一秒内发出60次这样的信号。
上面是CPU、GPU和显示器协作方式对于Flutter也不例外Flutter也遵循了这种模式 GPU的VSync信号同步给到UI线程UI线程使用Dart来构建抽象的视图结构(这一步是在Framework层中实现的)绘制好的抽象视图结构会在GPU线程中进行图像的合成(这一步在引擎层中完成)然后提供给skia渲染成GPU所需要的数据最终提供给GPU进行渲染。由此可知flutter高性能的核心其实就在于根据vsync信号进行图像的快速构建也就是上图中的绿色部分。
Flutter渲染流程 在Flutter框架中存在着一个渲染流程。这个渲染流水线是由垂直同步信号驱动的而Vsync信号由系统提供的如果你的Flutter app是运行在Android上那Vsync信号就是我们熟悉的Android的那个Vsync信号。
当Vsync信号到来了以后Flutter框架会按照图里的顺序执行一系列动作
动画构建布局绘制
最终生成一个场景之后送往底层由GPU绘制到屏幕上。
动画阶段因为动画会随着每个Vsync信号的到来而改变状态(State),所以动画阶段是流水线的第一个阶段构建阶段在这个阶段那些需要被重新构建的Widget会在此时被重新构建。也就是我们熟悉的build()方法被调用的时候。布局阶段这时会确定各个显示元素的位置、尺寸此时是RenderObject.performLayout()被调用的时候绘制阶段此时是RenderObject.paint()被调用的时候
Flutter组件的生命周期 createState()当框架要创建一个StatefulWidget时它会立即调用State的createState()initState()当State的构造方法被执行后会调用一次initState()需要指出的是initState在State生命周期中只会被调用一次build()这个方法会被经常调用比如setState以及配置改变都会触发build方法的调用didUpdateConfig()当收到一个新的config时调用setState()当需要修改页面状态比如刷新数据等的时候我们可以通过调用setState来实现dispose()当移除State对象时将调用dispose方法通常在该方法中进行取消订阅取消所有动画、流等操作。
Flutter渲染机制——为了高性能而生的三棵树
在 Flutter 中 widget 的功能是“描述一个UI元素的配置信息”它就是说 Widget 其实并不是表示最终绘制在设备屏幕上的显示元素所谓的配置信息就是 Widget 接收的参数比如对于 Text 来讲文本的内容、对齐方式、文本样式都是它的配置信息。
既然 Widget 只是描述一个UI元素的配置信息那么真正的布局、绘制是由谁来完成的呢Flutter 框架的处理流程是这样的
根据 Widget 树生成一个 Element 树Element 树中的节点都继承自 Element 类。根据 Element 树生成 Render 树渲染树渲染树中的节点都继承自RenderObject 类。 3。 根据渲染树生成 Layer 树然后上屏显示Layer 树中的节点都继承自 Layer 类。
真正的布局和渲染逻辑在 Render 树中Element 是 Widget 和 RenderObject 的粘合剂可以理解为一个中间代理。
从创建到渲染的大体流程是根据Widget生成Element然后创建相应的RenderObject并关联到Element.renderObject属性上最后再通过RenderObject来完成布局排列和绘制。
接下来我们重点看下Element这个东西Element的生命周期如下
Framework 调用Widget.createElement 创建一个Element实例记为element。Framework 调用 element.mount(parentElement,newSlot) mount方法中首先调用element所对应Widget的createRenderObject方法创建与element相关联的RenderObject对象然后调用element.attachRenderObject方法将element.renderObject添加到渲染树中插槽指定的位置这一步不是必须的一般发生在Element树结构发生变化时才需要重新添加。插入到渲染树后的element就处于“active”状态处于“active”状态后就可以显示在屏幕上了可以隐藏。当有父Widget的配置数据改变时同时其State.build返回的Widget结构与之前不同此时就需要重新构建对应的Element树。为了进行Element复用在Element重新构建前会先尝试是否可以复用旧树上相同位置的elementelement节点在更新前都会调用其对应Widget的canUpdate方法如果返回true则复用旧Element旧的Element会使用新Widget配置数据更新反之则会创建一个新的Element。Widget.canUpdate主要是判断newWidget与oldWidget的runtimeType和key是否同时相等如果同时相等就返回true否则就会返回false。根据这个原理当我们需要强制更新一个Widget时可以通过指定不同的Key来避免复用。当有祖先Element决定要移除element 时如Widget树结构发生了变化导致element对应的Widget被移除这时该祖先Element就会调用deactivateChild 方法来移除它移除后element.renderObject也会被从渲染树中移除然后Framework会调用element.deactivate 方法这时element状态变为“inactive”状态。“inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element“inactive”态的element在当前动画最后一帧结束前都会保留如果在动画执行结束后它还未能重新变成“active”状态Framework就会调用其unmount方法将其彻底移除这时element的状态为defunct,它将永远不会再被插入到树中。如果element要重新插入到Element树的其他位置如element或element的祖先拥有一个GlobalKey用于全局复用元素那么Framework会先将element从现有位置移除然后再调用其activate方法并将其renderObject重新attach到渲染树。
现在读者应该能够知道为什么笔者说“三棵树是为了性能而生的”。因为视图中的控件都是Render树中的对象我们知道new出一个对象都是需要消耗操作系统很多资源的通过三棵树就可以做到对已有render树中的对象进行复用而不是每更改一个widget就new出一个新的render对象。
这里额外多提一嘴我们在日常开发的过程中经常会接触到BuildContext这个东西。那这个东西究竟是什么呢笔者就直接说答案了有兴趣的同学可以看下源码。BuildContext就是widget对应的Element所以我们可以通过context在StatelessWidget和StatefulWidget的build方法中直接访问Element对象。我们获取主题数据的代码Theme.of(context)内部正是调用了Element的dependOnInheritedWidgetOfExactType()方法。
三棵树的运用
我们可以看到Element是Flutter UI框架内部连接widget和RenderObject的纽带大多数时候开发者只需要关注widget层即可但是widget层有时候并不能完全屏蔽Element细节所以Framework在StatelessWidget和StatefulWidget中通过build方法参数又将Element对象也传递给了开发者这样一来开发者便可以在需要时直接操作Element对象。 那么现在笔者有个问题如果没有widget层单靠Element层是否可以搭建起一个可用的UI框架如果可以应该是什么样子
案例如下
class HomeView extends ComponentElement{HomeView(Widget widget) : super(widget);String text 123456789;overrideWidget build() {Color primaryTheme.of(this).primaryColor; //1return GestureDetector(child: Center(child: TextButton(child: Text(text, style: TextStyle(color: primary),),onPressed: () {var t text.split()..shuffle();text t.join();markNeedsBuild(); //点击后将该Element标记为dirtyElement将会rebuild},),),);}
}上面build方法不接收参数这一点和在StatelessWidget和StatefulWidget中build(BuildContext)方法不同。代码中需要用到BuildContext的地方直接用this代替即可如代码注释1处Theme.of(this)参数直接传this即可因为当前对象本身就是Element实例。 当text发生改变时我们调用markNeedsBuild()方法将当前Element标记为dirty即可标记为dirty的Element会在下一帧中重建。实际上State.setState()在内部也是调用的markNeedsBuild()方法。 上面代码中build方法返回的仍然是一个widget这是由于Flutter框架中已经有了widget这一层并且组件库都已经是以widget的形式提供了如果在Flutter框架中所有组件都像示例的HomeView一样以Element形式提供那么就可以用纯Element来构建UI了HomeView的build方法返回值类型就可以是Element了。
如果我们需要将上面代码在现有Flutter框架中跑起来那么还是得提供一个“适配器”widget将HomeView结合到现有框架中下面CustomHome就相当于“适配器”
class CustomHome extends Widget {overrideElement createElement() {return HomeView(this);}
}后记
给大家推荐一本flutter相关的电子书籍《Flutter实战·第二版》