前言
本文總結(jié)Flutter架構(gòu)概覽,包含其設(shè)計層面的核心原則以及概念。
Flutter是一個跨平臺的UI工具集,它允許在各種操作系統(tǒng)上復(fù)用相同的代碼,同時應(yīng)用程序直接與底層平臺交互,避免了不同平臺視圖的差異,同時也讓開發(fā)者能夠在不同平臺上都能交付擁有原生體驗的高性能應(yīng)用。
開發(fā)階段,F(xiàn)Lutter應(yīng)用會在一個VM(程序虛擬機)中運行,從而可以保留狀態(tài)且無需重新編譯的情況下,熱重載相關(guān)的更新。對于發(fā)行版(release),F(xiàn)lutter程序會直接編譯錯機器碼,或者針對Web平臺的JavaScript。
概覽分為以下幾個部分:
- 分層模型:Flutter的構(gòu)成要素
- 響應(yīng)式用戶界面:Flutter用戶界面開發(fā)的核心概念
- widgets介紹:構(gòu)建Flutter用戶界面的基石
- 渲染過程:Flutter如何將界面布局轉(zhuǎn)化為像素
- 平臺嵌入層的概念:讓Flutter應(yīng)用可以再移動端以及桌面端操作系統(tǒng)執(zhí)行的代碼
架構(gòu)層
Flutter被設(shè)計為一個可擴展的分層系統(tǒng)。它可以被看做是各個獨立的組件系列合集,上層的組件各自依賴下層的組件。組件無法越權(quán)訪問底層的內(nèi)容,并且框架層的各個部分都是可選且可替代。

對于底層操作系統(tǒng)而言,F(xiàn)lutter應(yīng)用程序的包裝方式與其他原生應(yīng)用相同。在每一個平臺上,都回去包含一個特定的嵌入層,從而提供一個程序入口,程序由此可以與底層操作系統(tǒng)進(jìn)行協(xié)調(diào),訪問諸如Surface渲染,輔助功能和輸入等等服務(wù),并且管理時間循環(huán)隊列。該嵌入層采用了適合當(dāng)前平臺語言編寫,例如Android使用的是Java/C++,IOS和MacOSSierra使用的是OC和OC++,Windows和Linux使用的是C++,F(xiàn)lutter代碼可以通過嵌入層,以模塊方式集成到現(xiàn)有的應(yīng)用中,也可以作為應(yīng)用的主體。Flutter本身包含了各個常見平臺的嵌入層,同時也存在一些其他的嵌入層。
Flutter引擎毫無疑問是Flutter的核心,它主要是C++編寫,并提供了Flutter應(yīng)用所需要的原語。當(dāng)需要繪制新的一幀的內(nèi)容時,引擎將負(fù)責(zé)對需要合成的場景進(jìn)行柵格化。它提供了Flutter核心API的底層實現(xiàn),包括圖形(通過Skia)、文本布局、文件以及網(wǎng)絡(luò)IO、輔助功能支持、插件架構(gòu)和Dart運行環(huán)境以及編譯環(huán)境的工具鏈。
引擎將C++ 代碼包裝成Dart代碼,通過dart:ui暴露給Flutter框架層。該庫暴露了最底層的原語,包括用于驅(qū)動圖形輸入、圖形、和文本渲染的子系統(tǒng)的類。
通常,開發(fā)者可以通過Flutter Framework與Flutter進(jìn)行交互,該Framework提供了以Dart語音編寫的現(xiàn)代響應(yīng)式框架。它包括由一系列層組成的一組豐富的平臺,布局和基礎(chǔ)庫。從下層到上層,依次有:
- 基礎(chǔ)的 foundational 類及一些基層之上的構(gòu)建塊服務(wù),如 animation、 painting 和 gestures,它們可以提供上層常用的抽象。
- 渲染層 用于提供操作布局的抽象。有了渲染層,你可以構(gòu)建一棵可渲染對象的樹。在你動態(tài)更新這些對象時,渲染樹也會自動根據(jù)你的變更來更新布局。
- widget 層 是一種組合的抽象。每一個渲染層中的渲染對象,都在 widgets 層中有一個對應(yīng)的類。此外,widgets 層讓你可以自由組合你需要復(fù)用的各種類。響應(yīng)式編程模型就在該層級中被引入。
- Material 和 Cupertino 庫提供了全面的 widgets 層的原語組合,這套組合分別實現(xiàn)了 Material 和 iOS 設(shè)計規(guī)范。
Flutter 框架相對較小,因為一些開發(fā)者可能會使用到的更高層級的功能已經(jīng)被拆分到不同的軟件包中,使用 Dart 和 Flutter 的核心庫實現(xiàn),其中包括平臺插件,例如 camera 和 webview;與平臺無關(guān)的功能,例如 characters、 http 和 animations。還有一些軟件包來自于更為寬泛的生態(tài)系統(tǒng)中,例如 應(yīng)用內(nèi)支付、 Apple 認(rèn)證 和 Lottie 動畫。
該概覽的其余部分將從 UI 開發(fā)的響應(yīng)式范例開始,瀏覽各個構(gòu)建層。而后,我們會講述 widgets 如何被組織,并轉(zhuǎn)換成應(yīng)用程序的渲染對象。同時我們也會講述 Flutter 如何在平臺層面與其他代碼進(jìn)行交互,最終,我們會對目前 Flutter 對于 Web 平臺的支持與其他平臺的異同做一個總結(jié)。
響應(yīng)式用戶界面
Flutter 是一個響應(yīng)式的且偽聲明式的 UI 框架,開發(fā)者負(fù)責(zé)提供應(yīng)用狀態(tài)與界面狀態(tài)之間的映射,框架則在運行時將應(yīng)用狀態(tài)的更改更新到界面上。在大部分傳統(tǒng)的 UI 框架中,界面的初始狀態(tài)通常會被一次性定義,然后,在運行時根據(jù)用戶代碼分別響應(yīng)事件進(jìn)行更新。
Flutter 與其他響應(yīng)式框架類似,采用了顯式剝離基礎(chǔ)狀態(tài)和用戶界面的方式,來解決這一問題。你可以通過 React 風(fēng)格的 API,創(chuàng)建 UI 的描述,讓框架負(fù)責(zé)通過配置優(yōu)雅地創(chuàng)建和更新用戶界面。
在 Flutter 里,widgets(類似于 React 中的組件)是用來配置對象樹的不可變類。這些 widgets 會管理單獨的布局對象樹,接著參與管理合成的布局對象樹。 Flutter 的核心就是一套高效的遍歷樹的變動的機制,它會將對象樹轉(zhuǎn)換為更底層的對象樹,并在樹與樹之間傳遞更改。
build() 是將狀態(tài)轉(zhuǎn)化為 UI 的方法,widget 通過重寫該方法來聲明 UI 的構(gòu)造。build() 方法在框架需要時都可以被調(diào)用(每個渲染幀可能會調(diào)用一次),從設(shè)計角度來看,它應(yīng)當(dāng)能夠快速執(zhí)行且沒有額外影響的。這樣的實現(xiàn)設(shè)計依賴于語言的運行時特征(特別是對象的快速實例化和清除)。幸運的是,Dart 非常適合這份工作。
Widgets
應(yīng)用額如前所述,F(xiàn)Lutter強調(diào)以widgets作為組成單位。Widgets是構(gòu)建Flutter應(yīng)用界面的基礎(chǔ)塊,每個widget都是一部分不可變的UI聲明。
Widgets通過布局組合形成一種層次結(jié)構(gòu)關(guān)系。每個Widget都是嵌套在其父級的內(nèi)部,并可以通過父級接收上下文。從根布局(托管Flutter應(yīng)用的容器,通常是MaterialApp或者CupertinoApp)開始,自上而下就是這樣的結(jié)構(gòu),如下面實例;
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('My Home Page'),
),
body: Center(
child: Builder(
builder: (BuildContext context) {
return Column(
children: [
const Text('Hello World'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
print('Click!');
},
child: const Text('A button'),
),
],
);
},
),
),
),
);
}
}
在上面的代碼中,所有的實例化的類都是widgets。
應(yīng)用會根據(jù)事件交互,通知框架替換層級中的舊的widget為新的widget,最后框架會比較新舊widgets,高效的更新用戶界面。
Flutter擁有其自己的UI控制實現(xiàn),而不是由系統(tǒng)自帶的方法進(jìn)行托管:例如,IOS的Switch控件和Android的選擇控件都有一個Dart實現(xiàn)。
這樣的實現(xiàn)有幾個優(yōu)勢:
- 提供了誣陷的擴展性。
- Flutter可以直接合成所有的場景,而無需在Flutter與原生平臺之間來回的切換,從而避免了明顯的性能瓶頸。
- 將應(yīng)用的行為與操作系統(tǒng)的依賴解耦。
組成
Widget通常由更小的且用途單一的widgets組合而成,提供更強大的功能。
在設(shè)計的時候,相關(guān)的概念設(shè)計已盡可能地少量存在,而通過大量的內(nèi)容進(jìn)行填充。eg,F(xiàn)lutter在widgets層中使用了相同的概念(一個Widget)來表示屏幕上的繪制、布局(位置和大?。⒂脩艚换?、狀態(tài)管理、主題、動畫以及導(dǎo)航。在動畫層,Animation和Tween這對概念組合,涵蓋了大部分的設(shè)計空間。在渲染層,RenderObject用來描述布局、繪制、觸摸判斷以及可訪問性。在這些場景中,最終對于包含的內(nèi)容都很多:有數(shù)百個widgets和Render objects,以及數(shù)十種的動畫和補間類型。
類的層次結(jié)構(gòu)是有意的淺而廣,以最大限度的增加可能的組合數(shù)量,重點放在小的,可組合的widget上,確保每個widget都能橫好的完成一件事情。核心功能均被抽象,甚至像編劇和對齊這樣的基礎(chǔ)功能,都被實現(xiàn)為單獨的組件,而不是內(nèi)置于核心中。(這樣的實現(xiàn)也與傳統(tǒng)的API形成了對比,類似于邊距這樣的功能通常都內(nèi)置在了每個組件的公共核心內(nèi),F(xiàn)lutter中的widget則不同。)因此,如果你需要講一個widget居中,預(yù)期調(diào)整Align這樣的屬性,不如將他包裹在一個Center widget內(nèi)。
Flutter中包含了邊距,對齊,行,列和網(wǎng)格系列的widgets。這些布局類型的widgets自身沒有視覺內(nèi)容,而只用于控制其他的widgets的部分布局條件。Flutter也包含了以這種組合方法組成的實用性widgets。
例如,一個常用的widget Container,是由幾個widget組合而成,包含了布局、繪制、定位和大小的功能。更具體地說,Container是由LimitedBox、ConstrainedBox、Align、Padding、DecoratedBox和Transform組合而成的,你也可以通過查看源碼看到這些組合。Flutter 有一個典型的特征,即你可以深入到任意一個 widget,查看其源碼。因此,你可以通過同樣的方式組合其他的 widgets,也可以參考 Container 來創(chuàng)建其他的 widget,而不需要繼承 Container 來實現(xiàn)自定義的效果。
構(gòu)建widgets
先前提到,可以通過重寫build()方法,返回一個新的元素樹,來定義視覺展示。這棵樹用更為具體的術(shù)語表示了widget在UI中的部分。例如,工具欄widget的build方法可能會返回水平布局,其中可能包含了一些文字,各種各樣的按鈕。根據(jù)需要,框架會遞歸請求每個widget進(jìn)行構(gòu)建,直到整棵樹都被具體的可渲染的對象描述為止。然后框架會將可渲染的對象縫合在一起,組成可渲染的對象樹。
Widget的build方法應(yīng)該是沒有副作用的。每當(dāng)一個方法要求構(gòu)建市,widget都應(yīng)當(dāng)能返回一個widget的元素樹,與先簽返回的widget也沒有關(guān)聯(lián)??蚣軙鶕?jù)渲染對象樹來確定哪些構(gòu)建方法需要被調(diào)用,這是一響略顯繁重的工作。
每個渲染幀,F(xiàn)lutter都可以根據(jù)變換的狀態(tài),調(diào)用build()方法重建部分UI。因此,保證build方法輕量且能夠快速返回widget是非常關(guān)鍵的,繁重的計算工作應(yīng)該通過一些異步的方法來完成,然后作為構(gòu)建方法build的一部分存儲。
盡管這樣的實現(xiàn)看起來不夠成熟,但是這樣的自動對比方法非常有效,可以實現(xiàn)高性能的交互應(yīng)用。同時,以這種方式設(shè)計的build方法,將重點放在widget組合的聲明上,從而簡化了代碼,而不是以一種狀態(tài)去更新另一種狀態(tài)這樣的復(fù)雜過程。
狀態(tài)管理
那么,在眾多的widget都持有狀態(tài)的情況下,系統(tǒng)中的狀態(tài)是如何被傳遞和管理的呢?
與其他類相同,你可以通過widget的構(gòu)造函數(shù)來初始化數(shù)據(jù),如此一來build()方法可以確保子widget使用其所需要的數(shù)據(jù)進(jìn)行實例化:
@override
Widget build(BuildContext context) {
return ContentWidget(importantState);
}
然而,隨著widget樹層級的逐漸增加加深,依賴樹結(jié)構(gòu)上下傳遞狀態(tài)信息會變得十分麻煩。這時,第三張類型的widget——InheritedWidget,提供了一種從共享的祖先節(jié)點獲取數(shù)據(jù)的簡易辦法。你可以使用InheritedWidget創(chuàng)建包含狀態(tài)的widget,該widget會將一個共同的祖先節(jié)點包裹在widget樹中,如下:

現(xiàn)在,當(dāng)ExamWidget或者GradeWIdget對象需要獲取StudentState的數(shù)據(jù)時,可以直接使用以下方式:
final studentState = StudentState.of(context);
調(diào)用of(context)會根據(jù)當(dāng)前構(gòu)建的上下文(即當(dāng)前的widge位置的句柄),并返回類型為StudentState的在樹中距離最近的祖先節(jié)點。InheritedWidget同時也包含了updateShouldNotify()方法,F(xiàn)lutter會調(diào)用它來判斷依賴了某個狀態(tài)的widget是否需要更新重建。
InheritedWidget在Flutter中被大量用于共享狀態(tài),例如應(yīng)用的視覺主題,包含了應(yīng)用于整個應(yīng)用的顏色和字體樣式等屬性。MaterialApp的build()方法會在構(gòu)建市在樹中插入一個主題,更生層次的widget便可以使用.of()方法來查找相關(guān)的主題數(shù)據(jù),例如:
Container(
color: Theme.of(context).secondaryHeaderColor,
child: Text(
'Text with a background color',
style: Theme.of(context).textTheme.headline6,
),
);
類似的,以該方法實現(xiàn)的還有提供了路由頁面的Navigator,提供了屏幕信息指標(biāo),包括方向,尺寸和高度的MediaQuery等等。
隨著應(yīng)用程序的不斷迭代,更高級的狀態(tài)管理方法變得更加有吸引力,它們可以減少有狀態(tài)的widget的創(chuàng)建。許多Flutter應(yīng)用使用了provider用于狀態(tài)管理,它對InheritedWidget進(jìn)行了進(jìn)一步的包裝。FLutter的分層架構(gòu)也允許使用其他的實現(xiàn)來替換狀態(tài)管理只UI的方案,例如flutter_hooks。
渲染和布局
本節(jié)介紹Flutter的渲染機制,包括將widget層級結(jié)構(gòu)轉(zhuǎn)換成屏幕上繪制的實際像素的一系列步驟。
Flutter的渲染模型
你可能思考過:既然Flutter是一個跨平臺的框架,那么它又是如何提供與原生平臺框架相當(dāng)?shù)男阅艿哪兀?/p>
讓我們從Android原生應(yīng)用的角度開始思考。當(dāng)你在編寫繪制的內(nèi)容的時候,你需要調(diào)用Android框架的Java代碼。Android的系統(tǒng)庫提供了可以將自身繪制到Canvas對象的組件,接下來Android就可以使用由C/C++編寫的Skia圖形引擎,調(diào)用CPU和GPU完成在設(shè)備上的繪制。
跨平臺框架都會在Android和IOS的UI底層庫上創(chuàng)建一層抽象,該抽象層嘗試抹平各個系統(tǒng)之間的差異。這時,應(yīng)用程序的代碼通常使用JavaScript等解釋型語言來進(jìn)行編寫,這些代碼會與基于Java的Android和基于OC的IOS進(jìn)行交互,最終展示UI界面。所有流程都增加了顯著的開銷,在UI和應(yīng)用邏輯有凡在的交互時更為如此。
相比之下,F(xiàn)lutter通過染過系統(tǒng)UI組件庫,使用自己的widget內(nèi)容集,消減了抽象層的開銷。用于繪制Flutter圖像內(nèi)容的Dart代碼被編譯成機器碼,并使用Skia進(jìn)行渲染。Flutter同時也嵌入了自己的Skia副本作文引擎的一部分,讓開發(fā)者能再設(shè)備未更新到最新系統(tǒng)時,也能跟進(jìn)升級自己的應(yīng)用,保證穩(wěn)定性并提升性能。
從用戶操作到GPU
對于Flutter渲染機制而言,首要原則就是簡單快捷。Flutter為數(shù)據(jù)流向系統(tǒng)提供了直通的通道,如以下的流程圖所示:

接下來讓我們更加深入了解其中的一些階段。
構(gòu)建:從Widget到Element
首先觀察以下的代碼片段,它代表了一個簡單的widget結(jié)構(gòu):
Container(
color: Colors.blue,
child: Row(
children: [
Image.network('https://www.example.com/1.png'),
const Text('A'),
],
),
);
當(dāng)Flutter需要繪制這段代碼時,框架會調(diào)用build()方法,返回一顆基于當(dāng)前應(yīng)用狀態(tài)來繪制UI的widget子樹。在這個過程中,build()方法可能會在必要時,根據(jù)狀態(tài)引入新的widget。在上面的例子中,Container的color和child就是電信的例子。我們可以查看Container的源碼,會發(fā)現(xiàn)當(dāng)color屬性不為空時,ColoredBox會被加入用于顏色布局。
if (color != null)
current = ColoredBox(color: color!, child: current);
與之對應(yīng)的,Image和Text在構(gòu)建過程中也會引入RawImage和RichText。如此一來,最終生成的widget結(jié)構(gòu)比代碼表示的層級更深,在該場景中如下圖:

這就是為什么你在使用Dart DevTools的Flutter inspector調(diào)試widget樹結(jié)構(gòu)時,會發(fā)下實際的結(jié)構(gòu)比你原本代碼中的結(jié)構(gòu)更深。
在構(gòu)建階段,F(xiàn)lutter會將代碼中描述的widgets轉(zhuǎn)化成對應(yīng)的Element樹,每一個Widget都有一個對應(yīng)的Element。每一個Element代表了梳妝層次結(jié)構(gòu)中特定位置的widget實例。目前有兩種Element的基本類型:
- ComponentElement,其他Element的宿主。
- RenderObjectElement,參與布局或繪制階段的Element。

RenderObjectElement是底層RenderObject與對應(yīng)的widget之間的橋梁,我們晚點會介紹。
任何widget都可以通過其BuildContext引用到Element,它是該widget在樹中的位置的句柄。類似于Theme.of(context)方法調(diào)用中的context,它作為build()方法的參數(shù)被傳遞。
由于widgets以及它上下節(jié)點的關(guān)系都是不可變的,因此,對widget樹做任何操作(例如將Text('A')修改成Text('B'))都會返回一個新的widget對象集合。但是這并不是意味著底層呈現(xiàn)的內(nèi)容必須要重新構(gòu)建。Element樹每一幀之間都是持久化的,因此起著至關(guān)重要的性能作用,F(xiàn)lutter依靠該優(yōu)勢,實現(xiàn)類一種好似widget樹被完全拋棄,而緩存了底層表示的機制。Flutter可以根據(jù)發(fā)生變化的widget,來重建需要重新配置的Element樹的部分。
布局和渲染
很少有應(yīng)用只繪制單個widget。因此,有效的排布widget的結(jié)構(gòu)以及在渲染完成前決定每個Element的大小和位置,是所有UI框架的重點之一。
在渲染樹中,每個節(jié)點的基類都是RenderObject,該基類為布局和繪制定義了一個抽象的模型。這是再平凡不過的事情:它并不總是一個固定大小,甚至不尊徐笛卡爾坐標(biāo)系規(guī)律。每一個RenderObjectElement都了解其父節(jié)點的信息,對于其子節(jié)點,除了如何訪問和獲得他們的布局約束,并沒有更多的信息。這樣設(shè)計讓RenderObject擁有高效的抽象能力,能夠處理各種各樣的使用場景。
在構(gòu)建階段,F(xiàn)lutter會為Element樹中的每個RenderObjectElement創(chuàng)建或更新其對于的一個從RenderObject繼承的對象。RenderObject實際上是原語:渲染文字的RenderParagraph、渲染圖片的RenderImage以及在繪制子節(jié)點內(nèi)容前應(yīng)用變換的RenderTransform是更為上層的實現(xiàn)。

大部分的Flutter widget是由一個繼承了RenderBox的子類對象渲染的,他們呈現(xiàn)出的RenderObject會在二維迪卡空間中擁有固定的大小。RenderBox提供了盒子模型限制,為每個widget關(guān)聯(lián)了渲染的最小和最大的寬度和高度。
在進(jìn)行布局的時候,F(xiàn)lutter會議DFS(深度優(yōu)先遍歷)方式遍歷渲染書,并將限制以自上而下的方式從父節(jié)點傳遞給子節(jié)點。子節(jié)點若要確定自己的大小,則必須遵循父節(jié)點傳遞的限制。子節(jié)點的響應(yīng)方式是在父節(jié)點簡歷的約束內(nèi)將大效益自下而上的方式傳遞給父節(jié)點。

在遍歷完成一次樹之后,每個對象都通過父級約束而擁有了明確的大小,隨時可以通過調(diào)用paint()進(jìn)行渲染。
盒子限制模型十分強大,它的對象布局的時間復(fù)雜度是O(n):
- 父節(jié)點可以通過設(shè)定最大和最小的尺寸限制,決定其子節(jié)點對象的大小。例如:在一個手機應(yīng)用中,最高層級的渲染對象將會限制其子節(jié)點的大小為屏幕的尺寸。(子節(jié)點可以選擇如何占用空間。例如,它們可能在設(shè)定的限制中以居中的方式布局。)
- 父節(jié)點可以決定子節(jié)點的寬度,而讓子節(jié)點靈活地自適應(yīng)布局高度(或決定高度而自適應(yīng)寬度)?,F(xiàn)實中有一種例子就是流式布局的文本,它們常常會填充橫向限制,再根據(jù)文字內(nèi)容的多少決定高度。
這樣的盒子約束模型,同樣也適用于子節(jié)點對象需要知道有多少可用空間渲染其內(nèi)容的場景,通過使用 LayoutBuilder widget,子節(jié)點可以得到從上層傳遞下來的約束,并合理利用該約束對象,使用方法如下:
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const OneColumnLayout();
} else {
return const TwoColumnLayout();
}
},
);
}
所有的RenderObject的根節(jié)點是RenderView,代表了渲染樹的總體輸出。當(dāng)平臺需要渲染新的一幀內(nèi)容時(例如一個vsync型號或者一個紋理的更新完成),會調(diào)用一次compositeFrame()方法,它是RenderView的一部分。該方法會創(chuàng)建一個SceneBuilder來觸發(fā)當(dāng)前畫面的更新。當(dāng)畫面更新完畢,RenderView會將合成的畫面?zhèn)鬟f給dart:ui中的Window.render()方法,控制GPU進(jìn)行渲染。
Platform embedding
我們都知道,F(xiàn)lutter 的界面構(gòu)建、布局、合成和繪制全都由 Flutter 自己完成,而不是轉(zhuǎn)換為對應(yīng)平臺系統(tǒng)的原生組件。獲取紋理和聯(lián)動應(yīng)用底層的生命周期的方法,不可避免地會根據(jù)平臺特性而改變。 Flutter 引擎本身是與平臺無關(guān)的,它提供了一個穩(wěn)定的 ABI(應(yīng)用二進(jìn)制接口),包含一個 平臺嵌入層,可以通過其方法設(shè)置并使用 Flutter。
平臺嵌入層是用于呈現(xiàn)所有 Flutter 內(nèi)容的原生系統(tǒng)應(yīng)用,它充當(dāng)著宿主操作系統(tǒng)和 Flutter 之間的粘合劑的角色。當(dāng)你啟動一個 Flutter 應(yīng)用時,嵌入層會提供一個入口,初始化 Flutter 引擎,獲取 UI 和柵格化線程,創(chuàng)建 Flutter 可以寫入的紋理。嵌入層同時負(fù)責(zé)管理應(yīng)用的生命周期,包括輸入的操作(例如鼠標(biāo)、鍵盤和觸控)、窗口大小的變化、線程管理和平臺消息的傳遞。 Flutter 擁有 Android、iOS、Windows、macOS 和 Linux 的平臺嵌入層,當(dāng)然,開發(fā)者可以創(chuàng)建自定義的嵌入層,正如這個 可用的例子 以 VNC 風(fēng)格的幀緩沖區(qū)支持了遠(yuǎn)程 Flutter,還有 [支持樹莓派運行的例子]https://github.com/ardera/flutter-pi)。
每一個平臺都有各自的一套 API 和限制。以下是一些關(guān)于平臺簡短的說明:
在 iOS 和 macOS 上, Flutter 分別通過
UIViewController和NSViewController載入到嵌入層。這些嵌入層會創(chuàng)建一個FlutterEngine,作為 Dart VM 和您的 Flutter 運行時的宿主,還有一個FlutterViewController,關(guān)聯(lián)對應(yīng)的FlutterEngine,傳遞 UIKit 或者 Cocoa 的輸入事件到 Flutter,并將FlutterEngine渲染的幀內(nèi)容通過 Metal 或 OpenGL 進(jìn)行展示。在 Android 上,F(xiàn)lutter 默認(rèn)作為一個
Activity加載到嵌入層中。此時視圖是通過一個FlutterView進(jìn)行控制的,基于 Flutter 內(nèi)容的合成和 z 排列 (z-ordering) 的要求,將 Flutter 的內(nèi)容以視圖模式或紋理模式進(jìn)行呈現(xiàn)。在 Windows 上,F(xiàn)lutter 的宿主是一個傳統(tǒng)的 Win32 應(yīng)用,內(nèi)容是通過一個將 OpenGL API 調(diào)用轉(zhuǎn)換成 DirectX 11 的等價調(diào)用的庫 ANGLE 進(jìn)行渲染的。目前正在嘗試將 UWP 應(yīng)用作為 Windows 的一種嵌入層,并將 ANGLE 替換為通過 DirectX 12 直接調(diào)用 GPU 的方式。