【Flutter 專題】100 何為 Flutter Widgets ?

阿策小和尚發表於2021-07-09

      小菜學習 Flutter 有一段時間了,其中 Flutter 的核心思想是 Everything is Widget;但是什麼是 Widget 它與我們常說的 ElementRenderObject 有什麼關係呢,小菜就個人理解簡單整理一下;

Widget

原始碼分析

abstract class Widget extends DiagnosticableTree {
  const Widget({ this.key });

  final Key key;

  @protected
  Element createElement();

  @override
  String toStringShort() {
    return key == null ? '$runtimeType' : '$runtimeType-$key';
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
  }

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}
複製程式碼

      簡單分析原始碼和註釋可得,Widget 繼承自 DiagnosticableTree 是該樹上的一個物件;

  1. Widget 描述了 Element 的配置,只是用來儲存屬性的容器;
  2. createElement() 用來生成一個 Element 物件並新增到 Widget 樹中;
  3. toStringShort() 是對該 Widget 的簡短說明,包括 Widget 型別和對應的 Key 等;
  4. canUpdate() 用來判斷當前 Widget 是否重建,當兩個新舊 runtimeTypekey 相同時則更新 Widget 否則會新建一個 Widget 替代老舊的 Widget

子類 Widget

      Widget 主要有三類子類 Widget;分別是組合類 Widget(StatelessWidget/StatefulWidget)、代理類 Widget(ProxyWidget)、渲染類 Widget(RenderObjectWidget);

1. StatelessWidget / StatefulWidget

      StatelessWidget 是狀態不可變的 Widget,主要通過 build() 方法,把一個或多個 Widget 整合成一個新的 Widget;這也完全符合 Flutter 【組合大於繼承】的思想;StatelessWidget 的核心方法就是 build() 方法,把多個 Widget 組合包裝成一個新的 Widget

abstract class StatelessWidget extends Widget {
  const StatelessWidget({ Key key }) : super(key: key);

  @override
  StatelessElement createElement() => StatelessElement(this);
  
  @protected
  Widget build(BuildContext context);
}
複製程式碼

      StatefulWidget 是狀態可變的 Widget,而其核心是 State 狀態管理;常用的 setState(){} 便是用來更新重構 Widget

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);

  @override
  StatefulElement createElement() => StatefulElement(this);

  @protected
  State createState();
}
複製程式碼

      State 用於處理 StatefulWidget 業務邏輯和狀態管理,有 _StateLifecycle 生命週期進行維護;

  1. created 物件建立,在 initState() 生命週期時執行;
  2. initialized 物件初始化,在 didChangeDependencies() 過程中執行;initState() 只是建立了 State 物件但是未初始化完成;
  3. ready 物件準備好且 dispose() 方法未執行;
  4. defunct 物件銷燬,在 dispose() 執行時進行銷燬;
2. ProxyWidget

      ProxyWidget 作為一個抽象的代理 Widget 並沒有實質性的作用,只是在父類和子類需要傳遞資訊時使用;主要有 InheritedWidgetParentDataWidget 兩類;

abstract class ProxyWidget extends Widget {
  const ProxyWidget({ Key key, @required this.child }) : super(key: key);

  final Widget child;
}
複製程式碼

      使用過 BlocProvider 等狀態管理的朋友都瞭解過 InheritedWidget,主要都是對 InheritedWidget 的優化和封裝;可以在樹結構中傳遞資訊,當使用 InheritedWidget 時,子類狀態變更時可以通知父類進行對應的變更;小菜簡單理解為資料上移;

      而 ParentDataWidgetInheritedWidget 作用方向相反,用於為具有多個子類的 RenderObjectWidget 提供對於的配置等,例如 Stack 使用已定位好的父類 Widget 來定位每個子 Widget;小菜簡單理解為資料下移;

      InheritedWidgetParentDataWidget 涉及內容較多,小菜暫不做深入研究;

3. RenderObjectWidget

      RenderObjectWidget 是真正用於繪製渲染 Widget,一切在螢幕上展示的 Widget 根本上都離不開 RenderObjectWidget;它提供了 RenderObjectElement 的建立配置和 RenderObject 渲染物件的規定,提供了應用程式的實際渲染;

abstract class RenderObjectWidget extends Widget {
  const RenderObjectWidget({ Key key }) : super(key: key);

  @override
  RenderObjectElement createElement();

  @protected
  RenderObject createRenderObject(BuildContext context);

  @protected
  void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
  
  @protected
  void didUnmountRenderObject(covariant RenderObject renderObject) { }
}
複製程式碼

      updateRenderObject 是在 Widget 更新後,修改對應的 RenderObject 物件,在每次更新時都會呼叫;didUnmountRenderObject 是在 RenderObjectRender Tree 中刪除時呼叫;

Key / GlobalKey

      Key 可以用來控制在 Widget 重建時是否與其他 Widget 匹配,只有 KeyruntimeType 同時一致才會認為是同一個 WidgetKey 在構建相同型別 Widget 的多個例項時很有用,例如 List 列表中多個相同型別的 item,可以提高列表效率;

      GlobalKey 可以作為應用全域性唯一標識,在整個 Widget 層級中都是唯一的,可以使用 GlobalKey 來檢索與 Widget 關聯的狀態;


      WidgetElementRenderObject 都是密不可分的,之後進一步學習 ElementRenderObject;小菜對底層的研究還不夠深入;如有錯誤,請多多指導!

來源: 阿策小和尚

相關文章