Flutter - 什麼是Widget,RenderObject和Element

原文在這裏git

本文詳細解釋瞭如何把Widget轉化成了屏幕上的一個個像素點

想要成爲一個更好的開發,瞭解底層的實現技術幾乎是必不可少的。你能夠更容易的建立自定義的佈局和特效,若是你學習了這些底層技術是如何工做的。也可讓少在電腦前加幾個晚上的班。github

本文的目的就是要介紹Flutter之下的技術,並讓你理解他們的是如何運行的。less

## 咱們如今開始ide

你也許已經知道如何使用StatelessWidget和StatefulWidget。可是這些Widget只是把其餘的Widget組合到了一塊兒。在屏幕加上的佈局和繪製是發生在別的地方的。工具

**我強烈建議你打開你最心愛的編輯工具,而後跟着本文一步一步的查看實際代碼是如何實現的並一路「原來如此」過去。、佈局

## Opacity組件
從Opacity這個組件開始是最好不過了。這個Widget足夠的簡單,並且是一個絕佳的例子。學習

它只接受一個子組件。所以你能夠把任何一個widget放進Opacity裏改變其顯示。另外還有一個值opacity,在0.0和1.0之間。它用來控制透明度。動畫

OpacitySingleChildRenderObjectWidget的子類。繼承的路徑是這樣的:ui

Opacity -> SingleChildRenderObjectWidget -> RenderObjectWidget -> Widget。

StatelessWidgetStatefulWidget是這樣的繼承路徑: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;
}

RenderOpacity

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,執行了具體的佈局和繪製動做
  • 由於Opacity組件和它的子組件基本上徹底同樣,因此它代理了其子組件的全部方法
  • 它override了繪製(paint)方法,這個方法會給Opacity的子組件添加一個特定的opacity值

就這樣了麼?

記住組件(widget)只不過是一個配置,RenderObject纔是管理佈局和繪製的。

在Flutter裏,你基本上一直都會建立新的Widget,當build()方法被調用的時候你就建立了一大堆的組件。當某些變化發生的時候,build方法基本上都會被調用。好比一個動畫裏,build方法更會被常常調用。最好是不要每次都從新繪製整個子樹,更新會更好一些。

你不能得到一個組件在屏幕上的大小或者位置,由於一個組件姿式一個藍圖,並非實際繪製在界面上的。它只包含了render object須要用的信息。

Element

Element是一個組件樹的實際組件。

發生了什麼

第一次一個組件被建立的時候,會有一個Element被建立,而且二者互相關聯。以後這個element被插入到了一個樹裏。若是組件(widget)發生了更改,它會和舊的組件對比,並對應的更新element。最重要的是在這個時候element不會從新建立,只會被更新。

Element是flutter核心的一部分,不過如今不須要知道更多的內容。這些就足夠了。

Opacity組件的Element是在哪裏建立的

請好奇的同窗看過來。在SingleChildRenderObectWidget

@override
SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this);

SingleChildRenderObjectElement也只是一個有一個child的Element。

Element建立RenderObject

若是是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組件工做的原理。

個人目標是用這篇文章來介紹組件之下的原理。還有不少的內容沒有覆蓋到,可是我但願這篇文章至少可讓你初窺門徑。

相關文章
相關標籤/搜索