Flutter 架构概览

原文
这篇文章主要提供Flutter架构的一个概览,包括设计它的核心原则和理念。

Flutter是一个跨平台UI工具箱, 允许跨操作系统复用代码,例如ios, android。 目标是让程序员在不同平台上交付高性能的应用。尽可能共享的代码的同时拥抱变化。

在开发过程中,Flutter 应用运行在一个支持热加载的虚拟机上。 发布时,Flutter 应用直接编译成机器码,不管是 IntelX64 或者ARM指令集,还是JavaScript。 Flutter架构是开源的,且有一个蒸蒸日上的第三方库生态可提供核心功能。

该文章分为下面几个部分:

  1. 层模式:Flutter构造的片段

  2. 响应式用户接口:Flutter用户接口开发的核心理念

  3. Widgets的介绍:Flutter用户接口的基石

  4. 渲染过程:Flutter如何把UI代码编程像素

  5. 嵌入式平台的概览:让移动和桌面应用执行Flutter 应用

  6. Flutter集成其他代码:对Flutter应用可用的其他技术

  7. Web支持:关于浏览器环境中Flutter特性的总结

架构层

Flutter被设计成一个可扩展,层次清晰的系统。 它存在于一系列独立的库,每个库依赖于底层。 没有层有特权访问底下的层,且每层被设计为可选的和可替代的。

1.png

对于底层操作系统,Flutter应用像其他本地应用那样被打包成同一种方式。 一个特定的嵌入式平台提供一个入口;与底层系统协调,访问如渲染surface,输入和消息事件循环这样的服务。 嵌入式程序是用一种适合平台语言编写的。目前Android下用的是Java和C++, Ios用的是OC, macOs是oc++. Windows和linux是C++。Flutter 代码能够以一个模块集成到一个已经存在应用当中,也可以是作为整个应用的内容。

Flutter的核心是Flutter引擎, 主要用c++编写且首要支持所有的Flutter应用。 无论什么时候新的一帧需要被绘制,引擎负责栅格化合成场景。它提供底层Flutter核心API的实现。包括图形,文本布局, 文件和网络IO,访问控制,插件架构,Dart的运行时和编译链。

引擎通过dart:ui 暴露给Flutter框架,它用Dart类封装了底层C++代码。 该库公开底层的原语,例如用于驱动输入、图形和文本呈现子系统的类。

一般情况下,开发者和Flutter通过Flutter framework 交互。该framework提供了一个现代的,响应式,用Dart语言写的架构。包括一系列丰富的平台,布局,组件库,该Framwork由一系列层组成。从低到高,包括:

  1. 基础组件类,包括在底层基础上提供常用抽象的构建块服务,如动画、绘画和手势。

  2. 渲染层:为处理布局提供一个抽象层。 通过该层,你可以构建一个可渲染的对象树。 你可以动态操纵这些对象,且该树动态的更新布局来反映你的改变。

  3. Widgets层是一个组合的抽象层。每个渲染的对象有一个相应的类。 此外,widgets层允许你定义可复用的类的组合。该层引入了响应式编程。

  4. Material 和Cupertino 库提供了丰富的控制器集合。该集合使用widget层的组合原语来实现Material 和IOS设计语言。

Flutter 框架相对小。许多高层的特性被实现为包,包括平台插件,例如camera和webview, 和平台不可知的特征例如 characters, http和动画。 还有一些库来自于更广阔的生态,覆盖的服务像内置支付,Apple认证,和动画。

响应式用户接口

从表现上看, Flutter 是一个响应式的,假的声明式UI框架,开发者提供一个应用状态到接口状态的映射。 当应用状态改变, framework在运行时承担更新接口的任务。该模型的灵感来自于Facebook的RN框架和其他一些传统的设计原则。

在大多数传统UI框架,为了响应事件,用户接口初始状态声明一次后接着在运行时分开的被用户代码更新。 该方法的一项挑战是当应用逐渐变的复杂。 开发者需要意识到状态改变如何贯穿整个UI 。例如,考虑如下UI:

2.png

这里有很多地方状态可以被改变, color box, hue slider, radio 按钮。 当用户与UI交互时,改变必须在每个地方反应出来。 更糟糕的是,除非非常小心,一个小的改变可以造成波纹效应对于看起来无关的代码。

该问题的解决方案之一是MVC, 当你把数据改变推到model 通过controller。 Model通过控制器把新的状态推到view。然后,这也有问题,因为创建和更新是两个不同的步骤且很容易不同步。

Flutter和其他响应式框架,采取另一种方式解决该问题,通过清晰的把用户接口从它的底层状态中解耦。 通过响应式API, 有只创建UI声明, framework将会用该配置来同时创建和更新用户接口。

在Flutter, widgets 被用于配置对象树且不可改变的类代表。 这些widgets为layout管理一个独立的对象树。Flutter 核心上是一系列机制,该机制可以高效的遍历被改变的树的部分,并把树的对象转换成底层的树的对象,且在这些树之间传递变化。

一个widget通过覆盖build()函数来声明它的用户接口,这是一个传递状态给UI的函数。

UI = f(state)

Build()函数被设计成高速执行且没有副作用。允许它被framework随时的调用(可能是一帧渲染一次)

该方法依赖于特定的语言运行时特性(特别是,高速对象的实例化和删除),幸运的是,Dart特别适合该任务。

Widgets

如上所述,Flutter 强调widgets 是组合的单元。 Widgets 是Flutter 应用的用户接口的建筑模块。每个widget都是用户接口一部分不可变的声明

Widgets 给予组合形成一个层级。 每个widget嵌套在父widget中且能收到父widget的context. 这个结构一直延伸到根widget。如这个实验性例子所示:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('My Home Page')),
        body: Center(
          child: Builder(
            builder: (BuildContext context) {
              return Column(
                children: [
                  Text('Hello World'),
                  SizedBox(height: 20),
                  RaisedButton(
                    onPressed: () {
                      print('Click!');
                    },
                    child: Text('A button'),
                  ),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}

在之前代码中,所以实例化的类是widgets。

Apps 更新他们的用户接口来响应事件 通过告诉framework替换另一个widget. Framework 接着对比新旧widgets,且高效的更新用户接口。

Flutter 的每个控制器有它自己的实现, 而不是让系统提供。

这样实现有以下优点:

  1. 无限的延伸性。 开发者可以以任意方式创建一个Switch控制的变体,且不受限于系统提供的扩展点。

  2. 通过允许Flutter一次性组合整个屏幕,而不用来来回回的转换Flutter代码和平台代码.避免显著的性能瓶颈

  3. 把应用行为从操作系统的依赖中解耦出来。 应用看起来在所有系统上一样,即使操作系统改变了它控制器的实现。

组成

Widgets 通常组合其他小的,目的单一的widgets来结构生成强大的功能。

在可能的情况下,设计概念的数量保持最小,但允许总的词汇很大。例如, 在widgets层, Flutter用同一个关键概念(widget)来表示绘制到屏幕上,布局(位置和大小),用户交互,状态管理,主题,动画,和导航。 在动画层,一对概念,动画和Tweens,覆盖了多数设计空间。 在渲染层,RederObjects 是用于描述布局,会话,命中测试和可访问性。 在这每个例子当中,相应的词汇最终变大:这里有上百个widgets 和渲染objects, 和数十个动画和补间动画类型。

类层是故意很小但最大程度的去组合,专注于小的,可组合的widgets。每个widget只把一个做好。核心特性是抽象的,甚至基础特性例如padding和alignment 被实现成独立的组件而不是被编译到核心当中。(这也与传统api有了更鲜明的对比)。 例如,居中一个widget,而不是调整Align属性, 你把它包裹在一个Center widget中。

这里有针对padding, alignment, rows, columns, grids的组件。 这些布局组件本身不包含一个视觉表现。相反, 他们唯一的目的是控制另一个widget布局的某些方面。

例如:Container,一个通用的widget,由几个widget组成,负责布局,绘制,定位。 特别情况下,Container由LimitedBox,ConstrainedBox, Align, Padding , DecoratedBox, Transform widget组成,你可以通过读它的源码来了解。

构建 widget

如之前所述, 通过重载build()函数来返回一个元素树。 这个树以一种更具体的方式代表 widget部分的用户接口。例如, 一个toolbar Widget 可能 有一个build 函数返回一个横向的带一些text 和不同的Button的布局。 按需, framework 递归的要求每个widget去Build 直到该树完全被具体的可渲染的对象描述。 Framework接着把这些对象封成一个可渲染的对象树。

一个widget的build函数应该没有副作用。 不管函数什么时候被调用,widget应该返回一个新的widgets树。不管先前返回什么widget。基于对象树, Framework通过繁重的工作来确定那个构建函数被编译。

在每一被渲染的帧, Flutter 可以通过调用build()函数重建部分状态被改的UI。 因此build函数快速返回很重要, 繁重的计算工作应该以异步的方式完成, 且通过build 函数作为部分状态存起来。

虽然该方法相对幼稚,但是这种自动对比的方法非常有效,能做出高性能,可响应的应用。并且,这种设计简化了你的代码通过关注声明一个widget怎么构成,而不是更新用户接口状态的复杂性。

Widget state

Framework 引入了两类主要的widget:有状态的和无状态的widget

.很多widget没有可变的状态,他们没有任何属性随着时间变化。

然而,如果一个widget的唯一特性是需要根据用户行为和其他因素来改变, 该widget是有状态的。 例如:如果一个widget有一个计数器,用户点击的时候该计数器值增加。 Widget需要重新编译它的部分UI。 这些widget的子类,Statefulwidget, 他们在State类中存贮可变的状态。 StatefulWidget 没有build函数,相反,他们的用户接口通过他们的State对象被构建。

无论你什么时候改变一个State对象,你必须调用setState()去发信号给framework来更新用户接口,通过调用State的Build函数。

有独立的state和widget对象让其他widget对待有状态的和无状态的widget以完全一样的方式,而不用考虑丢失状态。父widget可以在任何时候创建子widget的新实例,而不需要保留子widget以保存其状态,frameowrk 在合适的时候完成这所有的工作,寻找和重新使用存在的state

State management

那么,如果很多widget能够保存状态, 状态如何保存、管理并传递到整个系统?

和其他类一样,你能够使用widget中的构造函数去初始化它的数据,这样build()函数能够保证任何子widget带它需要的数据被实例化

@override

Widget build(BuildContext  context)  {

  return  ContentWidget(importantState);

}

当widget 树变的更深,然而,把状态信息沿着树的层级上上下下的传递变的笨重,因此一个第三方的widget类型, InheritedWidget, 提供一种容易的方式去从一个共享的祖先拿取数据。你可以使用InheritedWidget 去创建一个在widget树种包裹一通用的祖先的state widget,如下例子所示。

无论何时 ExamWidget 或者GradeWidget 对象需要从StudentState 获取数据,他们现在可以通过一个命令来访问:

final studentState = StudentState.of(context);

of(Context) 调用可以 获得build context, 且返回最近的符合StudentState的祖先。InheritedWidget 也提供一个 updateShouldNotify() 函数, Flutter 调用来决定是否状态改变应该触发使用它的子widget的rebuild

Flutter 本身为了共享状态广泛的使用InheriteWidget 作为framework的一部分,例如应用可见的主题,包含像color, style这样的属性。 MaterialAppbuild() 函数在树中插入了一个主题当它编译的视图,接着在更深的层级的widget可以用.of()函数来 查找 相关的主题数据,例如:

Container(

 color: Theme.of(context).secondaryHeaderColor,

 child: Text(

  'Text with a background color',

 style: Theme.of(context).textTheme.headline6,

  ),

);

这个方法也用于 Navigator, 提供页面路由。MediaQuery, 用于提供访问屏幕的亮度,尺寸和方向。

随着应用不断增大,更多状态管理方法减少有状态的widgets使用变得更有吸引力。 很多Flutter应用使用实用程序包例如Provider,该Provider 提供InheritedWidget的封装。Flutter’s 分层的架构也使替代实现UI状态的转移的方法成为可能,例如flutter_hooks

Rendering and layout

这章节描述渲染流水线,该管道是一系列Flutter 来转化widget到实际绘制到屏幕上的像素的步骤。

Flutter’s rendering model

你或许会怀疑:如果Flutter 是一个跨平台的framework, 它怎么能够提供与单一平台可比的性能?

思考传统Android 应用工作是有用的。 当绘制时, 你先调用Android framework的Java 代码。 Android系统提供负责把他们自己绘制到Canvas对象的组件, 接着Android能够使用Skia 渲染。 Skia是一个用C/C++写的图形引擎,调用CPU或者GPU来完成绘制到设备上。

跨平台框架 一般通过在本地Andrid Ui库和IOS UI库创建一个抽象层, 意图消除每个平台表现上的不一致。应用代码常被写成一种类似javaScript的解释性语言, 且必须反过来与基于Java的android和基于OC的ios来展示UI。 所增加的这些能够明显的增加开销,特别是在Ui和应用逻辑有很多交互的地方。

相反,Flutter 最小化这些抽象层, 绕过系统UI widget库,支持自己的widget集合。绘制Flutter的试图的Dart代码被编译成本地代码,用Skia来渲染。 Flutter 也嵌入他自己的Skia的拷贝作为部分引擎,允许开发者升级他们的应用程序,以保持最新的性能改进,即使手机没有更新到新的安卓版本。对于其他本地平台,如iOS、Windows或macOS, Flutter也是如此。

From user input to the GPU

Flutter应用到它的渲染流水线的重载原则是简单和快速的。Flutter有一个直接的流水线,该流水线用于支持数据怎么流向系统,如以下序列图所示:


3.png

让我们用更具体的细节看这些阶段。

Container(

 color: Colors.blue,

 child: Row(

 children: [

  Image.network('https://www.example.com/1.png'),

  Text('A'),

  ],

  ),

);

当Flutter需要渲染以上代码片段, 它调用build() 函数,该函数返回基于当前app状态渲染UI的一个子widget树。在这个过程当中,当需要的时候,build()函数能够基于它的状态引入新的widgets。 作为一个简单的例子,在之前代码片段中, Container有color和child属性。通过看Container的源码,你可以看到color不为Null时, 它被插入到一个ColoredBox中代表Color:

if  (color  !=  null)

  current  =  ColoredBox(color: color, child: current);

相应的,在这个过程中Image和Text widget 可能插入子widget例如RawImage和RichText。 最终的widget层级可能因此比代码代表的更深,如下例子:


4.png

这解释了当你通过debug tool,例如Flutter inspector和部分Dart DevTools 检查 树时,你可能看见一个相当深的结构,对比你的源码。

在build阶段,Flutter 把widget翻译成一个元素树, 每个widget一个元素。 每个元素代表一个特定的widget实例。 这里两种基本类型:

组件元素,其他元素的宿主

渲染对象元素,一种加入到布局或者绘制阶段的元素


5.png

RenderObjectElements是它们的widget模拟物和底层RenderObject之间的中介,我们稍后会讲到。

任何widget的元素能够通过它的BuildContext被引用。这是诸如Theme.of(context)等函数调用中的context,并作为参数提供给build()方法

由于 widget是不可改变的,包括节点间的parent/child关系。任何widget树的改变,例如把Test(‘A’) 变成Text(‘b’) ,造成一个新的widget集合被返回。 但这不意味着下层的表现被重现编译。 元素树在每一帧之间是持久的, 因此扮演了一个很重要的性能角色, 允许Flutter表现的像widget层级是完全可处置的当缓存它的底层表现。仅仅通过遍历改变了的widget, Flutter能够只重新编译部分需要重新配置的部分元素树。

Layout and rendering

只有单一的widget的应用是非常少的。 UI framework中很重要的一部分是高效的部署widgets的层级,决定每个元素的大小和位置在他们被渲染到屏幕之前。

在渲染树中的每个节点的基类是RenderObject, 该类提供了布局和渲染的抽象层。 这是非常普遍的,它不承诺一个固定的大小 。 每个RenderObject 知道它的父类,但是对于孩子节点知道很少,除了如何访问他们和他们的约束。这给RenderObject提供了足够的抽象,能够处理各种案例。

在Build阶段,Flutter 创建或者更新一个继承了RenderObject的对象。 RenderObject是原语:RenderParagraph 渲染text, RenderImage渲染一个图片, RenderTransorm在绘制它的孩子之前应用一个transformation

6.png

多数Flutter widget 通过一个继承RenderBox子类的对象来被渲染, 代表一个RenderObject的固定大小。 RenderBox提供一个box约束模型的基础,为每个要被渲染的widget 建立一个最小的和最大的宽高。

进行布局时,Flutter 以深度优先遍历渲染树,且把大小约束传下去。 为了决定它的大小,孩子必须遵守被它父亲所给的约束。孩子传给它的父亲一个大小。该大小在父亲创建的约束下。


7.png

在穿过这颗树的最后,每个对象有一个限定的大小,且准备通过调用patin()函数被渲染。

作为布局手段,Box约束模型是非常强的,时间复杂度为o(n)

父亲能够指示孩子的大小通过设置最大和最小约束为相同值。

父亲能够指示孩子的宽度,但是在高度上给孩子灵活性。反之亦然。 一个例子是流文本,有一个固定的横约束,但是根据文本的数量不同,高度不同。

该模型即使当一个子对象需要知道有多少可用空间来决定它将渲染它的内容也能生效。通过用LayoutBuilder widget, 该子对象能够检查传下去的约束且用这些约束来决定它将怎么用他们。例如:

Widget build(BuildContext  context)  {

  return  LayoutBuilder(

 builder: (context,  constraints)  {

  if  (constraints.maxWidth  < 600)  {

  return  OneColumnLayout();

  }  else  {

  return  TwoColumnLayout();

  }

  },

  );

所有RenderObjects的根是RenderView, 代表整个渲染树的输出。当平台要求新的一帧被渲染(例如,由于垂直同步信号) compositeFrame()被调用。 这将创建一个SceneBuilder 去触发界面的更新。 当该场景完成, RenderView 对象 传递组合的 scene到Window.redner()函数, 并把控制权交给GPU

Platform embedding

如我们已经见到的,与被转化成相同的OS widgets相反,Flutter 用户接口被创建,布局,组合, 并被Flutter自己绘制。 为了获得textture和加入到底层操作系统的应用的生命周期, 该机制不可避免有所不同由于该平台的独特关注点。 引擎是与平台无关的,且提供了一个稳定的API来给安装和使用Flutter提供一个平台嵌入器。

平台嵌入器是一个操作系统应用, 该应用持有所有的Flutter 内容, 在宿主操作系统和Flutter之间像胶水一个工作。 当你启动一个Flutter应用, 嵌入式器 提供入库点,初始化Flutter引擎, 为UI和栅格化获取线程, 且创建一个textture让Flutter能够写入数据。该嵌入式器也对整个应用的声明周期负责, 包括输入手势(鼠标,键盘,点击),屏幕大小,线程管理, 和平台消息。Flutter包括的平台有Android,Ios,Windows,Macos和Linux。

每个平台有它自己API集合和约束, 一些平台特性的简要说明:

在ios和macOS中,Flutter 把嵌入式器分别作为一个UIViewController 或者NSViewController。平台嵌入式器创建一个FlutterEngine, 作为DartVm的宿主和你的Flutter运行时, FlutterViewController附属到FlutterEngine来传递UIKIt和Cocoa 输入时间到Flutter且来展示被渲染的帧,通过调用Metal或者OpenGl的FlutterEngine

在Android中,Flutter 默认情况下以一个Activity装载到嵌入式器。 这个view被FlutterView控制, 它依靠Flutter内容的组合以及z-ordering要求,将以view或者texture的形式渲染Flutter的内容

在Windows, Flutter 持有一个传统 Win32 应用。 内容用ANGLE渲染,一个将OpenGl API 调用转换成DirectX11 等价物的库. 目前正在努力提供一个使用UWP应用模型的Windows嵌入器,以及通过DirectX 12将角度替换为更直接的GPU路径。

Integrating with other code

Flutter提供各种互操作机制,不管是你访问代码或者用kotlin,Swift写的API,调用基于C的API, 在Flutter app中嵌入本地控制器,或者把Flutter嵌入到一个已经存在的应用当中。

Platform channels

对于移动和桌面程序,Flutter 允许你调用自定义代码通过平台Channel, 这是一种很方便的Dart Code和平台代码沟通的方式。通过创建一个公共平台,你能在Dart和平台组件间发送和接收消息。 数据被序列化成Dart 类型,像amp 。 且被反序列化成HaspMap(kotlin), Dictonary(Swift)

如下是一个简单的平台channel例子

// Dart side

const  channel  =  MethodChannel('foo');

final  String  greeting  =  await  channel.invokeMethod('bar',  'world');

print(greeting);

*content_copy*

// Android (Kotlin)

val channel = MethodChannel(flutterView,  "foo")

channel.setMethodCallHandler {  call,  result  ->

  when  (call.method)  {

  "bar"  ->  result.success("Hello, ${call.arguments}")

  else  ->  result.notImplemented()

  }

}

*content_copy*

// iOS (Swift)

let channel =  FlutterMethodChannel(name:  "foo", binaryMessenger:  flutterView)

channel.setMethodCallHandler  {

  (call:  FlutterMethodCall, result:  FlutterResult)  ->  Void  in

  switch  (call.method)  {

  case  "bar": result("Hello, \(call.arguments  as!  String)")

  default: result(FlutterMethodNotImplemented)

  }

}

Foreign Function Interface

对于基于C的API,包括这些能够生成代码的且用现代语言编写的如Rust或者GO, Dart通过使用dart:ffi 库来 提供一种直接的机制绑定到本地代码。

FFI 模型比平台channel要快很多。因为数据传输不需要序列化。 相反,Dart运行时提供在堆上分配内存的能力。FFI除了Web外其他平台都支持。

为了使用FFI,你可以创建一个typedef 对于每个Dart 和没有管理的函数签名。指示虚拟机在他们之间映射。作为一个简单的例子,这儿有一些碎片代码调用传统的Win32 MessageBox()

typedef  MessageBoxNative  =  Int32  Function(

  IntPtr  hWnd,  Pointer<Utf16>  lpText,  Pointer<Utf16>  lpCaption,  Int32  uType);

typedef  MessageBoxDart  =  int  Function(

  int  hWnd,  Pointer<Utf16>  lpText,  Pointer<Utf16>  lpCaption,  int  uType);

final  user32  =  DynamicLibrary.open('user32.dll');

final  MessageBox  =

  user32.lookupFunction<MessageBoxNative,  MessageBoxDart>('MessageBoxW');

final  result  =  MessageBox(

 0,  // No owner window

  Utf16.toUtf16('Test message'),  // Message

  Utf16.toUtf16('Window caption'),  // Window title

 0 // OK button only

  );

在Flutter app中渲染本地代码控制器。

因为Flutter的内容被渲染到texture 且它的widget树完全是内部的,在Flutter的内部模型中不存在类似Android视图的东西,也不存在于Flutter widgets的交错渲染中。对于那些想要在他们的Flutter应用中包括现有平台组件(如浏览器控件)的开发者来说,这是一个问题。

Flutter 通过引入platform view widgets(AndriodView 和UiKitView)来解决这个问题。 让你在每个平台嵌入这种类型的内容。 平台view 能够被其他Flutter内容集成。每个widget对于底层操作系统的行为像一个中介。例如: 在Android,AndroidView 服务于三个重要功能。

  1. 每次帧被绘制, AndroidView会拷贝一份图形texture 且提供给Flutter 组合成Flutter渲染的Surface

  2. 响应命中测试和输入手势,并将它们翻译成等效的本地输入。

  3. 创建可访问性树的模拟,并在本机层和颤振层之间传递命令和响应。

不可避免的,这种方式有些性能损耗。 因此一般情况下, 该方法最适用于复杂的控制器,像Google map。 这种用Flutter实现是不切实际的。

通常, 基于平台测试,一个Flutter应用实例化这些widget 在build()函数中。如下:


if  (defaultTargetPlatform  ==  TargetPlatform.android)  {

  return  AndroidView(

 viewType: 'plugins.flutter.io/google_maps',

 onPlatformViewCreated: onPlatformViewCreated,

 gestureRecognizers: gestureRecognizers,

 creationParams: creationParams,

 creationParamsCodec: const  StandardMessageCodec(),

  );

  }  else  if  (defaultTargetPlatform  ==  TargetPlatform.iOS)  {

  return  UiKitView(

 viewType: 'plugins.flutter.io/google_maps',

 onPlatformViewCreated: onPlatformViewCreated,

 gestureRecognizers: gestureRecognizers,

 creationParams: creationParams,

 creationParamsCodec: const  StandardMessageCodec(),

  );

  }

  return  Text(

  '$defaultTargetPlatform is not yet supported by the maps plugin');

  }

基于Androidview和UikitView和本地代码交流 一般情况使用平台platform channel机制。

目前,platform view 对于桌面平台是不可用的, 但这不是架构的限制,后续可能会加入。

Hosting Flutter content in a parent app

与之前场景相反是把Flutter嵌入到一个Android或者IOS应用当中,如之前所描述的那样,一个运行在移动设备上的新创建的Flutter应用是被一个Android activity 或者IOS UIViewController 持有的。Flutter 内容能够被嵌入到一个已经存在的Android或者IOS应用中通过使用相同的API。

Flutter 模块模板被设计的很容易嵌入, 你可以把它作为原始依赖嵌入到已经存在的Gradle或者Xcode 构建定义, 或者你可以把它编译成Android Archive 或者 iOS Framework binary ,并且使用时不需要每个开发者必须安装Flutter

Flutter 引擎需要很短的时间来初始化, 因为它需要装载Flutter共享库, 初始化Dart运行时,独立的创建和运行一个Dart, 并把一个rederning surface 附属到UI。 为了最小化延迟, 最好在初始化应用时初始化, 或者在第一个Flutter 界面前初始化,此外, 分离Flutter引擎允许它被多个Flutter界面重用并共享内存加载必须内存库的开销。

Flutter web support

当一般的架构理念应用到所有Flutter支持的平台,这儿有一些Web flutter应用独有的特性值得一聊。

只要JavaScripit 语言存在, Dart一直被编译成JavaScrpit。 许多重要的应用从Dart编译成JavaScript并已经运行的产品。包括 advertiser tooling for Google Ads。 因为Flutter框架是用Dart写的, 编译成JavaScript是相对直接的。

然而,Flutter 引擎是用C++写的, 被设计成与底层操作系统而不是web浏览器交互。 因此一种不同的方法是被需要的。 Web应用中,Flutter 提供一种引擎上的重新实现,在浏览器的API标准之上。我们目前有两个选择在web上渲染Flutter内容:HTML 和 WebGL。 在Html模式中,Flutter 使用HTML,Css , Canvas 和SVG。为了渲染WebGl, Flutter使用被编程成WebAssembly的Skia版本,叫做CanvasKit。 hTML模式提供最好的代码大小特性,CanvasKit给浏览器图形栈提供最快的路径且提供某种程度上更高的图形保证性。

Web版本的架构图如下:

8.png

对比其他平台,也许最值得注意的是Flutter不需要提供Dart运行时。相反,Flutter框架和其他一些你写的代码被编译成JavaScript。 注意:这里有很少语义上的差异在所有的模式下(JIT对比AOT, 本地对比web编译)大多数开发人员永远不会编写出一行出现这种差异的代码。

在开发时,Flutter 应用使用Dartdevc,支持持续集成编译且因此允许热加载。 相反,当你准备为web创建一个应用时,dart2Js, Dart高优化的产品JavScript编译器被使用,打包Flutter 核心和framework以及你的应用 为一个变小的源文件,能被部署到任何web服务器。 代码能被用一个文件或者分成多个文件通过 deferred imports

Further information

对于那些有兴趣了解更多关于Flutter内部的信息的人,Flutter白皮书提供了一个有用的指导框架的设计哲学。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容

  • Flutter是开源并且免费的,拥有现代的响应式框架特性,高速的2D渲染引擎,方便快捷的开发工具以及各种开箱即用的...
    luehning阅读 544评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,458评论 16 22
  • 创业是很多人的梦想,多少人为了理想和不甘选择了创业来实现自我价值,我就是其中一个。 创业后,我由女人变成了超人,什...
    亦宝宝阅读 1,796评论 4 1
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,543评论 0 11
  • 可爱进取,孤独成精。努力飞翔,天堂翱翔。战争美好,孤独进取。胆大飞翔,成就辉煌。努力进取,遥望,和谐家园。可爱游走...
    赵原野阅读 2,711评论 1 1