小菜學習 Flutter 有一段時間了,其中 Flutter 的核心思想是 Everything is Widget;但是什麼是 Widget 它與我們常說的 Element 和 RenderObject 有什麼關系呢,小菜就個人了解簡單整理一下;
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 是該樹上的一個對象;
- Widget 描述了 Element 的配置,隻是用來儲存屬性的容器;
- createElement() 用來生成一個 Element 對象并添加到 Widget 樹中;
- toStringShort() 是對該 Widget 的簡短說明,包括 Widget 類型和對應的 Key 等;
- canUpdate() 用來判斷目前 Widget 是否重建,當兩個新舊 runtimeType 和 key 相同時則更新 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 生命周期進行維護;
- created 對象建立,在 initState() 生命周期時執行;
- initialized 對象初始化,在 didChangeDependencies() 過程中執行;initState() 隻是建立了 State 對象但是未初始化完成;
- ready 對象準備好且 dispose() 方法未執行;
- defunct 對象銷毀,在 dispose() 執行時進行銷毀;
2. ProxyWidget
ProxyWidget 作為一個抽象的代理 Widget 并沒有實質性的作用,隻是在父類和子類需要傳遞資訊時使用;主要有 InheritedWidget 和 ParentDataWidget 兩類;
abstract class ProxyWidget extends Widget {
const ProxyWidget({ Key key, @required this.child }) : super(key: key);
final Widget child;
}
使用過 Bloc 或 Provider 等狀态管理的朋友都了解過 InheritedWidget,主要都是對 InheritedWidget 的優化和封裝;可以在樹結構中傳遞資訊,當使用 InheritedWidget 時,子類狀态變更時可以通知父類進行對應的變更;小菜簡單了解為資料上移;
而 ParentDataWidget 與 InheritedWidget 作用方向相反,用于為具有多個子類的 RenderObjectWidget 提供對于的配置等,例如 Stack 使用已定位好的父類 Widget 來定位每個子 Widget;小菜簡單了解為資料下移;
InheritedWidget 和 ParentDataWidget 涉及内容較多,小菜暫不做深入研究;
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 是在 RenderObject 在 Render Tree 中删除時調用;
Key / GlobalKey
Key 可以用來控制在 Widget 重建時是否與其他 Widget 比對,隻有 Key 和 runtimeType 同時一緻才會認為是同一個 Widget;Key 在建構相同類型 Widget 的多個執行個體時很有用,例如 List 清單中多個相同類型的 item,可以提高清單效率;