概览
Flutter本质上是一个跨平台的UI工具集,允许在各自操作系统上复用同样的代码。
尽可能提供原生体验的高性能和复用代码。
开发中,Flutter应用在一个VM上运行,使得可在保留状态且无需重新编译情况下,进行热加载。
发行时,Flutter应用会直接通过AOT编译为机器码或者是JS。
分层模型
Flutter是一个可拓展的分层系统,可被视为各个独立组件的集合,上层组件依赖下层,上层无法越界访问更下层的组件,框架内各部分是可选可替代的。
分为框架、引擎、嵌入层
嵌入层
对于底层操作系统,Flutter应用程序的包装方式与其他原生应用相同,每个平台都会包含一个特定的嵌入层,提供一个程序入口,使得程序可以与底层操作系统进行协调、访问服务和管理事件循环队列。
对于不同平台,嵌入层使用的语言都不一样。Flutter代码可以作为模块集成到现有应用或作为应用主体。
引擎
主要使用C++编写,提供Flutter应用所需原语。
当需要绘制新一帧内容时,引擎将负责对需要合成的场景进行栅格化。
其提供Flutter核心API的底层实现:图形、文本布局等…
引擎将底层C++代码包装成Dart代码,通过dart:ui暴露给Flutter框架层。该库暴露了最底层的原语,包括用于驱动输入,图形等子系统的类
框架
开发者通过Flutter框架和Flutter交互,该框架提供了以Dart语言编写的现代响应式框架。
自下而上有:
Foundational and Annimation、Painting、Gestures
Foundational:基础类
Annimation:动画
Painting:绘画
Gestures:手势
向上层提供常用的抽象
渲染层
提供操作布局的抽象,操作层使得可以构建一颗可渲染对象的树,可动态更新。
Widget
组合的抽象,每个渲染层中的渲染对象在widget层中有一个对应的类。
widget层可让你自由组合需要复用的各种类,引入了响应式编程模型。
Material和Cupertino
提供了全面的widget层的原语组合,分别实现了Material和IOS设计规范。
其他更高层级的功能被拆分到不同的软件包中。
应用剖析
- Dart应用:将Widget合成预期的UI,实现业务。
- 框架:提供API封装,将应用widget树构建在一个Scene中。
- 引擎:将合成的Scene进行栅格化、封装Flutter进行封装,暴露功能给dart:ui API给框架、使用嵌入层API与平台进行整合。
- 嵌入层:协调底层操作系统的服务、管理事件循环体系、暴露特定平台API给应用集成嵌入层。
- 运行器:将嵌入层暴露的平台API合成为目标平台可运行的应用包。
响应式用户界面
Flutter是一个响应式的且伪声明式的UI框架,
随widget构造方法引入的新输入会随着其build方法传播给更低等级的widget;
而底层的widget中出现的修改也会沿着结构树通过event handler向上传播。
函数-响应式、指令-响应式都有出现,
前者是指widget的build方法中只包含其针对变化如何响应的表达式,
后者则包含一系列构造子元素的表达式,用于描述该widget如何响应变化。
Flutter,widget用来配置对象树的不可变类,这些widget管理单独的布局对象树,接着参与管理合成的布局对象树。
Flutter核心是一套高效的遍历树的变动的机制,将对象树转换为更底层的对象树,在树间传递更改。
build将状态转换为UI,在框架需要都可以被调用,可快速执行且没有额外影响,依赖DART的运行时特征:对象的快速实例化和清除。
Widgets
Flutter强调以widgets为组成单位。widgets是构建flutter应用界面的基础块,每个widget都是一部分不可变的UI声明。
widget通过布局组合形成一种层次结构关系,每个widget都嵌套在其父级的内部,可以通过父级接收上下文。从根布局(MaterialApp或CupertinoApp)开始,自上而下。
应用根据事件交互,通知框架替换层级中的旧widget为新的widget,比较新旧widget,高效更新。
Flutter拥有自己的UI控制实现,不使用系统自带。
这样提供了无限拓展性,不受系统提供的拓展限制。
Flutter可直接合成所有的场景,避免与原生平台来回切换,避免了性能瓶颈。
将应用的行为与操作系统的依赖解耦,实现不同平台,体验一致。
组成
widget通常由更小的且用途单一的widget组合而成,提供更强大的功能。
Flutter 在widget层中使用了widget来表示屏幕上的绘制、布局等
Flutter 在动画层,Animation和Tween涵盖了大部分的设计空间
Flutter 在渲染层,RenderObject用来描述布局、绘制、触摸判断等
Widget思想是 浅而广,最大限度地增加可能的组合数量,每个widget完成一件事,将核心功能抽象,如边距等基础功能,被实现为单独的组件
构建widget
build方法会返回一个新的元素树,这棵树更具体地表示了widget在ui中的部分。
框架会递归请求每个widget进行构建,直到整棵树都被具体的可渲染对象描述为止。
框架会将可渲染的对象缝合在一起,组合成可渲染对象树。
每个渲染帧,Flutter都可以根据变化的状态,调用build方法重建部分UI。
因此,build方法轻量且能快速返回widget,计算工作通过一些异步方法完成,并存储在状态中,由build使用。
widget状态
框架包含两种核心的widget:有无状态widget StatefulWidget / StatelessWidget
改变state时,调用setState来告知框架,调用State的构建方法来更新UI
Flutter将状态和widget对象分离,无需担心状态丢失,可随时创建新实例和复用已存在的状态对象
状态管理
通过context传递状态信息,子widget通过build(BuildContext context)方法接受父widget传入的信息,初始化所需数据。
随着widget树层级加深,依赖树形结构上下传递状态信息会变得十分困难。
InheritedWidget将一个共同的祖先结点包裹在widget树中
final studentState = StudentState.of(context);
通过of(context)会根据当前构建的上下文,返回类型为StudentState的在树中距离最近的祖先结点。
InheritedWidget包含updateShouldNotify()来判断widget是否需要重建。
provider用于状态管理,对InheritedWidget进行进一步报装,如flutter_hooks也可以用来替换状态至ui。
渲染过程
Flutter的渲染模型
Android系统提供了将自身绘制到Canvas对象的组件,接着使用C/C++编写的Skia图像引擎,调用CPU和GPU完成再设别上绘制。
一般的跨平台框架会在Android和IOS的UI底层库上创建一层抽象,程序代码通常使用JS进行编写,基于与系统进行交互来显示UI界面,当UI和应用逻辑有繁杂的交互时更会如此。
Flutter通过绕过系统UI组件库,使用自己的widget内容集,削减了抽象层的开销。
绘制Flutter图像内容的Dart代码被编译为机械码,使用平台提供的skia渲染,Flutter也嵌入自己的skia副本。
输入事件执行流程
- User input 响应用户输入:对输入手势进行响应(点击效果等)
- Animation 执行动画:由计时器触发界面更改
- Build 构建组件:创建widget在屏幕上
- Layout 组件布局:定位和测量屏幕上的元素 -----------------
- Paint 绘画组件:将元素转换为虚拟表示 ----------------- Rendering渲染
- Composition 整合组件:按绘画顺序覆盖组件 -----------------
- Rasterize 光栅化:将输出翻译为GPU绘画指令
Widget到Element
build方法返回一棵基于当前应用状态来绘制UI的widget子树。
build方法会在必要时,根据状态引入新widget,如color为空时引入ColoredBox,image引入RawImage,Text引入RichText
构建时,Flutter会将widget转换为Element树,每个widget都有一个对应的Element
分为两种:
ComponentElement:其他Element的宿主
RenderObjectElement:参与布局或者绘制阶段的Element
RenderObjectElement 是底层 RenderObject 与对应的 widget 之间的桥梁。
对 widget 树做的任何操作(例如将 Text(‘A’) 替换成 Text(‘B’))都会返回一个新的 widget 对象集合。但这并不意味着底层呈现的内容必须要重新构建。 Element 树每一帧之间都是持久化的,因此起着至关重要的性能作用, Flutter 依靠该优势,实现了一种好似 widget 树被完全抛弃,而缓存了底层表示的机制。 Flutter 可以根据发生变化的 widget,来重建需要重新配置的 Element 树的部分。
布局和渲染
在渲染树中,每个节点的基类都是RenderObject,为布局和绘制定义了一个抽象模型。
每个RenderObject都了解其父节点的信息,但对于其子节点,除了访问和获得他们的布局约束,并没有更多的信息。
这样使得RenderObject拥有更高效的抽象能力。
构建时,Flutter会为Element树中的每个RenderObjectElement创建或更新其对应的一个从RenderObject对象。
大部分Flutter widget 由一个继承了RenderBox的子类的对象渲染的,其呈现出的RenderObject拥有固定的大小。提供了盒子限制模型,为每个widget关联了渲染的最小和最大的宽度和高度。
进行布局时,Flutter会以DFS遍历渲染树,将限制以自上而下的方式从父节点传递给子节点。
子节点若要确定自己的大小,必须循环父节点传递的限制。
子节点的响应方式是在父节点建立的约束内将大小以自下而上的方式传递给父节点。
遍历完一次树后,每个对象都通过父级约束而拥有了明确的大小,随时可以通过调用paint渲染。
盒子限制模型的对象布局的时间复杂度为O(n)
父节点可通过设定最大和最小的尺寸限制,决定其子节点对象的大小。
父节点可以决定子节点的宽度,然后自适应布局高度。
LayoutBuilder widget 的子节点可以得到从上层传递下来的约束,并合理利用该约束对象。
RenderObject根节点为RenderView,代表渲染树的总体输出。当平台需要渲染新一帧内容时,会调用一次compositeFrame方法,是RenderView的一部分。
compositeFrame创建一个SceneBuilder来触发当前画面的更新,更新完后,RenderView会将合成的画面传递给dart:ui中的Window.render方法来控制GPU进行渲染。
平台嵌入层
Flutter界面构建、布局、合成和绘制都由Flutter自己完成,获取纹理和联动应用底层的生命周期的方法,不可避免地根据平台特性而改变。Flutter引擎本身是平台无关,提供ABI接口设置并使用Flutter。
平台嵌入层提供一个入口,初始化Flutter引擎,获取UI和栅格化线程。嵌入层充当宿主操作系统和Flutter之间的粘合剂。还负责管理应用的声明周期。
Flutter集成其他代码
Flutter提供了多种代码交互机制,可以将原生代码嵌入Flutter,或者Flutter嵌入现有应用。
平台通道
Flutter通过 平台通道 调用自定义代码。通过创建一个常用的通道,开发者可以在Dart与使用Kotlin等语言编写的平台组件间发送和接收消息。
数据由Dart类型序列化为一种标准格式,再反序列化为kotlin等中的等效类型。
Web支持
。。。