Flutter 架构原理

Flutter 一个跨平台的UI构建工具,可以使用一套代码搭建Andriod,IOS,和 desktop 等不同的应用,达到一次编写到处运行。

Flutter 架构图

embedder

embedder(嵌入器)i是和底层的操作系统进行交互的部分。因为flutter最终要将程序打包到对应的平台中,所以这个嵌入器需要和底层的平台接口进行交互。

flutter 打包程序,可以作为整个应用程序,也可以作为现有程序的一部分被嵌入使用。

engine

flutter中最核心的部分。

其存在是为了支持Dart Framework 的运行。它提供了Flutter的核心API,包括作图、文件、操作系统、网络IO、dart 运行时环境等核心功能。

engine 主要是通过dart:ui 暴露给Flutter framework 层的。

Flutter framework

用户编程的接口。我们的应用程序需要和Flutter framework进行交互,最终构建出一个应用程序。

Flutter framework 主要是使用dart语言编写的。

有最基础的foundational 包,和构建在其上的animation、painting、gestures。

再上面就是rendering层,rendering为我们提供了动态构建可渲染对象树的方法,通过这些方法,我们可以对布局进行处理。

接着是widgets layer,它是rendering层中对象的组合,表示一个小挂件。

最后是Material和Cupertino库,这些库使用widgets层中提供的小部件,组合成不同风格的控件集。

Widgets

小插件

子widgets可以共享父widgets的上下文环境。

缺点:代码层级结构特别多。

Widgets的可扩展性

不依赖于操作系统提供的接口。

好处:一切都是由Flutter自己控制,使用者可以在Flutter的基础上进行无限扩展,而不用受限于系统底层的实现限制。

另一方面,这样可以减少Flutter在呈现过程中在Flutter代码和平台代码之间来回切换,减少了性能瓶颈,提升效率。

最后,因为UI的实现和底层的操作系统是分离的,所以Flutter的APP在不同的平台上可以有统一的外观和实现,可以保证风格的统一。

Widgets 的状态管理

Widgets 表示的是不可变的用户UI界面结构。虽然结构是不能变化的,但是Widgets里的状态是可以动态变化的。

根据Widgets中是否包含状态,Widgets可以分为stateful和stateless widget对应的类是StatefulWidget和StatelessWidget。

  • 对于有些Widgets来说,比如icon或者Label,它里面本身就不需要状态,这些Widgets就是StatelessWidget。
  • 但是如果有些Widgets中的某些内容可能需要根据用户或者其他原因来动态变化·,则就需要使用StatefulWidget。
    之前提到Widgets是不可变的,StatefulWidget中的可变数据是存放在对应的State中的,所以StatefulWidgets本身并没有build方法,所有用户界面都是通过State对象来构建的。
    当State 发生变化时,需要调用setState()方法来通知flutter框架来调用State的build方法,从而将变化反馈到用户界面中。

StatefulWidget是带有状态的,那这些状态是如何管理和传递的?

  1. State本身提供了一个build方法,用于构建初始的状态:

    Widget build(BuildContext context);

如果在一个StatefulWidget中需要嵌入另一个StatefulWidget,那么可以在其对应的State中调用另外一个StatefulWidget的构造函数,将要传递的数据,以构造函数参数的形式传递给子Widget。

但是如果嵌套层级过多的话,这种构造函数的传递方式,显然不能满足我们的要求。

于是Flutter提供了也给InheriedWidget类,如果我们自定义的类需要共享数据给子Widgets,则可以继承InheritedWidget。

InheritedWidget 作用:

  1. 子Widget可以通过Inherited widgets提供的静态of方法拿到离它最近的父Inherited widgets实例。
  2. 当Inherited widgets改变state之后,会自动触发state消费者的rebuild行为。
    Inherited widgets类的定义:
abstract class InheritedWidget extends ProxyWidget {

  const InheritedWidget({ Key? key, required Widget child })
    : super(key: key, child: child);

  @override
  InheritedElement createElement() => InheritedElement(this);

  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

InheritedWidget是对实际Widget对象的代理,还将InheritedWidget封装到了InheritedElement中。(InheritedElement是底层通知机制的实现)

InheritedElement 还添加了一个updateShouldNotify,控制当前InheritedWidget rebuild的时候,是否需要rebuild继承它的子Widget。

栗子🌰:

class FrogColor extends InheritedWidget {
  const FrogColor({
    Key? key,
    required this.color,
    required Widget child,
  }) : super(key: key, child: child);

  final Color color;

  static FrogColor of(BuildContext context) {
    final FrogColor? result = context.dependOnInheritedWidgetOfExactType<FrogColor>();
    assert(result != null, 'No FrogColor found in context');
    return result!;
  }

  @override
  bool updateShouldNotify(FrogColor old) => color != old.color;
}
  1. FrogColor中定义了一个Color属性,当Color发生变化的时候,就会调用updateShouldNotify
  2. of 方法,接受的参数是BuildContext,然后调用context.dependOnInheritWidgetOf 去查找离该context最近的FrogColor。
  3. 为什么要使用of方法对context.dependOnInheritedWidgetOfExactType进行封装?
  • 因为context.dependOnInheritedWidgetOfExactType方法不一定能够找到要找到的对象,所以我们需要进行一些异常值的处理。
  • 有可能of方法返回的对象和context.dependOnInheritedWidgetOfExactType中查找的对象不一样,这都是可以的。

of 方法的具体使用:

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FrogColor(
        color: Colors.green,
        child: Builder(
          builder: (BuildContext innerContext) {
            return Text(
              'Hello Frog',
              style: TextStyle(color: FrogColor.of(innerContext).color),
            );
          },
        ),
      ),
    );
  }
}

还有个问题,of方法传入的是BuildContext对象(必须是InheritedWidget对象本身的后辈,也就是说在对象树中,必须是InheritedWidget 的子树)。

除了InheritedWidget,Flutter还提供了许多状态管理的工具,比如provider、bloc、flutter_hooks等。

渲染和布局

渲染就是将上面我们提到的widgets转换成用户肉眼可以感知的像素的过程。

Flutter作为一种跨平台的框架,和普通的跨平台框架或原生框架的区别:

  • 原生框架:
  • andriod:调用的是andriod框架的java代码,通过调用andriod系统库提供的进行绘制的组件,最后调用底层的Skia来进行绘制。Skia是一种用C/C++编写的图形引擎,它调用CPU或GPU在设备上完成绘制。
  • 常见的跨平台框架是如何运行的?
  • 在原生的代码框架上又封装了一层。通常使用JavaScript这样的解释性语言进行编写,然后编写的代码再和andriod的Java或IOS的Objective-C系统库进行交互。这样的结果就是在UI交互或调用之间会造成显著的性能开销。这也就是通用的跨平台语言不如原生的性能好的原因。
  • 但是flutter不一样,它并不是用系统自带的UI控件,而是拥有自己的实现。Flutter代码会直接被编译成使用Skia进行渲染的原生代码,从而提升渲染效率。

flutter的渲染流程:

  1. 调用build方法,生成一个widget集合(Container这个widget是由很多个其它的widget组成的,所以,Container会生成widget树。)
  2. 在build的过程中,会被转换为element tree。一个element和一个widget对应。
    element表示的是widget的实例,flutter中有两种类型的element,分别是:ComponentElement和RenderObjectElement。ComponentElement是其他Element的容器,而RenderObjectElement是真正参与layout和渲染的element。

因为 Widget本身是不可变的,所以任何对于Widget的修改都会返回一个新的Widget。那么是不是所有的变动,都会导致整个element tree重新渲染呢?

答:不会,flutter仅会重新渲染需要被重新绘制的element。

渲染树🌲如何构建的?

渲染树中的每个元素叫做RenderObject,它定义了布局和绘制的抽象模型。

RenderObjectElement 会在渲染的时候转换成为RenderObject。

不同的Render element会转换成不同的Render对象。

相关推荐
江上清风山间明月14 小时前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能1 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人1 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen1 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang1 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang1 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1231 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力2 天前
Flutter应用开发:对象存储管理图片
flutter