原文在這裏。git
本文詳細解釋瞭如何把Widget轉化成了屏幕上的一個個像素點
想要成爲一個更好的開發,瞭解底層的實現技術幾乎是必不可少的。你能夠更容易的建立自定義的佈局和特效,若是你學習了這些底層技術是如何工做的。也可讓少在電腦前加幾個晚上的班。github
本文的目的就是要介紹Flutter之下的技術,並讓你理解他們的是如何運行的。less
## 咱們如今開始ide
你也許已經知道如何使用StatelessWidget和StatefulWidget。可是這些Widget只是把其餘的Widget組合到了一塊兒。在屏幕加上的佈局和繪製是發生在別的地方的。工具
**我強烈建議你打開你最心愛的編輯工具,而後跟着本文一步一步的查看實際代碼是如何實現的並一路「原來如此」過去。、佈局
## Opacity組件
從Opacity這個組件開始是最好不過了。這個Widget足夠的簡單,並且是一個絕佳的例子。學習
它只接受一個子組件。所以你能夠把任何一個widget放進Opacity裏改變其顯示。另外還有一個值opacity
,在0.0和1.0之間。它用來控制透明度。動畫
Opacity
是SingleChildRenderObjectWidget
的子類。繼承的路徑是這樣的:ui
Opacity -> SingleChildRenderObjectWidget -> RenderObjectWidget -> Widget。
而StatelessWidget
和StatefulWidget
是這樣的繼承路徑:this
StatelessWidget/StatefulWidget -> Widget
不一樣之處就在於StatelessWidget和StatefulWidget只是把不一樣的組件(Widget)組合在一塊兒,而Opacity組件會控制一個組件如何繪製。
可是若是你想從Opacity的代碼裏找到一些繪製像素的線索,這幾乎是徒勞的。這是由於一個Widget只包含了配置信息,好比Opacity
組件只包含了一個opacity的值。
這就是爲何你能夠在一個組件的
build
方法裏建立新的Widget,裏面並不會包含耗費資源的構建組件的代碼,僅僅是包含了一些構建須要的信息。
繪製的時候會發生什麼呢?
RenderObject
繪製都在RenderObject
裏進行。這個單從名稱裏也能夠知道了。Opacit組件這樣建立和更新RenderObject:
@override RenderOpacity createRenderObject(BuildContext context) => new RenderOpacity(opacity: opacity); @override void updateRenderObject(BuildContext context, RenderOpacity renderObject) { renderObject.opacity = opacity; }
Opacity
組件把自身的size和子組件的size設定成同樣的值。它基本上和它子組件的每一方面都同樣,除了繪製。在繪製子組件以前添加了opacity值。
在這個狀況下, RenderOpacity
須要實現全部的方法(好比,執行佈局、碰撞檢測和計算size)而後交給子類去執行具體的工做。
RenderOpacity
繼承了RenderProxyBox
(這個類mixin了一些其餘 的類)。這些類都分別實現了上面提到的那些方法。
double get opacity => _opacity; double _opacity; set opacity(double value) { _opacity = value; markNeedsPaint(); }
我(做者)刪除了大部分的代碼,要看所有代碼能夠在這裏看。
getter把私有的值暴露出去。setter裏面會調用markNeedsPaint()
或者markNeedsLayout()
。就如名字所言,它會通知系統「這個組件發生了改變,須要重繪或者從新佈局」
在RenderOpacity
裏面還有以下的代碼:
@override void paint(PaintingContext context, Offset offset) { context.pushOpacity(offset, _alpha, super.paint); }
PaintingContext
就是一個畫布。在這個畫布上有一個方法叫作pushOpacity
!
這一行就是opacity的具體實現。
Opacity
不是一個StatelessWidget
或者StatefullWidget
而是一個SingleChildRendeObjectWidget
。Opacity
組件包含了一個opacity值。RenderOpacity
,繼承自RenderProxyBox
,執行了具體的佈局和繪製動做記住組件(widget)只不過是一個配置,RenderObject
纔是管理佈局和繪製的。
在Flutter裏,你基本上一直都會建立新的Widget,當build()
方法被調用的時候你就建立了一大堆的組件。當某些變化發生的時候,build方法基本上都會被調用。好比一個動畫裏,build方法更會被常常調用。最好是不要每次都從新繪製整個子樹,更新會更好一些。
你不能得到一個組件在屏幕上的大小或者位置,由於一個組件姿式一個藍圖,並非實際繪製在界面上的。它只包含了render object須要用的信息。
Element是一個組件樹的實際組件。
第一次一個組件被建立的時候,會有一個Element被建立,而且二者互相關聯。以後這個element被插入到了一個樹裏。若是組件(widget)發生了更改,它會和舊的組件對比,並對應的更新element。最重要的是在這個時候element不會從新建立,只會被更新。
Element是flutter核心的一部分,不過如今不須要知道更多的內容。這些就足夠了。
請好奇的同窗看過來。在SingleChildRenderObectWidget
裏
@override SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this);
SingleChildRenderObjectElement
也只是一個有一個child的Element。
若是是Element建立了RenderObject,那麼Opacity組件的RenderObject怎麼是本身建立的呢?
基本上是由於Opacity組件只是須要一個RenderObject
可是不須要一個定製的Element。
看下面的代碼:
SingleChildRenderObjectElement(SingleChildRenderObjectWidget widget) : super(widget);
SingleChildRenderObjectElement
有一個RenderObjectWidget
的引用(這裏也包含了建立RenderObject
的方法)。
在RenderObjectElement#mount
方法裏element被插入到了element樹裏:
@override void mount(Element parent, dynamic newSlot) { super.mount(parent, newSlot); _renderObject = widget.createRenderObject(this); attachRenderObject(newSlot); _dirty = false; }
在Element掛載的時候,它纔會讓組件建立一個render object。
這就是Opacity組件工做的原理。
個人目標是用這篇文章來介紹組件之下的原理。還有不少的內容沒有覆蓋到,可是我但願這篇文章至少可讓你初窺門徑。