Flutter 檢視體系概述

Cyandev發表於2018-04-15

之前幾篇文章都提過 Flutter 的思想十分類似 React,通過 Widget Tree 來表示整個 app 的檢視。一般來講,常規 app 的開發中我們只需要與 和它的子類打交道,然而 Widget 並不是我們真實看到的檢視,本文就來簡單介紹一下 Flutter 檢視系統背後的細節。

首先先擺 3 個關鍵的概念:WidgetElementRenderObject

Widget

為了解釋 Widget,我們先看看官方是如何定義它的:

Describes the configuration for an Element.

Widgets are the central class hierarchy in the Flutter framework. A widget is an immutable description of part of a user interface. Widgets can be inflated into elements, which manage the underlying render tree.

可以看到,Widget 的實際工作也就是描述如何建立 ElementWidget 是一個不可變物件,它可以被複用,請注意,這裡的複用不是指在兩次渲染的時候將物件從舊樹中拿過來放到新樹,而是在同一個 Widget Tree 中,某個子 Widget 可以出現多次,因為它只是一個 description。

在一次渲染中,Flutter Framework 會呼叫 Widget 的 方法,這個方法會建立一個新的對應的 物件並返回。所以即使 Widget 被重複使用,框架還是會建立多個不同的 Element 物件。 到這裡,Widget 的工作就暫告一段落了。

Element

ElementWidget 的例項體現,上面說過 Widget 可以重複使用,但是 Flutter Framework 仍然會對這幾個相同的 Widget 依次建立幾個全新的 Element。 一次渲染會生成一個 Element Tree 並放在記憶體中,在下次渲染時 Flutter Framework 會用嘗試用新的 Widget 去更新舊的 Element,此時 Element 被複用,這裡的複用是指不再建立新的 Element 的物件了,但每個相同的 Widget 還是各自對應了一個不同的 Element(好像有點繞,不知道大家能不能理解 -。-)。

那麼 Element 到底是做什麼的呢,概括地說就是儲存了一個樹形結構以便更新時做 diff、patch 和管理元件生命週期用的,由於 Widget 都是沒有狀態的,如果你想修改 Widget 的某一屬性就必須要重新建立一遍 Widget,而 StatefulWidget 內部包含 State,如果重新建立了狀態就會丟失,所以必須有一個地方來儲存這些暫時的狀態。

StatefulWidget 來舉例說明吧。 第一次渲染時,StatefulWidget 通過 createElement 建立出一個 StatefulElement,然後我們來看 StatefulElement 的構造方法:

StatefulElement(StatefulWidget widget)
    : _state = widget.createState(), super(widget) {
    assert(() {
      if (!_state._debugTypesAreRight(widget)) {
        throw new FlutterError(
          'StatefulWidget.createState must return a subtype of State<${widget.runtimeType}>\n'
          'The createState function for ${widget.runtimeType} returned a state '
          'of type ${_state.runtimeType}, which is not a subtype of '
          'State<${widget.runtimeType}>, violating the contract for createState.'
        );
      }
      return true;
    }());
    assert(_state._element == null);
    _state._element = this;
    assert(_state._widget == null);
    _state._widget = widget;
    assert(_state._debugLifecycleState == _StateLifecycle.created);
}
複製程式碼

可以看到,StatefulElement 內部會負責建立和儲存 State,這樣就是為什麼 StatefulWidget 被重新建立了而內部的狀態不會丟失的原因。

然後在 Widget Tree 發生變化的時候,Flutter Framework 通過 Elementupdate 來根據新 Widget 更新舊 Element

@override
void update(StatefulWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    final StatefulWidget oldWidget = _state._widget;
    // Notice that we mark ourselves as dirty before calling didUpdateWidget to
    // let authors call setState from within didUpdateWidget without triggering
    // asserts.
    _dirty = true;
    _state._widget = widget;
    try {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
      _state.didUpdateWidget(oldWidget);
    } finally {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
    }
    rebuild();
}
複製程式碼

所以如果你去看官方文件對 Element 的介紹,就會發現它就是負責狀態和生命週期管理的物件,實際上很少需要去自己實現 Element

RenderObject

這個東西通過名字就能知道,它就是負責檢視渲染的。實際上,所有的佈局、繪製和事件響應全都由它負責,開發複雜檢視時我們可能經常需要與之打交道。

而它又是由 Element 的子類 RenderObjectElement 建立出來的,RenderObject 也會構成一個 Render Tree,並且每個 RenderObject 也都會被儲存下來以便在更新時複用。

通常我們也不直接 subclass RenderObject 本身,而是它的子類 RenderBoxRenderBox 幫我們實現了很多基礎設施,然而它並不能容納子 RenderObject,想要實現一個容器,我們需要使用 RenderObjectWithChildMixin<ChildType>mixin,它們提供了對 children 的支援。

由於 RenderObject 本身又涉及很多細節,所以本文不再深入探討。


以上就是 Flutter 檢視系統對外大致的結構,希望對大家學習 Flutter 開發有所幫助~

References

  1. Widget class
  2. Element class
  3. RenderObject class

相關文章