這是我參與8月更文挑戰的第 12 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰
,
Flutter,什麼是 Widgets、RenderObjects 和 Elements?
有沒有想過 Flutter 如何獲取這些小部件並將它們實際轉換為螢幕上的畫素?不?
你應該!
瞭解底層技術的工作原理是優秀開發人員和優秀開發人員之間的區別。
當您知道哪些有效,哪些無效時,您可以更輕鬆地建立自定義佈局和特殊效果;知道這一點可以讓您在鍵盤上度過幾個漫長的夜晚。
這篇文章的目的是向您介紹 flutter 表面之外的世界。我們將看看 Flutter 的不同方面,並瞭解它的實際工作原理。
讓我們開始吧
您可能已經知道如何使用 StatelessWidget 和 StatefulWidget。但是這些小部件只組成其他小部件。佈置小部件並渲染它們發生在其他地方。
我強烈建議開啟你最喜歡的 IDE 並跟隨,檢視實際程式碼中的結構通常會產生那些“啊哈”時刻。在 Intellij 中,您可以雙擊 shift 並輸入類名來查詢它。
不透明度
為了熟悉 flutter 工作原理的基本概念,我們將檢視Opacity小部件並檢查它。因為它是一個非常基本的小部件,所以它是一個很好的例子。
它只接受一個child。因此,您可以將任何小部件包裝在 中Opacity
並更改其顯示方式。除了孩子之外,只有一個引數被呼叫opacity
,它是一個介於 0.0 和 1.0 之間的值。它控制不透明度(廢話)。
The Widget
這Opacity
是一個SingleChildRenderObjectWidget
.
擴充套件類擴充套件的層次結構是這樣的:
Opacity → SingleChildRenderObjectWidget → RenderObjectWidget → Widget
相比之下,StatelessWidget 和 StatefulWidget 如下所示:
StatelessWidget/StatefulWidget → Widget
不同之處在於 Stateless/StatefulWidget 只組成小部件,而 Opacity 小部件實際上改變了小部件的繪製方式。
但是,如果您檢視這些類中的任何一個,您將找不到與實際繪製不透明度相關的任何程式碼。
那是因為小部件只儲存配置資訊。 在這種情況下,不透明度小部件僅儲存不透明度值。
這就是每次呼叫構建函式時都可以建立新小部件的原因。因為小部件的構建成本並不高。它們只是資訊的容器。
渲染
但是,在沒有渲染髮生的呢?
它在 RenderObjects 中
從名稱中您可能已經猜到,RenderObject 負責一些事情,包括渲染。
Opacity 小部件使用這些方法建立和更新 RenderObject。
@override
RenderOpacity createRenderObject(BuildContext context) => new RenderOpacity(opacity: opacity);
@override
void updateRenderObject(BuildContext context, RenderOpacity renderObject) {
renderObject.opacity = opacity;
}
複製程式碼
渲染不透明度
該Opacity
部件尺寸本身是完全相同的大小作為其子一樣。It basically mimics every aspect of the child but the painting. 在繪製它的孩子之前,它會為其新增不透明度。
在這種情況下,RenderOpacity 需要實現所有方法 (例如執行佈局/ hit testing/ 計算大小) 並要求其子項執行實際工作.
The RenderOpacity
extends the RenderProxyBox
(which mixes in a few other classes). Those are exactly implementing those methods and deferring the actual calculation to the only child.
它基本上模仿了孩子的每一個方面 ,但畫。
(例如執行佈局/命中測試/計算大小)。
該RenderOpacity
擴充套件了RenderProxyBox
(在幾個其他類融合)。那些正是實施這些方法並將實際計算推遲到唯一的孩子。
double get opacity => _opacity;
double _opacity;
set opacity(double value) {
_opacity = value;
markNeedsPaint();
}
複製程式碼
我在這段程式碼中刪除了很多斷言/優化。檢視完整方法的原文 source
欄位通常向私有變數公開一個 getter。還有一個 setter,它除了設定欄位外,還呼叫markNeedsPaint()
or markNeedsLayout()
。 顧名思義,它告訴系統“嘿我已經改變了,請重新繪製/重新佈局我”。
在裡面RenderOpacity
我們找到了這個方法:
Inside the RenderOpacity
we find this method:
@override
void paint(PaintingContext context, Offset offset) {
context.pushOpacity(offset, _alpha, super.paint);
}
複製程式碼
同樣,刪除了優化和斷言. source
該PaintingContext
基本上是一個奇特的畫布。在這個漂亮的畫布上有一個叫做 pushOpacity 的方法
The PaintingContext
is basically a fancy canvas. And on this fancy canvas there is a method called pushOpacity. BINGO.
這一行是實際的不透明度實現。
回顧一下
- 該
Opacity
不是StatelessWidget
和StatefulWidget
而是一個SingleChildRenderObjectWidget
。 - 將
Widget
僅持有該渲染器可以使用的資訊。 - 在這種情況下,
Opacity
持有一個代表不透明度的雙精度值。 RenderOpacity
,它擴充套件了RenderProxyBox
執行實際布點/渲染等。- 因為 opacity 的行為與其子級幾乎完全一樣,所以它將每個方法呼叫委託給子級。
- 它覆蓋了paint 方法並呼叫pushOpacity,這將向小部件新增所需的不透明度。
請記住,小部件只是一個配置,RenderObject
唯一管理佈局/渲染等。
在 Flutter 中,您基本上一直在重新建立小部件。當您的build()
方法被呼叫時,您將建立一堆小部件。每次發生變化時都會呼叫此構建方法。例如,當一個動畫發生時,build 方法會經常被呼叫。這意味著您不能每次都重建整個子樹。相反,您想要更新它。
您無法獲得小部件在螢幕上的大小或位置,因為小部件就像藍圖,實際上並不在螢幕上。它只是對底層渲染物件應該使用哪些變數的描述。
元素介紹
元素是大樹中的具體小部件。
基本上發生的事情是:
第一次建立小部件時,它會膨脹為Element
. 然後元素被插入到樹中。如果小部件稍後發生更改,則會將其與舊小部件進行比較,並且元素會相應地更新。重要的是,元素不會被重建,它只會被更新。
元素是核心框架的核心部分,顯然它們還有更多內容,但現在這些資訊已經足夠了。
不透明度示例中建立的元素在哪裡?
對於那些好奇的人來說,只是一小段。
在SingleChildRenderObjectWidget
建立它。
@override
SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this);
複製程式碼
而這SingleChildRenderObjectElement
只是一個只有一個孩子的元素。
該元素建立了 RenderObject,但在我們的例子中,Opacity 小部件建立了它自己的 RenderObject?
這只是為了流暢的 API。因為更多時候不是,小部件需要一個RenderObject
但沒有自定義Element
. 該RenderObject
實際上是由創造Element
,讓我們一起來看看:
SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);
複製程式碼
將SingleChildRenderObjectElement
獲取到reference to RenderObjectWidget
(其中有建立的方法RenderObject
)。
mount 方法是將元素插入元素樹的地方,這裡發生了神奇的事情(RenderObjectElement 類):
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
複製程式碼
(Right after super.mount(parent,newSlot); source
只有一次(當它被安裝時)它會詢問小部件“請給我你想要使用的渲染物件,以便我可以儲存它”。
結束
就是這樣。這就是不透明度小部件在內部的工作方式。
我這篇文章的目標是向您介紹小部件之外的世界。還有很多話題要討論,但我希望我能給你一個很好的內部工作介紹。