Flutter框架分析(二)-- Widget

1. 前言

Widget是開發Flutter應用過程當中,接觸最多的概念。Flutter的理念是萬物皆Widget(Everything is Widget),這是爲了實現Flutter的一個設計理念:激進式組合(Aggressive composability)。Widget由一系列的小的Widget組合而成,而這些進行組合的Widget,自己是由更基礎的Widget構成。例如,Padding也是一種Widget,而不是Widget中的一個屬性。html

Flutter中,Widget的定義是:描述一個UI元素的配置數據。它並非表示最終繪製在設備上的顯示元素,而只是描述顯示元素的一個配置數據。其主要功能包括:web

  • 描述UI控件(如Text);
  • 描述UI的具體樣式(如Color);
  • 描述一些功能(如GestureDetector);
  • 。。。

2. Widget分類

image.png 如上圖所示,Widget從功能上看,能夠分爲三大類:數組

  • Component Widget

組合類Widget。這類Widget主要用來組合其餘更基礎的Widget,獲得功能更加複雜的Widget。日常的業務開發通常用的就是此類Widgetmarkdown

  • Render Widget

渲染類Widget,這類Widget是框架最核心的Widget,會參與後面的佈局和渲染流程;只有這種類型的Widget會繪製到屏幕上。架構

  • Proxy Widget

代理類Widget,其自己並不涉及Widget內部邏輯,只是爲子Widget提供一些附加的中間功能。例如:InheritedWidget用於將一些狀態信息傳遞給子孫Widgetapp

3. Widget和Element

前面說過,Widget的功能是描述一個UI元素的配置數據,真正表明屏幕上顯示元素的類是Element。並且,一個Widget能夠對應多個Element,緣由是同一個Widget對象能夠被添加到UI樹的不一樣部分。關於Element的介紹,以及WidgetElement的關係咱們將在後面深刻介紹。如今須要記住如下兩點:框架

  • WidgetElement的配置數據,Element才真正表明屏幕顯示元素;
  • 一個Widget對象能夠對應多個Element對象。

如下是一個Widget對象對應多個Element對象的一個示例:less

class SameWidgetMultiElementWidget1 extends StatefulWidget {
  @override
  _SameWidgetMultiElementWidgetState createState() => new _SameWidgetMultiElementWidgetState();
}

class _SameWidgetMultiElementWidgetState extends State<SameWidgetMultiElementWidget1> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    Text testText = Text("multi element");
    return Column(
      children: <Widget>[
        testText,
        testText,
      ],
    );
  }
}
複製代碼

其對應的Widget TreeElement Tree以下:ide

image.png 在上面示例中,左邊是Widget Tree,右邊是Element Tree。在Widget Tree中,Column有兩個子節點,可是這兩個子節點使用的是同一個Text對象,所以這個Text對象出如今兩個不一樣的位置(Slot),此時在Element Tree會生成兩個不一樣的StatelessElement對象,即一個Text對象對應兩個StatelessElement對象。svg

4. StatelessWidget

StatelessWidget是咱們常常接觸的一類widget,其適用於不須要維護組件狀態的場景。其核心函數以下:

  • createElement

源碼以下:

@override
StatelessElement createElement() => StatelessElement(this);
複製代碼

該函數用於建立StatelessElement,通常用戶不用重寫該函數。

  • build

源碼以下:

@protected
Widget build(BuildContext context);
複製代碼

該函數是Widget的核心方法之一,主要用於向用戶提供描繪用戶界面的接口。用戶經過此函數組合用戶須要的子WidgetFramework則經過調用該方法,將用戶定義的Widget組合加入到Widget Tree中。其調用場景主要有兩種:

  1. Widget被加入Widget Tree
  2. Widget的依賴(depencies)變化,例如此Widget依賴的InheritedWidget變更了。

5. StatefulWidget

StatefulWidget適用於須要維護組件狀態的場景,它自己是不可變的,可是它有一個可變的State。所以,它持有的State一般被用於記錄狀態數據,例如計數器中的計數等。其核心函數以下:

  • createElement

源碼以下:

@override
StatefulElement createElement() => StatefulElement(this);
複製代碼

該函數用於建立StatefulElement ,通常用戶不用重寫該函數。

  • createState

源碼以下:

@protected
@factory
State createState();
複製代碼

該函數用於建立對應的State,用戶經過該方法建立其須要保存數據的StateFramework在一個StatefulWidget的生命週期內,可能會屢次調用該函數。例如,假如一個StatefulWidget被加入Widget Tree多個位置,Framework將爲每一個位置建立一個單獨的State.

6. State

State表示對應的StatefulElement須要保存的信息,其有如下特色:

  1. 在對應Widget在構建時能夠被同步讀取;
  2. 在對應Widget的生命週期中,可能會發生改變。開發者須要確保在State發生變化時,調用State.setState通知FrameworkFramenwork收到消息後,會從新調用build方法重構Widget Tree,更新UI。

State生命週期各主要函數以下:

  • initState

Widget第一次插入到Widget Tree時會被調用,對於每個State對象,Flutter Framework只會調用一次該回調,因此,一般在該回調中作一些一次性的操做,如狀態初始化、訂閱子樹的事件通知等。

  • didChangeDependencies

State對象的依賴發生變化時會被調用;其主要場景有兩個:

  1. 其依賴的InheritedWidget對象發生了變化。
  2. 第一次插入Widget Tree中,在initState以後。
  • build

此回調讀者如今應該已經至關熟悉了,它主要是用於構建Widget子樹的,會在以下場景被調用:

  1. 在調用initState以後。
  2. 在調用didUpdateWidget以後。
  3. 在調用setState以後。
  4. 在調用didChangeDependencies以後。
  5. State對象從樹中一個位置移除後(會調用deactivate)又從新插入到樹的其它位置以後。
  • reassemble

此回調是專門爲了開發調試而提供的,在熱重載(hot reload)時會被調用,此回調在Release模式下永遠不會被調用。

  • didUpdateWidget

Widget從新構建時,Flutter Framework會調用Widget.canUpdate來檢測Widget Tree中同一位置的新舊節點,而後決定是否須要更新,若是Widget.canUpdate返回true則會調用此回調。

  • deactivate

State對象從樹中被移除時,會調用此回調。在一些場景下,Flutter Framework會將State對象從新插到樹中,如包含此State對象的子樹在樹的一個位置移動到另外一個位置時(能夠經過GlobalKey來實現)。若是移除後沒有從新插入到樹中則緊接着會調用dispose方法。

  • dispose

State對象從樹中被永久移除時調用;一般在此回調中釋放資源。

State生命週期以下: image.png

7. RenderObjectWidget

RenderObjectWidgetRenderObjectElement提供了配置,是真正負責渲染的Widget。其核心函數以下:

  • createRenderObject

該函數用於建立對應的RenderObject

  • updateRenderObject

該函數用於根據此Widget的配置更新對應的RenderObject。注意此函數不該該更新RenderObject的子節點。

  • didUnmountRenderObject

該函數用於在RenderObject被從樹中移除的時候,清理RenderObject的資源。

8.小結

本文主要介紹了Widget的類型及核心函數,其重點以下:

  • Widget的定義是:描述一個UI元素的配置數據。它並非表示最終繪製在設備上的顯示元素,而只是描述顯示元素的一個配置數據。
  • Widget主要分爲三類:Component WidgetProxy Widget以及Render Widget,其中只有Render Widget纔會參與後面的佈局(layout)和渲染(paint)流程。
  • 一個Widget對象能夠對應多個Element對象。

9. 參考文檔

1. 深刻淺出 Flutter Framework 之 Widget
2. Flutter實戰

10. 相關文章

Flutter框架分析(一)--架構總覽
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject樹
Flutter框架分析(六)-Constraint
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析(八)-Platform Channel
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget

相關文章
相關標籤/搜索