詳解Flutter Widget
概述:
所有的一切都可以被稱為widget
在開發(fā) Flutter 應(yīng)用過(guò)程中,接觸最多的無(wú)疑就是Widget,是『描述』 Flutter UI 的基本單元,通過(guò)Widget可以做到:
- 描述 UI 的層級(jí)結(jié)構(gòu) (通過(guò)
Widget嵌套); - 定制 UI 的具體樣式 (如:
font、color等); - 指導(dǎo) UI 的布局過(guò)程 (如:
padding、center等); - …
Google 在設(shè)計(jì)Widget時(shí),還賦予它一些鮮明的特點(diǎn):
- 聲明式 UI —— 相對(duì)于傳統(tǒng) Native 開發(fā)中的命令式 UI,聲明式 UI 有不少優(yōu)勢(shì),如:開發(fā)效率顯著提升、UI 可維護(hù)性明顯加強(qiáng)等;
- 不可變性 —— Flutter 中所有
Widget都是不可變的(immutable),即其內(nèi)部成員都是不可變的(final),對(duì)于變化的部分需要通過(guò)「Stateful Widget-State」的方式實(shí)現(xiàn); - 組合大于繼承 ——
Widget設(shè)計(jì)遵循組合大于繼承這一優(yōu)秀的設(shè)計(jì)理念,通過(guò)將多個(gè)功能相對(duì)單一的Widget組合起來(lái)便可得到功能相對(duì)復(fù)雜的Widget。
Widget的本質(zhì):
在Widget源碼中有這樣一段注釋:

這段注釋闡明了Widget的本質(zhì):用于配置Element的,Widget本質(zhì)上是 UI 的配置信息 (附帶部分業(yè)務(wù)邏輯)。
我們通常會(huì)將通過(guò)
Widget描述的 UI 層級(jí)結(jié)構(gòu)稱之為「Widget Tree」,但與「Element Tree」、「RenderObject Tree」以及「Layer Tree」相比,實(shí)質(zhì)上并不存在「Widget Tree」。為了描述方便,將 Widget 組合描述的 UI 層級(jí)結(jié)構(gòu)稱之為「Widget Tree」,也未嘗不可。
分類:

Widget
Widget,所有 Widget 的基類。

如上圖所示,在 Widget基類中有 3 個(gè)重要的方法 (屬性):
- Key key —— 在同一父節(jié)點(diǎn)下,用作兄弟節(jié)點(diǎn)間的唯一標(biāo)識(shí),主要用于控制當(dāng) Widget 更新時(shí),對(duì)應(yīng)的 Element 如何處理 (是更新還是新建)。若某 Widget 是其「Parent Widget」唯一的子節(jié)點(diǎn)時(shí),一般不用設(shè)置 key;
GlobalKey 是一類較特殊的 key,在介紹 Element 時(shí)會(huì)附帶介紹。
- Element createElement() —— 每個(gè)
Widget都有一個(gè)與之對(duì)應(yīng)的Element,由該方法負(fù)責(zé)創(chuàng)建,createElement可以理解為設(shè)計(jì)模式中的工廠方法,具體的Element類型由對(duì)應(yīng)的Widget子類負(fù)責(zé)創(chuàng)建; - static bool canUpdate(Widget oldWidget, Widget newWidget) —— 是否可以用 new widget 修改前一幀用 old widget 生成的 Element,而不是創(chuàng)建新的 Element,
Widget類的默認(rèn)實(shí)現(xiàn)為:2個(gè)Widget的runtimeType與key都相等時(shí),返回true,即可以直接更新 (key 為 null 時(shí),認(rèn)為相等)。
上述更新流程,同樣在介紹 Element 時(shí)會(huì)重點(diǎn)分析。
StatelessWidget
無(wú)狀態(tài)-組合型 Widget,由其build方法描述組合 UI 的層級(jí)結(jié)構(gòu)。在其生命周期內(nèi)狀態(tài)不可變。
/// A widget that does not require mutable state. /// /// A stateless widget is a widget that describes part of the user interface by /// building a constellation of other widgets that describe the user interface /// more concretely. The building process continues recursively until the /// description of the user interface is fully concrete (e.g., consists /// entirely of [RenderObjectWidget]s, which describe concrete [RenderObject]s).
具體是兩個(gè)方法:
- StatelessElement createElement() ——「Stateless Widget」對(duì)應(yīng)的 Element 為
StatelessElement,一般情況下StatelessWidget子類不必重寫該方法,即子類對(duì)應(yīng)的 Element 也是StatelessElement;
@override StatelessElement createElement() => StatelessElement(this);
- Widget build(BuildContext context) —— 算是 Flutter 體系中的核心方法之一
@protected Widget build(BuildContext context);
以『聲明式 UI』的形式描述了該組合式 Widget 的 UI 層級(jí)結(jié)構(gòu)及樣式信息,也是開發(fā) Flutter 應(yīng)用的主要工作『場(chǎng)所』。該方法在 3 種情況下被調(diào)用:
- Widget 第一次被加入到 Widget Tree 中 (更準(zhǔn)確地說(shuō)是其對(duì)應(yīng)的 Element 被加入到 Element Tree 時(shí),即 Element 被掛載『mount』時(shí));
- 「Parent Widget」修改了其配置信息;
- 該 Widget 依賴的「Inherited Widget」發(fā)生變化時(shí)。
當(dāng)「Parent Widget」或 依賴的「Inherited Widget」頻繁變化時(shí),build方法也會(huì)頻繁被調(diào)用。因此,提升build方法的性能就顯得十分重要,F(xiàn)lutter 官方給出了幾點(diǎn)建議:
- *減少不必要的中間節(jié)點(diǎn),即減少 UI 的層級(jí),*如:對(duì)于「Single Child Widget」,沒(méi)必要通過(guò)組合「Row」、「Column」、「Padding」、「SizedBox」等復(fù)雜的 Widget 達(dá)到某種布局的目標(biāo),或許通過(guò)簡(jiǎn)單的「Align」、「CustomSingleChildLayout」即可實(shí)現(xiàn)。又或者,為了實(shí)現(xiàn)某種復(fù)雜精細(xì)的 UI 效果,不一定要通過(guò)組合多個(gè)「Container」,再附加「Decoration」來(lái)實(shí)現(xiàn),通過(guò) 「CustomPaint」自定義或許是更好的選擇;
- *盡可能使用
constWidget,*為 Widget 提供const構(gòu)造方法;
關(guān)于 const constructor 可以看看我這篇文章。
- 必要時(shí),*可以將「Stateless Widget」重構(gòu)成「Stateful Widget」,*以便可以使用「Stateful Widget」中一些特定的優(yōu)化手法,如:緩存「sub trees」的公共部分,并在改變樹結(jié)構(gòu)時(shí)使用
GlobalKey; - *盡量減小 rebuilt 范圍,*如:某個(gè) Widget 因使用了「Inherited Widget」,導(dǎo)致頻繁 rebuilt,可以將真正依賴「Inherited Widget」的部分提取出來(lái),封裝成更小的獨(dú)立 Widget,并盡量將該獨(dú)立 Widget 推向樹的葉子節(jié)點(diǎn),以便減小 rebuilt 時(shí)受影響的范圍。
StatefulWidget
有狀態(tài)-組合型 Widget,但要注意的是StatefulWidget本身還是不可變的,其可變狀態(tài)存在于State中。
/// A widget that has mutable state. /// /// State is information that (1) can be read synchronously when the widget is /// built and (2) might change during the lifetime of the widget. It is the /// responsibility of the widget implementer to ensure that the [State] is /// promptly notified when such state changes, using [State.setState].
具體有兩個(gè)方法:
- StatefulElement createElement() ——「Stateful Widget」對(duì)應(yīng)的 Element 為
StatefulElement,一般情況下StatefulWidget子類不用重寫該方法,即子類對(duì)應(yīng)的Element 也是StatefulElement;
@override StatefulElement createElement() => StatefulElement(this);
- State createState() —— 創(chuàng)建對(duì)應(yīng)的 State,該方法在
StatefulElement的構(gòu)造方法中被調(diào)用。可以簡(jiǎn)單地理解為當(dāng)「Stateful Widget」被添加到 Widget Tree 時(shí)會(huì)調(diào)用該方法。
@protected @factory State createState(); // ignore: no_logic_in_create_state, this is the original sin
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
_state._element = this;
_state._widget = widget;
}
實(shí)際上是「Stateful Widget」對(duì)應(yīng)的「Stateful Element」被添加到 Element Tree 時(shí),伴隨「Stateful Element」的初始化,createState方法被調(diào)用。從后文可知一個(gè) Widget 實(shí)例可以對(duì)應(yīng)多個(gè) Element 實(shí)例 (也就是同一份配置信息 (Widget) 可以在 Element Tree 上不同位置配置多個(gè) Element 節(jié)點(diǎn)),因此,createState方法在「Stateful Widget」生命周期內(nèi)可能會(huì)被調(diào)用多次。
另外,需要注意的是配有GlobalKey的 Widget 對(duì)應(yīng)的 Element 在整個(gè) Element Tree 中只有一個(gè)實(shí)例。
State
有狀態(tài)小部件 的邏輯和Stateful Widget
State 用于處理「Stateful Widget」的業(yè)務(wù)邏輯以及可變狀態(tài)
由于其內(nèi)部狀態(tài)是可變的,故 State 有較復(fù)雜的生命周期:

如上圖,State 的生命周期大致可以分為 8 個(gè)階段:
- 在對(duì)應(yīng)的「Stateful Element」被掛載 (mount) 到樹上時(shí),通過(guò)
StatefulElement.constructor–>StatefulWidget.createState創(chuàng)建 State 實(shí)例;
從
StatefulElement.constructor中的_state._element = this;可知,State._emelent指向了對(duì)應(yīng)的 Element 實(shí)例,而我們熟知的State.context引用的就是這個(gè)_element:BuildContext get context => _element;。
State實(shí)例與Element實(shí)例間的綁定關(guān)系一經(jīng)確定,在整個(gè)生命周期內(nèi)不會(huì)再變了 (Element 對(duì)應(yīng)的 Widget 可能會(huì)變,但對(duì)應(yīng)的 State 永遠(yuǎn)不會(huì)變),期間,Element可以在樹上移動(dòng),但上述關(guān)系不會(huì)變 (即「Stateful Element」是帶著狀態(tài)移動(dòng)的)。
- StatefulElement 在掛載過(guò)程中接著會(huì)調(diào)用
State.initState,子類可以重寫該方法執(zhí)行相關(guān)的初始化操作 (此時(shí)可以引用context、widget屬性); - 同樣在掛載過(guò)程中會(huì)調(diào)用
State.didChangeDependencies,該方法在 State 依賴的對(duì)象 (如:「Inherited Widget」) 狀態(tài)發(fā)生變化時(shí)也會(huì)被調(diào)用,*子類很少需要重寫該方法,*除非有非常耗時(shí)不宜在build中進(jìn)行的操作,因?yàn)樵谝蕾囉凶兓瘯r(shí)build方法也會(huì)被調(diào)用; - 此時(shí),State 初始化已完成,其
build方法此后可能會(huì)被多次調(diào)用,在狀態(tài)變化時(shí) State 可通過(guò)setState方法來(lái)觸發(fā)其子樹的重建; - 此時(shí),「element tree」、「renderobject tree」、「layer tree」已構(gòu)建完成,完整的 UI 應(yīng)該已呈現(xiàn)出來(lái)。此后因?yàn)樽兓竐lement tree」中「parent element」可能會(huì)對(duì)樹上該位置的節(jié)點(diǎn)用新配置 (Widget) 進(jìn)行重建,當(dāng)新老配置 (oldWidget、newWidget)具有相同的「runtimeType」&&「key」時(shí),framework 會(huì)用 newWidget 替換 oldWidget,并觸發(fā)一系列的更新操作 (在子樹上遞歸進(jìn)行)。同時(shí),
State.didUpdateWidget方法被調(diào)用,子類重寫該方法去響應(yīng) Widget 的變化;
上述 3 棵樹以及更新流程在后續(xù)文章中會(huì)有詳細(xì)介紹
- 在 UI 更新過(guò)程中,任何節(jié)點(diǎn)都有被移除的可能,State 也會(huì)隨之移除,(如上一步中「runtimeType」||「key」不相等時(shí))。此時(shí)會(huì)調(diào)用
State.deactivate方法,由于被移除的節(jié)點(diǎn)可能會(huì)被重新插入樹中某個(gè)新的位置上,故子類重寫該方法以清理與節(jié)點(diǎn)位置相關(guān)的信息 (如:該 State 對(duì)其他 element 的引用)、同時(shí),不應(yīng)在該方法中做資源清理;
重新插入操作必須在當(dāng)前幀動(dòng)畫結(jié)束之前
- 當(dāng)節(jié)點(diǎn)被重新插入樹中時(shí),
State.build方法被再次調(diào)用; - 對(duì)于在當(dāng)前幀動(dòng)畫結(jié)束時(shí)尚未被重新插入的節(jié)點(diǎn),
State.dispose方法被執(zhí)行,State 生命周期隨之結(jié)束,此后再調(diào)用State.setState方法將報(bào)錯(cuò)。子類重寫該方法以釋放任何占用的資源。

至此,State 中的核心方法基本都已在上述過(guò)程中介紹了,下面重點(diǎn)看一下setState方法:
void setState(VoidCallback fn) {
assert(fn != null);
assert(() {
if (_debugLifecycleState == _StateLifecycle.defunct) {
throw FlutterError.fromParts(<DiagnosticsNode>[...]);
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
throw FlutterError.fromParts(<DiagnosticsNode>[...]);
}
return true;
}());
final dynamic result = fn() as dynamic;
assert(() {
if (result is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[...]);
}
return true;
}());
_element.markNeedsBuild();
}
- 從上述源碼可以看到,關(guān)于
setState方法有幾點(diǎn)值得關(guān)注: - 在
State.dispose后不能調(diào)用setState(第 4 行); - 在 State 的構(gòu)造方法中不能調(diào)用
setState(第 7 行); setState方法的回調(diào)函數(shù) (fn) 不能是異步的 (返回值為Future),原因很簡(jiǎn)單,因?yàn)閺牧鞒淘O(shè)計(jì)上 framework 需要根據(jù)回調(diào)函數(shù)產(chǎn)生的新狀態(tài)去刷新 UI (第 14 行);- 通過(guò)
setState方法之所以能更新 UI,是在其內(nèi)部調(diào)用_element.markNeedsBuild()實(shí)現(xiàn)的 (具體過(guò)程在介紹 Element 時(shí)再詳細(xì)分析)。
關(guān)于 State 最后再?gòu)?qiáng)調(diào) 2 點(diǎn):
- 若State.build方法依賴了自身狀態(tài)會(huì)變化的對(duì)象,如:ChangeNotifier、Stream或其他可以被訂閱的對(duì)象,需要確保在initState、didUpdateWidget、dispose
等 3 方法間有正確的訂閱 (subscribe) 與取消訂閱 (unsubscribe) 的操作:
在initState中執(zhí)行 subscribe;
如果關(guān)聯(lián)的「Stateful Widget」與訂閱有關(guān),在didUpdateWidget中先取消舊的訂閱,再執(zhí)行新的訂閱;
在dispose中執(zhí)行 unsubscribe。
- 在
State.initState方法中不能調(diào)用BuildContext.dependOnInheritedWidgetOfExactType,但State.didChangeDependencies會(huì)隨之執(zhí)行,在該方法中可以調(diào)用。
ParentDataWidget
ParentDataWidget以及下面要介紹的InheritedElement都繼承自ProxyWidget,由于ProxyWidget作為抽象基類本身沒(méi)有任何功能,故下面直接介紹ParentDataWidget、InheritedElement。
/// Base class for widgets that hook [ParentData] information to children of/// [RenderObjectWidget]s.
ParentDataWidget作為 Proxy 型 Widget,其功能主要是為其他 Widget 提供ParentData信息。雖然其 child widget 不一定是 RenderObejctWidget 類型,但其提供的ParentData信息最終都會(huì)落地到 RenderObejctWidget 類型子孫 Widget 上。
ParentData 是『parent renderobject』在 layout『child renderobject』時(shí)使用的輔助定位信息,詳細(xì)信息會(huì)在介紹 RenderObject 時(shí)介紹。
void attachRenderObject(dynamic newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
final ParentDataElement<RenderObjectWidget> parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
ParentDataElement<RenderObjectWidget> _findAncestorParentDataElement() {
Element ancestor = _parent;
while (ancestor != null && ancestor is! RenderObjectElement)
{
if (ancestor is ParentDataElement<RenderObjectWidget>)
return ancestor;
ancestor = ancestor._parent;
}
return null;
}
void _updateParentData(ParentDataWidget<RenderObjectWidget> parentData)
{
parentData.applyParentData(renderObject);
}
上面這段代碼來(lái)自RenderObjectElement,可以看到在其attachRenderObject方法第 6 行從祖先節(jié)點(diǎn)找ParentDataElement,如果找到就用其 Widget(ParentDataWidget) 中的 parentData 信息去設(shè)置 Render Obejct。在查找過(guò)程中如查到RenderObjectElement (第 13 行),說(shuō)明當(dāng)前 RenderObject 沒(méi)有 Parent Data 信息。
最終會(huì)調(diào)用到ParentDataWidget.applyParentData(RenderObject renderObject),子類需要重寫該方法,以便設(shè)置對(duì)應(yīng)RenderObject.parentData。
來(lái)看個(gè)例子,通常配合Stack使用的Positioned(繼承自ParentDataWidget):
void applyParentData(RenderObject renderObject) {
assert(renderObject.parentData is StackParentData);
final StackParentData parentData = renderObject.parentData;
bool needsLayout = false;
if (parentData.left != left)
{
parentData.left = left;
needsLayout = true;
}
... if (parentData.width != width)
{
parentData.width = width;
needsLayout = true;
}
... if (needsLayout) {
final AbstractNode targetParent = renderObject.parent;
if (targetParent is RenderObject)
targetParent.markNeedsLayout();
}
}
可以看到,Positioned在必要時(shí)將自己的屬性賦值給了對(duì)應(yīng)的RenderObject.parentData (此處是StackParentData),并對(duì)「parent render object」調(diào)用markNeedsLayout(第 19 行),以便重新 layout,畢竟修改了布局相關(guān)的信息。
abstract class ParentDataWidget<T extends RenderObjectWidget> extends ProxyWidget
如上所示,ParentDataWidget在定義上使用了泛型<T extends RenderObjectWidget>,其背后的含義是:
從當(dāng)前ParentDataWidget節(jié)點(diǎn)向上追溯形成的祖先節(jié)點(diǎn)鏈(『parent widget chain』)上,在 2 個(gè)ParentDataWidget類型的節(jié)點(diǎn)形成的鏈上至少要有一個(gè)『RenderObject Widget』類型的節(jié)點(diǎn)。因?yàn)橐粋€(gè)『RenderObject Widget』不能接受來(lái)自 2 個(gè)及以上『ParentData Widget』的信息。
/// Base class for widgets that efficiently propagate information down the tree. // To obtain the nearest instance of a particular type of inherited widget from ///a build context, use [BuildContext.dependOnInheritedWidgetOfExactType].
InheritedWidget 用于在樹上向下傳遞數(shù)據(jù)。
通過(guò)BuildContext.dependOnInheritedWidgetOfExactType可以獲取最近的「Inherited Widget」,需要注意的是通過(guò)這種方式獲取「Inherited Widget」時(shí),當(dāng)「Inherited Widget」?fàn)顟B(tài)有變化時(shí),會(huì)導(dǎo)致該引用方 rebuild。
具體原理在介紹 Element 時(shí)會(huì)詳細(xì)分析。
通常,為了使用方便會(huì)「Inherited Widget」會(huì)提供靜態(tài)方法of,在該方法中調(diào)用BuildContext.dependOnInheritedWidgetOfExactType。of方法可以直接返回「Inherited Widget」,也可以是具體的數(shù)據(jù)。
有時(shí),「Inherited Widget」是作為另一個(gè)類的實(shí)現(xiàn)細(xì)節(jié)而存在的,其本身是私有的(外部不可見),此時(shí)of方法就會(huì)放到對(duì)外公開的類上。最典型的例子就是Theme,其本身是StatelessWidget類型,但其內(nèi)部創(chuàng)建了一個(gè)「Inherited Widget」:_InheritedTheme,of方法就定義在上Theme上:
static MediaQueryData of(BuildContext context, {
bool nullOk = false })
{
final MediaQuery query = context.dependOnInheritedWidgetOfExactType<MediaQuery>();
if (query != null)
return query.data;
if (nullOk) return null;
}
該of方法返回的是ThemeData類型的具體數(shù)據(jù),并在其內(nèi)部首先調(diào)用了BuildContext.dependOnInheritedWidgetOfExactType。
我們經(jīng)常使用的「Inherited Widget」莫過(guò)于MediaQuery,同樣提供了of方法:

- InheritedElement createElement() ——「Inherited Widget」對(duì)應(yīng)的 Element 為
InheritedElement,一般情況下InheritedElement子類不用重寫該方法; - bool updateShouldNotify(covariant InheritedWidget oldWidget) —— 在「Inherited Widget」rebuilt 時(shí)判斷是否需要 rebuilt 那些依賴它的 Widget;
如下是MediaQuery.updateShouldNotify的實(shí)現(xiàn),在新老Widget.data 不相等時(shí)才 rebuilt 那依賴的 Widget。
bool updateShouldNotify(MediaQuery oldWidget) => data != oldWidget.data;
RenderObjectWidget
真正與渲染相關(guān)的 Widget,屬于最核心的類型,一切其他類型的 Widget 要渲染到屏幕上,最終都要回歸到該類型的 Widget 上。
- RenderObjectElement createElement() ——「RenderObject Widget」對(duì)應(yīng)的 Element 為
RenderObjectElement,由于RenderObjectElement也是抽象類,故子類需要重寫該方法; - RenderObject createRenderObject(BuildContext context) —— 核心方法,創(chuàng)建 Render Widget 對(duì)應(yīng)的 Render Object,同樣子類需要重寫該方法。該方法在對(duì)應(yīng)的 Element 被掛載到樹上時(shí)調(diào)用(
Element.mount),即在 Element 掛載過(guò)程中同步構(gòu)建了「Render Tree」(詳細(xì)過(guò)程后續(xù)文章會(huì)詳細(xì)分析);
@overrideRenderFlex createRenderObject(BuildContext context) {
return RenderFlex(
direction: direction,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: getEffectiveTextDirection(context),
verticalDirection: verticalDirection,
textBaseline: textBaseline,
)
;
}
上面是Flex.createRenderObject的源碼,真實(shí)感受一下 (還是代碼更有感覺(jué))。可以看到,用Flex的信息(配置)初始化了RenderFlex。
Flex是Row、Column的基類,RenderFlex繼承自RenderBox,后者繼續(xù)自RenderObject。
- void updateRenderObject(BuildContext context, covariant RenderObject renderObject) —— 核心方法,在 Widget 更新后,修改對(duì)應(yīng)的 Render Object。該方法在首次 build 以及需要更新 Widget 時(shí)都會(huì)調(diào)用;
@overridevoid updateRenderObject(BuildContext context, covariant RenderFlex renderObject)
{
renderObject
..
direction = direction
..mainAxisAlignment = mainAxisAlignment
..
mainAxisSize = mainAxisSize
..
crossAxisAlignment = crossAxisAlignment
..
textDirection = getEffectiveTextDirection(context)
..
verticalDirection = verticalDirection
..
textBaseline = textBaseline;
}
Flex.updateRenderObject的源碼也很簡(jiǎn)單,與Flex.createRenderObject幾乎一一對(duì)應(yīng),用當(dāng)前Flex的信息修改renderObject。
- void didUnmountRenderObject(covariant RenderObject renderObject) —— 對(duì)應(yīng)的「Render Object」從「Render Tree」上移除時(shí)調(diào)用該方法。
RenderObjectWidget的幾個(gè)子類:LeafRenderObjectWidget、SingleChildRenderObjectWidget、MultiChildRenderObjectWidget只是重寫了createElement方法以便返回各自對(duì)應(yīng)的具體的 Element 類實(shí)例。
小結(jié)
至此,重要的基礎(chǔ)型 Widget 基本介紹完了,總結(jié)一下:
- Widget 本質(zhì)上是 UI 的配置信息 (附加部分業(yè)務(wù)邏輯),并不存在一顆真實(shí)的「Widget Tree」(與「Element Tree」、「RenderObject Tree」以及「Layer Tree」相比);
- Widget 從功能上可以分為 3 類:「Component Widget」、「Proxy Widget」以及「Renderer Widget」;
- Widget 與 Element 一一對(duì)應(yīng),Widget 提供創(chuàng)建 Element 的方法 (
createElement,本質(zhì)上是一個(gè)工廠方法); - 只有「Renderer Widget」才會(huì)參與最終的 UI 生成過(guò)程(Layout、Paint),只有該類型的 Widget 才有與之對(duì)應(yīng)的「Render Object」,同樣由其提供創(chuàng)建方法(
createRenderObject)。
到此這篇關(guān)于詳解Flutter Widget的文章就介紹到這了,更多相關(guān)Flutter Widget內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
五分鐘教你Android-Kotlin項(xiàng)目編寫
本篇文章主要介紹了五分鐘教你Android-Kotlin項(xiàng)目編寫,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
Android 實(shí)現(xiàn)錨點(diǎn)定位思路詳解
本篇文章就使用tablayout、scrollview來(lái)實(shí)現(xiàn)android錨點(diǎn)定位的功能。通過(guò)<a href="#head" rel="external nofollow" > 去設(shè)置頁(yè)面內(nèi)錨點(diǎn)定位跳轉(zhuǎn)。具體實(shí)現(xiàn)思路大家跟隨腳本之家小編一起通過(guò)本文看下吧2018-07-07
Android仿知乎懸浮功能按鈕FloatingActionButton效果
前段時(shí)間在看屬性動(dòng)畫,恰巧這個(gè)按鈕的效果可以用屬性動(dòng)畫實(shí)現(xiàn),下面通過(guò)本文給大家分享adroid仿知乎懸浮功能按鈕FloatingActionButton效果,需要的朋友參考下吧2017-04-04
Android?Camera1實(shí)現(xiàn)預(yù)覽框顯示
這篇文章主要為大家詳細(xì)介紹了Android?Camera1實(shí)現(xiàn)預(yù)覽框顯示,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
Android自定義LinearLayout布局顯示不完整的解決方法
這篇文章主要給大家介紹了關(guān)于Android自定義LinearLayout但布局顯示不完整的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
Android封裝的http請(qǐng)求實(shí)用工具類
提供一個(gè)Android封裝的http請(qǐng)求實(shí)用工具類,在做ANDROID網(wǎng)絡(luò)開發(fā)中這個(gè)經(jīng)常要用到,大家可以參考下面的工具類修改成自己的工具2013-11-11
Android應(yīng)用中圖片瀏覽時(shí)實(shí)現(xiàn)自動(dòng)切換功能的方法詳解
這篇文章主要介紹了Android應(yīng)用中圖片瀏覽時(shí)實(shí)現(xiàn)自動(dòng)切換功能的方法,文中還講解了一個(gè)觸摸大圖進(jìn)行圖片切換的深入功能,需要的朋友可以參考下2016-04-04

