文章目录

  • ​​前言​​
  • ​​一、组成​​
  • ​​二、简述Flutter UI 系统​​
  • ​​三、Widget & Element​​
  • ​​四、RenderObject & RenderBox​​
  • ​​五、BuildContext​​
  • ​​六、Widget & State & Context​​
  • ​​七、Widget 的 StatelessWidget 和 StatefulWidget​​
  • ​​八、StatefulWidget 的生命周期​​
  • ​​九、状态管理​​
  • ​​十、Flutter 的热重载​​
  • ​​十一、与Android 原生通信​​
  • ​​参考文章​​

前言

Flutter 是 Google 推出的一套开源跨平台 UI 框架,可以快速地在 Android、iOS 和 Web 平台上构建高质量的原生用户界面。同时,Flutter 还是 Google 新研发的 Fuchsia 操作系统的默认开发套件。在全世界,Flutter 正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。Flutter 采用现代响应式框架构建,其中心思想是使用组件来构建应用的 UI。当组件的状态发生改变时,组件会重构它的描述,Flutter 会对比之前的描述,以确定底层渲染树从当前状态转换到下一个状态所需要的最小更改。


一、组成

Flutter 基本概念理解_UI

  • Framework

    Flutter 的 FrameWork 层是用 Drat 编写的框架(SDK),它实现了一套基础库,包含 Material(Android 风格 UI)和 Cupertino(iOS 风格)的 UI 界面,下面是通用的 Widgets(组件),之后是一些动画、绘制、渲染、手势库等。这个纯 Dart 实现的 SDK 被封装为了一个叫作 dart:ui 的 Dart 库。我们在使用 Flutter 写 App 的时候,直接导入这个库即可使用组件等功能。
  • Engine

    Flutter 的 Engine 层是 Skia 2D 的绘图引擎库,其前身是一个向量绘图软件,Chrome 和 Android 均采用 Skia 作为绘图引擎。Skia 提供了非常友好的 API,并且在图形转换、文字渲染、位图渲染方面都提供了友好、高效的表现。Skia 是跨平台的,所以可以被嵌入到 Flutter 的 iOS SDK 中,而不用去研究 iOS 闭源的 Core Graphics / Core Animation。Android 自带了 Skia,所以 Flutter Android SDK 要比 iOS SDK 小很多。
  • Embedder

    Embedder 是操作系统适配层,实现了渲染 Surface 设置,线程设置,以及平台插件等平台相关特性的适配。

二、简述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。

三、Widget & Element


在Flutter中,Widget描述一个UI元素的配置数据,Widget并不是真正显示在设备屏幕上面的UI元素。代表设备屏幕上面显示的UI元素的是Element的实例。以Java的类、类的属性、对象来理解,Widget设置了Elememt的属性,对应每一个RenderObject对象,也就是显示在设备屏幕上面的UI元素。

四、RenderObject & RenderBox


每个 Element 都对应一个 RenderObject,我们可以通过 Element.renderObject 来获取。并且我们也说过 RenderObject 的主要职责是 Layout 和绘制,所有的 RenderObject 会组成一棵渲染树 Render Tree。 RenderObject 类本身实现了一套基础的 layout 和绘制协议,但是并没有定义子节点模型(如一个节点可以有几个子节点,没有子节点?一个?两个?或者更多?)。 它也没有定义坐标系统(如子节点定位是在笛卡尔坐标中还是极坐标?)和具体的布局协议(是通过宽高还是通过 constraint 和 size?,或者是否由父节点在子节点布局之前或之后设置子节点的大小和位置等)。为此,Flutter 提供了一个 RenderBox 类,它继承自 RenderObject,布局坐标系统采用笛卡尔坐标系,这和 Android 和 iOS 原生坐标系是一致的,都是屏幕的 top、left 是原点,然后分宽高两个轴,大多数情况下,我们直接使用 RenderBox 就可以了,除非遇到要自定义布局模型或坐标系统的情况。

五、BuildContext

  • BuildContext 是一个抽象类
  • 论是 StatelessWidget 和 StatefulWidget 的 build 方法都会传一个 BuildContext 对象
  • BuildContext 就是 Element 类
/// 以 StatelessElement 为例
class StatelessElement extends ComponentElement {
@override
Widget build() => widget.build(this);
}

六、Widget & State & Context

  • 在 Flutter 中,几乎所有东西都是 Widget。将一个 Widget 想象为一个可视化的组件(或与应用可视化方面交互的组件),当你需要构建与布局直接或间接相关的任何内容时,你正在使用 Widget。
  • Context:仅仅是已创建的所有 Widget 树结构中的某个 Widget 的位置引用。简而言之,将 context 作为 widget 树的一部分,其中 context 所对应的 widget 被添加到此树中。一个 context 只从属于一个 widget,它和 widget 一样是链接在一起的,并且会形成一个 context 树。
  • 定义了 StatefulWidget 实例的行为,它包含了用于” 交互 / 干预 “Widget 信息的行为和布局。应用于 State 的任何更改都会强制重建 Widget。

七、Widget 的 StatelessWidget 和 StatefulWidget

  • StatelessWidget: 一旦创建就不关心任何变化,在下次构建之前都不会改变。它们除了依赖于自身的配置信息(在父节点构建时提供)外不再依赖于任何其他信息。比如典型的 Text、Row、Column、Container 等,都是 StatelessWidget。它的生命周期相当简单:初始化、通过 build() 渲染。
  • StatefulWidget: 在生命周期内,该类 Widget 所持有的数据可能会发生变化,这样的数据被称为 State,这些拥有动态内部数据的 Widget 被称为 StatefulWidget。比如复选框、Button 等。State 会与 Context 相关联,并且此关联是永久性的,State 对象将永远不会改变其 Context,即使可以在树结构周围移动,也仍将与该 context 相关联。当 state 与 context 关联时,state 被视为已挂载。StatefulWidget 由两部分组成,在初始化时必须要在 createState() 时初始化一个与之相关的 State 对象。

八、StatefulWidget 的生命周期

Flutter 基本概念理解_Android_02

  • ​initState()​​:Widget 初始化当前 State,在当前方法中是不能获取到 Context 的,如想获取,可以试试 Future.delayed()。
  • ​didChangeDependencies()​​:在 initState() 后调用,State 对象依赖关系发生变化的时候也会调用。
  • ​deactivate()​​:当 State 被暂时从视图树中移除时会调用这个方法,页面切换时也会调用该方法,和 Android 里的 onPause 差不多。
  • ​dispose()​​:Widget 销毁时调用。
  • ​didUpdateWidget​​:Widget 状态发生变化的时候调用。

九、状态管理


Flutter 中的状态和前端 React 中的状态概念是一致的。React 框架的核心思想是组件化,应用由组件搭建而成,组件最重要的概念就是状态,状态是一个组件的 UI 数据模型,是组件渲染时的数据依据。 Flutter 的状态可以分为全局状态和局部状态两种。常用的状态管理有 ScopedModel、BLoC、Redux / FishRedux 和 Provider。

十、Flutter 的热重载


Flutter 的热重载是基于 JIT 编译模式的代码增量同步。由于 JIT 属于动态编译,能够将 Dart 代码编译成生成中间代码,让 Dart VM 在运行时解释执行,因此可以通过动态更新中间代码实现增量同步。 热重载的流程可以分为 5 步,包括:扫描工程改动、增量编译、推送更新、代码合并、Widget 重建。Flutter 在接收到代码变更后,并不会让 App 重新启动执行,而只会触发 Widget 树的重新绘制,因此可以保持改动前的状态,大大缩短了从代码修改到看到修改产生的变化之间所需要的时间。 另一方面,由于涉及到状态的保存与恢复,涉及状态兼容与状态初始化的场景,热重载是无法支持的,如改动前后 Widget 状态无法兼容、全局变量与静态属性的更改、main 方法里的更改、initState 方法里的更改、枚举和泛型的更改等。 可以发现,热重载提高了调试 UI 的效率,非常适合写界面样式这样需要反复查看修改效果的场景。但由于其状态保存的机制所限,热重载本身也有一些无法支持的边界。

十一、与Android 原生通信

Flutter 通过 PlatformChannel (非线程安全的)与原生进行交互
,其中 PlatformChannel 分为三种:

  • *BasicMessageChannel :用于传递字符串和半结构化的信息
  • MethodChannel :用于传递方法调用(method invocation)
  • *EventChannel : 用于数据流(event streams)的通信。

参考文章

  • 【flutter】flutter 核心原理总结​​地址​​
  • Flutter 学习笔记 - 面试题整理(一)​​地址​​