Flutter,一切皆Widget。
先看一張Google提供的的原理圖:

這張圖清晰地解釋了:Flutter只關心向 GPU提供視圖數(shù)據(jù),GPU的 VSync信號同步到 UI線程,UI線程使用 Dart來構建抽象的視圖結構,這份數(shù)據(jù)結構在 GPU線程進行圖層合成,視圖數(shù)據(jù)提供給 Skia引擎渲染為 GPU數(shù)據(jù),這些數(shù)據(jù)通過 OpenGL或者 Vulkan提供給 GPU。
所以 Flutter并不關心顯示器、視頻控制器以及 GPU具體工作,它只關心 GPU發(fā)出的 VSync信號,盡可能快地在兩個 VSync信號之間計算并合成視圖數(shù)據(jù),并且把數(shù)據(jù)提供給 GPU。
Skia是一款內(nèi)置于android或者說Google的2D繪圖引擎,且是跨平臺的,但是IOS不自帶這個引擎,所以打出來的IOS的包比android大一些。

從上圖我們可以看到,更進一步描述了開始的渲染流程圖,用戶輸入是驅(qū)動視圖更新的信號,如滑動屏幕等。然后會觸發(fā)動畫進度更新,框架開始build抽象視圖數(shù)據(jù),在之后,視圖會進行布局、繪制、合成(渲染過程的三個步驟),最后進行光柵化處理把數(shù)據(jù)生成一個個真正的像素填充數(shù)據(jù)。在Flutter中,構建視圖數(shù)據(jù)結構、布局、繪制、合成、與Engine的數(shù)據(jù)同步和通信放到了Framework層,而光柵化則放在了Engine層中。
視圖數(shù)據(jù)結構:
上面說到UI線程使用Dart來構建抽象的視圖結構,無論是比較底層的框架,還是上層的應用代碼,在向繪制引擎提供視圖數(shù)據(jù)時,都需要一份結構化的視圖數(shù)據(jù),類似抽象語法樹,也就是上面所講到的Layer Tree,F(xiàn)lutter的視圖數(shù)據(jù)抽象分為3部分,分別是Widget、Element、RenderObject。
Widget:

Widget里面存儲了一個視圖的配置信息,包括布局、屬性等。它是一份輕量的數(shù)據(jù)結構,在構建時是結構樹,它不參與直接的繪制,所以說Widget僅僅是配置文件,F(xiàn)lutter團隊對它做了優(yōu)化,頻繁的創(chuàng)建/銷毀它們,都不會存在明顯的性能問題。所以一旦數(shù)據(jù)發(fā)生改變,直接銷毀,new一個新的。
Widget包含StatelessWidget和StatefullWidget兩個常用類,StatelessWidget是無狀態(tài)變化的類,需要重新展示時得重新new,StatefullWidget是有狀態(tài)變化的類,很類似react的設計理念,state存放于中間,通過調(diào)用state.setState()才會觸發(fā)該節(jié)點及以下整個子樹更新。
Element

Element是Widget的抽象,當一個Widget首次被創(chuàng)建的時候,那么這個Widget會通過Widget.createElement,創(chuàng)建一個element,掛載到Element Tree遍歷視圖樹。在attachRootWidget函數(shù)中,把 widget交給了 RenderObjectToWidgetAdapter這座橋梁,Element創(chuàng)建的同時還持有 Widget和 RenderObject的引用。構建系統(tǒng)通過遍歷Element Tree來創(chuàng)建RenderObject,每一個Element都具有一個唯一的key,當觸發(fā)視圖更新時,只會更新標記的需變化的Element。類似react中setState后虛擬dom樹的更新。
一個Widget對象可以對應多個Element對象。(相同的widget可以同時存在)
RenderObject

RenderObject作為UI視圖的描述方式,其中含有4個重用的屬性和方法,
constraints: 從 parent 傳遞過來的約束。
parentData: 這里面攜帶的是 parent 渲染 child 的時候所用到的數(shù)據(jù)。
performLayout():此方法用于布局所有的 child。
paint():這個方法用于繪制自己或者 child。
后面會看到具體出現(xiàn)場景。
在 RenderObject樹中會發(fā)生 Layout、Paint的繪制事件(下面會具體講到),大部分繪圖性能優(yōu)化發(fā)生在這里,RenderObject Tree構建為Canvas的所需描述數(shù)據(jù),加入到Layer Tree中,最終在Flutter Engine中進行視圖合成并光柵化交給GPU。
接下來看下Flutter Widget渲染的三個階段:
1.Layout(布局的計算):確定每個子widget大小和在屏幕中的位置。
2.Paint(視圖的繪制):為每個子widget提供canvas,讓他們繪制自己。
3.Composite(合成):所有widget合成到一起,交給GPU處理。
Layout 布局

1.父控件(parent)將布局約束傳遞給子控件(child),父控件通過傳遞Containers參數(shù),告訴子控件自己的大?。ú季旨s束),以此來決定子控件的位置。
2.子控件將布局詳情上傳給父控件,并繼續(xù)向下約束子控件,子控件的位置不存儲在自己的容器(布局詳情)中,而是存儲在自己的parentData字段里,所以當他的位置發(fā)生變化時,并不需要重新布局或繪制。
例如:parent會給child一個約束,最大寬度500px(布局約束)、最大高度500px(布局約束),child會說我只用100px(布局詳情),并將其傳遞給parent,parent會繼續(xù)向上傳遞,直到root widget為止。所以布局約束數(shù)據(jù)的傳遞順序是自上而下,和web一樣,布局約束條件和布局詳情都取決于盒子模型協(xié)議和滑動布局協(xié)議。