国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Flutter源碼分析筆記:Widget類源碼分析

這篇具有很好參考價(jià)值的文章主要介紹了Flutter源碼分析筆記:Widget類源碼分析。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

Flutter源碼分析筆記
Widget類源碼分析

- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at: https://jclee95.blog.csdn.net
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/132259681

【介紹】:本文記錄閱讀與分析Flutter源碼 - Widget類源碼分析。


1. 概述

Widget類是Flutter框架中的核心類之一,用于描述用戶界面的一部分。它是一個(gè)不可變的描述,可以被實(shí)例化為元素(Element),后者負(fù)責(zé)管理底層的渲染樹。

Widget是Flutter框架的基礎(chǔ),用于描述用戶界面的一部分,不可變且無(wú)狀態(tài)。它有一些用于實(shí)例化元素、診斷調(diào)試和比較的方法和屬性,同時(shí)它的子類可以是StatelessWidget或StatefulWidget,用于構(gòu)建靜態(tài)或有狀態(tài)的用戶界面部分。

這部分源碼見附錄F-1。

2. 屬性

2.1 key

key屬性控制一個(gè)widget如何替換樹中的另一個(gè)widget。如果兩個(gè)widget的runtimeType和key屬性分別通過(guò)operator==比較是相等的,那么新的widget將通過(guò)更新底層元素來(lái)替換舊的widget。否則,舊的元素會(huì)從樹中移除,新的widget會(huì)被實(shí)例化為元素,然后插入樹中。

3. 方法

3.1 createElement()

該方法將配置實(shí)例化為一個(gè)具體的元素(Element)。一個(gè)widget可以在樹中被包含零次或多次,每次被包含在樹中時(shí),都會(huì)被實(shí)例化為一個(gè)元素。

3.2 canUpdate(Widget oldWidget, Widget newWidget)

該方法用于判斷一個(gè)新的widget是否可以用來(lái)更新一個(gè)已有元素的配置。根據(jù)runtimeType和key屬性的比較來(lái)判斷兩個(gè)widget是否可以更新。

3.3 debugFillProperties(DiagnosticPropertiesBuilder properties)

debugFillProperties(DiagnosticPropertiesBuilder properties):向診斷信息屬性構(gòu)建器添加屬性,用于調(diào)試和診斷。在子類中可以重寫以提供更多的調(diào)試信息。

3.4 _debugConcreteSubtype(Widget widget)

返回一個(gè)數(shù)值,表示特定Widget子類型的具體編碼。用于在熱重載時(shí)判斷已掛載元素的配置是否被修改。

3.5 其它

operator ==(Object other) 和 get hashCode

實(shí)現(xiàn)運(yùn)算符”==“用于判斷兩個(gè)widget是否相等,以及計(jì)算它們的哈希值。

toStringShort()

返回此widget的簡(jiǎn)短文本描述,通常是它的runtimeType和key的組合。

4. 繼承關(guān)系

4.1 Widget的父類

Widget 是一個(gè)抽象類,繼承自 DiagnosticableTree(這個(gè)類表示診斷樹,主要用于提供調(diào)試信息),因此繼承了一些用于調(diào)試和診斷的方法和屬性。

4.2 Widget的子類

4.2.1 StatefulWidget 和 StatelessWidget

Widget 本身沒有可變狀態(tài),它的所有字段都必須是 final。如果需要關(guān)聯(lián)可變狀態(tài),應(yīng)該使用 StatefulWidget,后者在被實(shí)例化為元素并添加到樹中時(shí)會(huì)創(chuàng)建一個(gè)State對(duì)象。Widget的子類可以是 StatelessWidget(始終以相同的方式構(gòu)建)或 StatefulWidget(可以在其生命周期內(nèi)多次構(gòu)建)。

Flutter源碼分析筆記:Widget類源碼分析,Dart語(yǔ)言與Flutter框架開發(fā)筆記,Flutter,Flutter源碼,Dart
StatefulWidget: 這是一個(gè)帶有可變狀態(tài)的 Widget 類別。它由兩部分組成:一個(gè)是不可變的描述部分(Widget),另一個(gè)是可變的狀態(tài)部分(State)。StatefulWidget 實(shí)例在構(gòu)建過(guò)程中可以改變其狀態(tài),當(dāng)狀態(tài)發(fā)生變化時(shí),相關(guān)的 State 對(duì)象會(huì)被重新構(gòu)建以更新界面。適用于有變化狀態(tài)的部分,比如用戶輸入、數(shù)據(jù)加載等。
StatelessWidget: 這是一個(gè)不可變的 Widget 類別,其描述和外觀在整個(gè)生命周期內(nèi)保持不變。StatelessWidget 實(shí)例在構(gòu)建時(shí)不會(huì)持有可變狀態(tài),因此適用于不需要變化的 UI 部分,如圖標(biāo)、文本等。

Widget可以被多次包含在樹中,每次都會(huì)被實(shí)例化為元素。如果某個(gè) widget 在樹中出現(xiàn)多次,它將被多次實(shí)例化。

4.2.2 RenderObjectWidget

RenderObjectWidget是Flutter渲染引擎的一部分,表示屏幕上的可見對(duì)象,它又有LeafRenderObjectWidget、SingleChildRenderObjectWidgetMultiChildRenderObjectWidget這三個(gè)子類。
Flutter源碼分析筆記:Widget類源碼分析,Dart語(yǔ)言與Flutter框架開發(fā)筆記,Flutter,Flutter源碼,Dart

  • LeafRenderObjectWidget: 這是一種將 RenderObject 無(wú)需管理子元素的 Widget 類別。它通常用于將自定義的繪制邏輯封裝為 Widget,然后通過(guò)構(gòu)建 RenderObject 進(jìn)行繪制。
  • SingleChildRenderObjectWidget: 這是一種管理單個(gè)子元素的 RenderObjectWidget 類別。它會(huì)創(chuàng)建一個(gè)單一的子元素,并將其作為子節(jié)點(diǎn)傳遞給 RenderObject 進(jìn)行渲染。
  • MultiChildRenderObjectWidget: 這是一種管理多個(gè)子元素的 RenderObjectWidget 類別。它會(huì)創(chuàng)建多個(gè)子元素,并將它們作為子節(jié)點(diǎn)傳遞給 RenderObject 進(jìn)行渲染。比如 Stack、Column 和 Row 等都是 MultiChildRenderObjectWidget 的子類。

4.2.3 ProxyWidget

ProxyWidget提供一種方式來(lái)包裝 Widgets,以實(shí)現(xiàn)特定的功能。它是一個(gè)具有子WidgetWidget,而非新的Widget

Flutter源碼分析筆記:Widget類源碼分析,Dart語(yǔ)言與Flutter框架開發(fā)筆記,Flutter,Flutter源碼,Dart

  • ParentDataWidget: 這是一個(gè)用于修改子 Widget 布局約束的 ProxyWidget 子類。它在渲染樹中修改子元素的布局信息,例如 Positioned 和 Align 等都是 ParentDataWidget 的子類,用于指定子元素的位置和對(duì)齊方式。
  • InheritedWidget: 這是一種特殊類型的 ProxyWidget,它允許在 Widget 樹中向下傳遞共享的數(shù)據(jù),而不需要顯式地傳遞。當(dāng) InheritedWidget 更新時(shí),其子孫節(jié)點(diǎn)會(huì)自動(dòng)重新構(gòu)建。適用于需要在多個(gè)部分之間共享數(shù)據(jù)的情況,如主題、語(yǔ)言等。

5. Widget樹

Widget樹的概念

在Flutter中,Widget 樹是指由各種不同類型的Widget構(gòu)成的層次結(jié)構(gòu)。每個(gè)Widget描述了用戶界面的一部分,可以是一個(gè)簡(jiǎn)單的元素,也可以是一個(gè)復(fù)雜的組合。這些Widget通過(guò)嵌套關(guān)系形成了一個(gè)樹狀結(jié)構(gòu),被稱為Widget樹。這種嵌套關(guān)系定義了界面中各個(gè)部分的排列和組織方式。

Widget樹是構(gòu)建用戶界面的基本模型。當(dāng) Flutter 應(yīng)用程序運(yùn)行時(shí),它會(huì)從一個(gè) 根Widget 開始,然后逐級(jí)構(gòu)建出整個(gè)界面。每個(gè)Widget都有一個(gè)與之相關(guān)聯(lián)的Element,負(fù)責(zé)管理底層的渲染樹。渲染樹最終會(huì)被轉(zhuǎn)化為可視的UI元素,顯示在屏幕上。

Widget樹的特點(diǎn)

嵌套關(guān)系Widget 樹的節(jié)點(diǎn)由各種不同類型的 Widget 組成,這些 Widget 可以嵌套在彼此內(nèi)部,形成層次結(jié)構(gòu)。

不可變性Widget 本身是 不可變 的,一旦創(chuàng)建就不能再進(jìn)行修改。如果需要更新界面,通常是通過(guò)創(chuàng)建新的Widget來(lái)替換舊的Widget。

構(gòu)建方式: 構(gòu)建 Widget 樹通常是通過(guò)構(gòu)建方法來(lái)完成的。在構(gòu)建方法中,你可以創(chuàng)建和組合不同的 Widget,從而構(gòu)建出整個(gè)界面。

熱重載: Flutter支持熱重載,這意味著你可以在不重新啟動(dòng)應(yīng)用程序的情況下快速修改和查看界面的變化。在熱重載期間,F(xiàn)lutter會(huì)比較新舊Widget樹的差異,并盡可能地保留應(yīng)用程序的狀態(tài)。

響應(yīng)式: Flutter的界面是響應(yīng)式的,意味著當(dāng)數(shù)據(jù)發(fā)生變化時(shí),相關(guān)的Widget會(huì)自動(dòng)更新。這是通過(guò)在StatefulWidget 中管理可變狀態(tài)來(lái)實(shí)現(xiàn)的。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-651929.html

F. 附錄

F.1 Widget類源碼(中文注釋)

/// 描述 [Element] 的配置。
///
/// 在 Flutter 框架中,組件是層次結(jié)構(gòu)的中心類。一個(gè)組件是用戶界面的不可變描述的一部分。
/// 組件可以被充實(shí)成元素,這些元素管理著底層的渲染樹。
///
/// 組件本身沒有可變狀態(tài)(它們的所有字段必須是 final 的)。
/// 如果你想將可變狀態(tài)與組件關(guān)聯(lián)起來(lái),可以考慮使用 [StatefulWidget],每當(dāng)它被充實(shí)成元素并
/// 并且被納入樹中時(shí),就會(huì)創(chuàng)建一個(gè) [State] 對(duì)象(通過(guò) [StatefulWidget.createState])。
///
/// 一個(gè)給定的組件可以零次或多次地包含在樹中。特別地,一個(gè)給定的組件可以多次放置在樹中。
/// 每次將一個(gè)組件放置在樹中時(shí),它都會(huì)被充實(shí)成一個(gè) [Element],這意味著一個(gè)多次包含在樹中的
/// 組件將會(huì)被充實(shí)多次。
///
/// [key] 屬性控制了一個(gè)組件如何替換樹中的另一個(gè)組件。如果兩個(gè)組件的 [runtimeType] 和 [key]
/// 屬性分別為 [operator==],那么新組件將通過(guò)更新底層元素(即通過(guò)調(diào)用 [Element.update]
/// 用新組件)來(lái)替換舊組件。否則,舊元素將從樹中移除,新組件將被充實(shí)成一個(gè)元素,并將新元素插入樹中。
///
/// 另外,將 [GlobalKey] 用作組件的 [key],可以使元素在樹中移動(dòng)(更改父級(jí))而不丟失狀態(tài)。
/// 當(dāng)找到一個(gè)新組件(其鍵和類型與前一幀中同一位置的先前組件不匹配),
/// 但是在樹的其他位置(前一幀中)有一個(gè)具有相同全局鍵的組件時(shí),那么該組件的元素將移動(dòng)到新位置。
///
/// 通常情況下,作為另一個(gè)組件唯一子組件的組件不需要明確的鍵。
///
/// 另請(qǐng)參閱:
///
///  * [StatefulWidget] 和 [State],用于在其生命周期內(nèi)可以多次構(gòu)建的組件。
///  * [InheritedWidget],用于引入可以被后代組件讀取的環(huán)境狀態(tài)。
///  * [StatelessWidget],對(duì)于在給定特定配置和環(huán)境狀態(tài)的情況下始終以相同方式構(gòu)建的組件。

abstract class Widget extends DiagnosticableTree {
  /// 為子類初始化 [key]。
  const Widget({this.key});

  /// 控制一個(gè)組件如何替換樹中的另一個(gè)組件。
  ///
  /// 如果兩個(gè)組件的 [runtimeType] 和 [key] 屬性分別為 [operator==],那么新組件將通過(guò)
  /// 更新底層元素(即通過(guò)調(diào)用 [Element.update] 用新組件)來(lái)替換舊組件。
  /// 否則,舊元素將從樹中移除,新組件將被充實(shí)成一個(gè)元素,并將新元素插入樹中。
  ///
  /// 另外,將 [GlobalKey] 用作組件的 [key],可以使元素在樹中移動(dòng)(更改父級(jí))而不丟失狀態(tài)。
  /// 當(dāng)找到一個(gè)新組件(其鍵和類型與前一幀中同一位置的先前組件不匹配),
  /// 但是在樹的其他位置(前一幀中)有一個(gè)具有相同全局鍵的組件時(shí),那么該組件的元素將移動(dòng)到新位置。
  ///
  /// 通常情況下,作為另一個(gè)組件唯一子組件的組件不需要明確的鍵。
  ///
  /// 另請(qǐng)參閱:
  ///
  ///  * [Key] 和 [GlobalKey] 的討論。
  final Key? key;

  /// 將此配置充實(shí)為具體實(shí)例。
  ///
  /// 一個(gè)給定的組件可以零次或多次地包含在樹中。特別地,一個(gè)給定的組件可以多次放置在樹中。
  /// 每次將一個(gè)組件放置在樹中時(shí),它都會(huì)被充實(shí)成一個(gè) [Element],這意味著一個(gè)多次包含在樹中的
  /// 組件將會(huì)被充實(shí)多次。
  
  
  Element createElement();

  /// 此組件的簡(jiǎn)短文本描述。
  
  String toStringShort() {
    final String type = objectRuntimeType(this, 'Widget');
    return key == null ? type : '$type-$key';
  }

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

  
  
  bool operator ==(Object other) => super == other;

  
  
  int get hashCode => super.hashCode;

  /// 是否可以使用 `newWidget` 更新當(dāng)前將 `oldWidget` 作為其配置的 [Element]。
  ///
  /// 使用給定組件作為其配置的元素可以更新為使用另一個(gè)組件作為其配置,前提是兩個(gè)組件具有
  /// [runtimeType] 和 [key] 屬性,這些屬性是 [operator==]。
  ///
  /// 如果組件沒有鍵(它們的鍵為 null),則認(rèn)為它們是匹配的,即使它們的子組件完全不同。
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType &&
        oldWidget.key == newWidget.key;
  }

  // 返回特定 `Widget` 具體子類型的數(shù)值編碼。
  // 這在 `Element.updateChild` 中用于確定熱重載是否修改了已裝載元素配置的超類。
  // 每個(gè) `Widget` 的編碼必須與 `Element._debugConcreteSubtype` 中相應(yīng)的 `Element` 編碼匹配。
  static int _debugConcreteSubtype(Widget widget) {
    return widget is StatefulWidget
        ? 1
        : widget is StatelessWidget
            ? 2
            : 0;
  }
}

F.2 StatelessWidget類源碼(中文注釋)

/// 一個(gè)不需要可變狀態(tài)的小部件。
///
/// 無(wú)狀態(tài)小部件是一個(gè)小部件,通過(guò)構(gòu)建一組其他更具體描述用戶界面的小部件來(lái)描述用戶界面的一部分。
/// 構(gòu)建過(guò)程遞歸地繼續(xù),直到用戶界面的描述完全具體(例如,完全由描述具體的 [RenderObject] 的 [RenderObjectWidget] 構(gòu)成)。
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=wE7khGHVkYY}
///
/// 無(wú)狀態(tài)小部件在你所描述的用戶界面部分不依賴于除了對(duì)象本身的配置信息和填充小部件時(shí)的 [BuildContext] 之外的任何內(nèi)容時(shí)非常有用。
/// 對(duì)于可以動(dòng)態(tài)改變的組合,例如由于具有內(nèi)部時(shí)鐘驅(qū)動(dòng)的狀態(tài),或者依賴于一些系統(tǒng)狀態(tài),請(qǐng)考慮使用 [StatefulWidget]。
///
/// ## 性能注意事項(xiàng)
///
/// 無(wú)狀態(tài)小部件的 [build] 方法通常只在三種情況下被調(diào)用:第一次將小部件插入樹中時(shí),
/// 當(dāng)小部件的父級(jí)更改其配置時(shí)(參見 [Element.rebuild])以及它所依賴的 [InheritedWidget] 發(fā)生更改時(shí)。
///
/// 如果小部件的父級(jí)會(huì)定期更改小部件的配置,或者它依賴于經(jīng)常更改的繼承小部件,
/// 那么優(yōu)化 [build] 方法的性能以保持流暢的渲染性能就顯得很重要。
///
/// 有幾種技術(shù)可以用來(lái)最小化重建無(wú)狀態(tài)小部件的影響:
///
///  * 最小化構(gòu)建方法和它創(chuàng)建的任何小部件所創(chuàng)建的傳遞節(jié)點(diǎn)的數(shù)量。例如,不要使用復(fù)雜的 [Row]、
///    [Column]、[Padding] 和 [SizedBox] 的排列來(lái)以特別花哨的方式定位單個(gè)子部件,
///    而是考慮只使用 [Align] 或 [CustomSingleChildLayout]。不要使用多個(gè) [Container] 并帶有 [Decoration] 的
///    錯(cuò)綜復(fù)雜的圖層來(lái)繪制恰好正確的圖形效果,而是考慮使用單個(gè) [CustomPaint] 小部件。
///
///  * 在可能的情況下使用 `const` 小部件,并為小部件提供 `const` 構(gòu)造函數(shù),
///    以便小部件的用戶也可以這樣做。
///
///  * 考慮將無(wú)狀態(tài)小部件重構(gòu)為有狀態(tài)小部件,以便它可以使用 [StatefulWidget] 中描述的一些技術(shù),
///    例如緩存子樹的常見部分和在更改樹結(jié)構(gòu)時(shí)使用 [GlobalKey]。
///
///  * 如果小部件由于使用 [InheritedWidget] 而可能經(jīng)常被重建,
///    考慮將無(wú)狀態(tài)小部件重構(gòu)為多個(gè)小部件,其中發(fā)生更改的樹的部分被推送到葉子上。
///    例如,不要使用四個(gè)小部件構(gòu)建一個(gè)樹,最內(nèi)部的小部件依賴于 [Theme],
///    而是考慮將構(gòu)建最內(nèi)部小部件的構(gòu)建函數(shù)部分分離出來(lái),將其構(gòu)建為自己的小部件,
///    這樣只有最內(nèi)部的小部件在主題更改時(shí)需要重建。
/// {@template flutter.flutter.widgets.framework.prefer_const_over_helper}
///  * 在嘗試創(chuàng)建可重用的 UI 片段時(shí),優(yōu)先使用小部件而不是助手方法。
///    例如,如果使用函數(shù)來(lái)構(gòu)建小部件,那么 [State.setState] 調(diào)用將需要 Flutter 完全重新構(gòu)建返回的包裝小部件。
///    如果使用了 [Widget],F(xiàn)lutter 將能夠高效地重新渲染只有那些真正需要更新的部分。
///    更好的是,如果創(chuàng)建的小部件是 `const`,F(xiàn)lutter 將會(huì)短路大部分的重建工作。
/// {@endtemplate}
///
/// 這個(gè)視頻更詳細(xì)地解釋了 `const` 構(gòu)造函數(shù)的重要性以及為什么使用小部件要比使用助手方法更好。
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo}
///
/// {@tool snippet}
///
/// 以下是一個(gè)名為 `GreenFrog` 的無(wú)狀態(tài)小部件子類的骨架。
///
/// 通常,小部件具有更多的構(gòu)造函數(shù)參數(shù),每個(gè)參數(shù)都對(duì)應(yīng)一個(gè) `final` 屬性。
///
/// ```dart
/// class GreenFrog extends StatelessWidget {
///   const GreenFrog({ super.key });
///
///   @override
///   Widget build(BuildContext context) {
///     return Container(color: const Color(0xFF2DBD3A));
///   }
/// }
/// ```
/// {@end-tool}
///
/// {@tool snippet}
///
/// 下一個(gè)示例顯示了更通用的小部件 `Frog`,它可以提供顏色和子部件:
///
/// ```dart
/// class Frog extends StatelessWidget {
///   const Frog({
///     super.key,
///     this.color = const Color(0xFF2DBD3A),
///     this.child,
///   });
///
///   final Color color;
///   final Widget? child;
///
///   @override
///   Widget build(BuildContext context) {
///     return ColoredBox(color: color, child: child);
///   }
/// }
/// ```
/// {@end-tool}
///
/// 按照慣例,小部件構(gòu)造函數(shù)只使用命名參數(shù)。同樣按照慣例,第一個(gè)參數(shù)是 [key],最后一個(gè)參數(shù)是 `child`、
/// `children` 或等效物。
///
/// 另請(qǐng)參閱:
///
///  * [StatefulWidget] 和 [State],用于在其生命周期內(nèi)可以多次構(gòu)建的小部件。
///  * [InheritedWidget],用于引入可以被后代小部件讀取的環(huán)境狀態(tài)。

abstract class StatelessWidget extends Widget {
  /// 為子類初始化 [key]。
  const StatelessWidget({super.key});

  /// 創(chuàng)建一個(gè) [StatelessElement] 來(lái)管理此組件在樹中的位置。
  ///
  /// 子類很少覆蓋這個(gè)方法。
  
  StatelessElement createElement() => StatelessElement(this);

  /// 描述此組件表示的用戶界面的部分。
  ///
  /// 當(dāng)此組件在給定的 [BuildContext] 中插入樹中,并且此組件引用的依賴項(xiàng)發(fā)生更改(例如,此組件引用的 [InheritedWidget] 發(fā)生更改)時(shí),
  /// 框架會(huì)調(diào)用此方法。這個(gè)方法可能在每一幀中被調(diào)用,除了構(gòu)建一個(gè)組件之外,不應(yīng)該有任何副作用。
  ///
  /// 框架通過(guò)此方法返回的組件來(lái)替換此組件下方的子樹,要么通過(guò)更新現(xiàn)有的子樹,要么通過(guò)刪除子樹并充實(shí)新的子樹,
  /// 具體取決于此方法返回的組件是否可以更新現(xiàn)有子樹的根,這由調(diào)用 [Widget.canUpdate] 來(lái)確定。
  ///
  /// 通常情況下,實(shí)現(xiàn)會(huì)返回一個(gè)新創(chuàng)建的一組配置為來(lái)自此組件的構(gòu)造函數(shù)和給定的 [BuildContext] 信息的小部件。
  ///
  /// 給定的 [BuildContext] 包含有關(guān)構(gòu)建此組件的位置在樹中的信息。
  /// 例如,上下文為該位置提供了繼承的小部件集合。如果小部件在樹中移動(dòng),或者如果小部件同時(shí)在多個(gè)位置插入樹中,
  /// 則該小部件可能會(huì)隨著時(shí)間的推移使用多個(gè)不同的 [BuildContext] 參數(shù)構(gòu)建。
  ///
  /// 此方法的實(shí)現(xiàn)只能依賴于:
  ///
  /// * 小部件的字段,它們本身不能隨時(shí)間變化,
  ///   和
  /// * 使用 [BuildContext.dependOnInheritedWidgetOfExactType] 從 `context` 獲得的任何環(huán)境狀態(tài)。
  ///
  /// 如果一個(gè)小部件的 [build] 方法需要依賴其他內(nèi)容,應(yīng)使用 [StatefulWidget]。
  ///
  /// 另請(qǐng)參閱:
  ///
  ///  * [StatelessWidget],其中包含性能考慮的討論。
  
  Widget build(BuildContext context);
}

F.3 StatefulWidget類源碼(中文注釋)

/// 一個(gè)具有可變狀態(tài)的小部件。
///
/// 狀態(tài)是(1)在小部件構(gòu)建時(shí)可以同步讀取的信息,以及(2)在小部件的生命周期內(nèi)可能會(huì)發(fā)生變化的信息。
/// 小部件實(shí)現(xiàn)者有責(zé)任確保當(dāng)狀態(tài)發(fā)生變化時(shí),[State] 及時(shí)被通知,使用 [State.setState]。
///
/// 有狀態(tài)小部件是通過(guò)構(gòu)建一組其他更具體描述用戶界面的小部件來(lái)描述用戶界面的一部分的小部件。
/// 構(gòu)建過(guò)程遞歸地繼續(xù),直到用戶界面的描述完全具體(例如,完全由描述具體的 [RenderObject] 的 [RenderObjectWidget] 構(gòu)成)。
///
/// 當(dāng)你描述的用戶界面部分可以動(dòng)態(tài)地改變時(shí),有狀態(tài)小部件非常有用,例如由于具有內(nèi)部時(shí)鐘驅(qū)動(dòng)的狀態(tài),或者依賴于一些系統(tǒng)狀態(tài)。
/// 對(duì)于只依賴于對(duì)象本身的配置信息和填充小部件時(shí)的 [BuildContext] 的組合,請(qǐng)考慮使用 [StatelessWidget]。
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=AqCMFXEmf3w}
///
/// [StatefulWidget] 實(shí)例本身是不可變的,并且將可變狀態(tài)存儲(chǔ)在由 [createState] 方法創(chuàng)建的單獨(dú)的 [State] 對(duì)象中,
/// 或存儲(chǔ)在 [State] 訂閱的對(duì)象中,例如 [Stream] 或 [ChangeNotifier] 對(duì)象,這些引用存儲(chǔ)在 [StatefulWidget] 本身的 final 字段中。
///
/// 框架在每次填充 [StatefulWidget] 時(shí)都會(huì)調(diào)用 [createState],這意味著如果小部件在樹中的多個(gè)位置插入,
/// 則多個(gè) [State] 對(duì)象可能與同一個(gè) [StatefulWidget] 關(guān)聯(lián)。同樣,如果從樹中移除 [StatefulWidget],
/// 然后再次插入樹中,框架將再次調(diào)用 [createState] 來(lái)創(chuàng)建一個(gè)新的 [State] 對(duì)象,簡(jiǎn)化了 [State] 對(duì)象的生命周期。
///
/// 當(dāng)從樹中的一個(gè)位置移動(dòng)到另一個(gè)位置時(shí),如果創(chuàng)建者為其 [key] 使用了 [GlobalKey],則 [StatefulWidget] 保留相同的 [State] 對(duì)象。
/// 因?yàn)榫哂?[GlobalKey] 的小部件最多可以在樹中的一個(gè)位置使用,所以使用 [GlobalKey] 的小部件最多有一個(gè)關(guān)聯(lián)的元素。
/// 當(dāng)具有全局鍵的小部件從樹的一個(gè)位置移動(dòng)到另一個(gè)位置時(shí),框架通過(guò)將與該小部件關(guān)聯(lián)的(唯一的)子樹從舊位置移植到新位置來(lái)利用此屬性
/// (而不是在新位置重新創(chuàng)建子樹)。與 [StatefulWidget] 相關(guān)聯(lián)的 [State] 對(duì)象與其余子樹一起被移植,
/// 這意味著 [State] 對(duì)象在新位置中被重用(而不是重新創(chuàng)建)。但是,為了有資格進(jìn)行移植,小部件必須在從舊位置刪除時(shí)的相同動(dòng)畫幀中
/// 插入新位置。
///
/// ## 性能注意事項(xiàng)
///
/// [StatefulWidget] 主要分為兩類。
///
/// 第一類是在 [State.initState] 中分配資源并在 [State.dispose] 中釋放這些資源的小部件,
/// 但不依賴于 [InheritedWidget] 或調(diào)用 [State.setState]。這些小部件通常用于應(yīng)用程序或頁(yè)面的根部,
/// 并通過(guò) [ChangeNotifier]、[Stream] 或其他類似的對(duì)象與子小部件通信。遵循這種模式的有狀態(tài)小部件相對(duì)便宜
/// (從 CPU 和 GPU 循環(huán)的角度來(lái)看),因?yàn)樗鼈冎粫?huì)構(gòu)建一次,然后永不更新。因此,它們的構(gòu)建方法可以相對(duì)復(fù)雜和深入。
///
/// 第二類是使用 [State.setState] 或依賴于 [InheritedWidget] 的小部件。在應(yīng)用程序的生命周期內(nèi),
/// 這些小部件通常會(huì)重建多次,因此最大限度地減少重建此類小部件的影響非常重要。
/// (它們可能還會(huì)使用 [State.initState] 或 [State.didChangeDependencies] 并分配資源,但重要的是它們會(huì)重建。)
///
/// 有幾種技術(shù)可以用來(lái)最小化重建有狀態(tài)小部件的影響:
///
///  * 將狀態(tài)推送到葉子節(jié)點(diǎn)。例如,如果頁(yè)面有一個(gè)滴答滴答的時(shí)鐘,
///    而不是將狀態(tài)放在頁(yè)面頂部并在每次時(shí)鐘滴答時(shí)重新構(gòu)建整個(gè)頁(yè)面,
///    創(chuàng)建一個(gè)專用的時(shí)鐘小部件只更新自己。
///
///  * 最小化構(gòu)建方法和它創(chuàng)建的任何小部件所創(chuàng)建的傳遞節(jié)點(diǎn)的數(shù)量。
///    理想情況下,有狀態(tài)小部件只會(huì)創(chuàng)建一個(gè)小部件,該小部件將是 [RenderObjectWidget]。
///    (顯然,這并不總是切實(shí)可行的,但小部件越接近此理想,它的效率就越高。)
///
///  * 如果子樹不會(huì)改變,請(qǐng)緩存表示該子樹的小部件,并在每次可以使用時(shí)重復(fù)使用它。
///    為此,將小部件分配給一個(gè) `final` 狀態(tài)變量,并在構(gòu)建方法中重復(fù)使用它。
///    對(duì)于小部件來(lái)說(shuō),重新使用要比創(chuàng)建新的(但配置相同)小部件要高效得多。
///    另一種緩存策略是將小部件的可變部分提取到接受子部件參數(shù)的 [StatefulWidget] 中。
///
///  * 在可能的情況下使用 `const` 小部件。 (這等效于緩存小部件并重復(fù)使用它。)
///
///  * 避免更改任何創(chuàng)建的子樹的深度,或更改子樹中任何小部件的類型。
///    例如,與其返回僅是子部件或在 [IgnorePointer] 中包裝的子部件,
///    最好始終在 [IgnorePointer] 中包裝子部件,并控制 [IgnorePointer.ignoring] 屬性。
///    這是因?yàn)楦淖訕涞纳疃刃枰匦聵?gòu)建、布局和繪制整個(gè)子樹,
///    而更改屬性只需要對(duì)渲染樹進(jìn)行最小的可能更改(例如,在 [IgnorePointer] 的情況下,根本不需要布局或重繪)。
///
///  * 如果由于某種原因必須更改深度,請(qǐng)考慮將子樹的常見部分包裝在具有在狀態(tài)小部件的生命周期內(nèi)保持一致的 [GlobalKey] 的小部件中。
///    (如果沒有其他小部件可以方便地分配鍵,則 [KeyedSubtree] 小部件可能對(duì)此有用。)
///
/// {@macro flutter.flutter.widgets.framework.prefer_const_over_helper}
///
/// 這個(gè)視頻更詳細(xì)地解釋了 `const` 構(gòu)造函數(shù)的重要性以及為什么使用小部件要比使用助手方法更好。
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=IOyq-eTRhvo}
///
/// 有關(guān)重建小部件的機(jī)制的更多詳細(xì)信息,請(qǐng)參閱 [Element.rebuild] 中的討論。
///
/// {@tool snippet}
///
/// 這是一個(gè)名為 `YellowBird` 的有狀態(tài)小部件子類的骨架。
///
/// 在此示例中,[State] 沒有實(shí)際的狀態(tài)。通常,狀態(tài)表示為私有成員字段。
/// 同樣,通常小部件具有更多的構(gòu)造函數(shù)參數(shù),每個(gè)參數(shù)對(duì)應(yīng)一個(gè) `final` 屬性。
///
/// ```dart
/// class YellowBird extends StatefulWidget {
///   const YellowBird({ super.key });
///
///   @override
///   State<YellowBird> createState() => _YellowBirdState();
/// }
///
/// class _YellowBirdState extends State<YellowBird> {
///   @override
///   Widget build(BuildContext context) {
///     return Container(color: const Color(0xFFFFE306));
///   }
/// }
/// ```
/// {@end-tool}
/// {@tool snippet}
///
/// 該示例顯示了更通用的小部件 `Bird`,它可以提供顏色和子部件,并且具有一種用于變異的內(nèi)部狀態(tài)和方法:
///
/// ```dart
/// class Bird extends StatefulWidget {
///   const Bird({
///     super.key,
///     this.color = const Color(0xFFFFE306),
///     this.child,
///   });
///
///   final Color color;
///   final Widget? child;
///
///   @override
///   State<Bird> createState() => _BirdState();
/// }
///
/// class _BirdState extends State<Bird> {
///   double _size = 1.0;
///
///   void grow() {
///     setState(() { _size += 0.1; });
///   }
///
///   @override
///   Widget build(BuildContext context) {
///     return Container(
///       color: widget.color,
///       transform: Matrix4.diagonal3Values(_size, _size, 1.0),
///       child: widget.child,
///     );
///   }
/// }
/// ```
/// {@end-tool}
///
/// 按照慣例,小部件構(gòu)造函數(shù)只使用命名參數(shù)。同樣按照慣例,第一個(gè)參數(shù)是 [key],最后一個(gè)參數(shù)是 `child`、
/// `children` 或者等價(jià)的內(nèi)容。
///
/// 另請(qǐng)參見:
///
///  * [State],其中托管 [StatefulWidget] 的邏輯。
///  * [StatelessWidget],對(duì)于在給定特定配置和環(huán)境狀態(tài)下始終以相同方式構(gòu)建的小部件。
///  * [InheritedWidget],對(duì)于引入可以由后代小部件讀取的環(huán)境狀態(tài)的小部件。
abstract class StatefulWidget extends Widget {
  /// 為子類初始化 [key]。
  const StatefulWidget({super.key});

  /// 創(chuàng)建一個(gè) [StatefulElement] 來(lái)管理此小部件在樹中的位置。
  ///
  /// 不常見的情況下,子類會(huì)覆蓋此方法。
  
  StatefulElement createElement() => StatefulElement(this);

  /// 在樹的給定位置為此小部件創(chuàng)建可變狀態(tài)。
  ///
  /// 子類應(yīng)該覆蓋此方法,以返回其關(guān)聯(lián)的 [State] 子類的新創(chuàng)建實(shí)例:
  ///
  /// ```dart
  /// @override
  /// State<SomeWidget> createState() => _SomeWidgetState();
  /// ```
  ///
  /// 框架可能會(huì)在 [StatefulWidget] 的生命周期內(nèi)多次調(diào)用此方法。
  /// 例如,如果在樹中的多個(gè)位置插入小部件,則框架會(huì)為每個(gè)位置創(chuàng)建一個(gè)單獨(dú)的 [State] 對(duì)象。
  /// 同樣,如果從樹中刪除 [StatefulWidget],然后再次插入樹中,
  /// 框架將再次調(diào)用 [createState] 來(lái)創(chuàng)建一個(gè)新的 [State] 對(duì)象,簡(jiǎn)化了 [State] 對(duì)象的生命周期。
  
  
  State createState();
}

F.4 State類源碼(中文注釋)

/// [StatefulWidget] 的邏輯和內(nèi)部狀態(tài)。
///
/// 狀態(tài)是可以在小部件構(gòu)建時(shí)同步讀取的信息,并且可能在小部件的生命周期內(nèi)發(fā)生變化。
/// 小部件實(shí)現(xiàn)者有責(zé)任確保當(dāng)這種狀態(tài)變化時(shí),[State] 能夠及時(shí)地得到通知,使用 [State.setState]。
///
/// [State] 對(duì)象通過(guò)在膨脹 [StatefulWidget] 以將其插入樹中時(shí)調(diào)用 [StatefulWidget.createState] 方法
/// 來(lái)由框架創(chuàng)建。因?yàn)榻o定的 [StatefulWidget] 實(shí)例可以多次膨脹(例如,小部件同時(shí)在多個(gè)位置合并到樹中),
/// 可能會(huì)與給定的 [StatefulWidget] 實(shí)例關(guān)聯(lián)多個(gè) [State] 對(duì)象。
/// 同樣,如果從樹中移除 [StatefulWidget],然后再次插入樹中,框架將再次調(diào)用 [StatefulWidget.createState]
/// 來(lái)創(chuàng)建一個(gè)新的 [State] 對(duì)象,簡(jiǎn)化了 [State] 對(duì)象的生命周期。
///
/// [State] 對(duì)象具有以下生命周期:
///
///  * 框架通過(guò)調(diào)用 [StatefulWidget.createState] 來(lái)創(chuàng)建一個(gè) [State] 對(duì)象。
///  * 新創(chuàng)建的 [State] 對(duì)象與 [BuildContext] 關(guān)聯(lián)。
///    此關(guān)聯(lián)是永久的:[State] 對(duì)象永遠(yuǎn)不會(huì)更改其 [BuildContext]。
///    但是,[BuildContext] 本身可以隨著其子樹一起移動(dòng)到樹中的其他位置。
///  * 在此時(shí),[State] 對(duì)象的關(guān)聯(lián) [BuildContext] 可以通過(guò) [context] 屬性訪問。
///  * 在調(diào)用 [dispose] 后,框架會(huì)斷開 [State] 對(duì)象與 [BuildContext] 的連接。

abstract class State<T extends StatefulWidget> with Diagnosticable {
  /// 當(dāng)前的配置。
  ///
  /// [State] 對(duì)象的配置是相應(yīng)的 [StatefulWidget] 實(shí)例。
  /// 在調(diào)用 [initState] 之前,框架會(huì)使用此屬性初始化。
  /// 如果父級(jí)將此位置在樹中更新為具有與當(dāng)前配置相同的 [runtimeType] 和 [Widget.key] 的新小部件,
  /// 框架將更新此屬性以引用新小部件,然后調(diào)用 [didUpdateWidget],將舊配置作為參數(shù)傳遞。
  T get widget => _widget!;
  T? _widget;

  /// 此狀態(tài)對(duì)象的當(dāng)前生命周期階段。
  ///
  /// 當(dāng)啟用斷言時(shí),框架會(huì)使用此字段來(lái)驗(yàn)證 [State] 對(duì)象是否按順序移動(dòng)其生命周期。
  _StateLifecycle _debugLifecycleState = _StateLifecycle.created;

  /// 驗(yàn)證創(chuàng)建的 [State] 是否是期望為特定 [Widget] 創(chuàng)建的 [State]。
  bool _debugTypesAreRight(Widget widget) => widget is T;

  /// 此小部件構(gòu)建的樹中的位置。
  ///
  /// 在使用 [StatefulWidget.createState] 創(chuàng)建 [State] 對(duì)象后,在調(diào)用 [initState] 之前,
  /// 框架會(huì)將 [State] 對(duì)象與 [BuildContext] 關(guān)聯(lián)起來(lái)。
  /// 此關(guān)聯(lián)是永久的:[State] 對(duì)象永遠(yuǎn)不會(huì)更改其 [BuildContext]。
  /// 但是,[BuildContext] 本身可以隨著其子樹一起移動(dòng)到樹中的其他位置。
  ///
  /// 在調(diào)用 [dispose] 后,框架會(huì)斷開 [State] 對(duì)象與 [BuildContext] 的連接。
  BuildContext get context {
    assert(() {
      if (_element == null) {
        throw FlutterError(
          'This widget has been unmounted, so the State no longer has a context (and should be considered defunct). \n'
          'Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.',
        );
      }
      return true;
    }());
    return _element!;
  }

  StatefulElement? _element;

  /// 當(dāng)前的 [State] 對(duì)象是否在樹中。
  ///
  /// 在創(chuàng)建 [State] 對(duì)象后,在調(diào)用 [initState] 之前,框架通過(guò)將其與 [BuildContext] 關(guān)聯(lián)起來(lái)來(lái)“安裝” [State] 對(duì)象。
  /// [State] 對(duì)象會(huì)一直安裝到框架調(diào)用 [dispose],此后框架將不會(huì)再要求 [State] 對(duì)象重新 [build]。
  ///
  /// 調(diào)用 [setState] 除非 [mounted] 為 true,否則會(huì)引發(fā)錯(cuò)誤。
  bool get mounted => _element != null;

  /// 插入樹中時(shí)調(diào)用此方法。
  ///
  /// 框架將為其創(chuàng)建的每個(gè) [State] 對(duì)象調(diào)用此方法一次。
  ///
  /// 覆蓋此方法以執(zhí)行依賴于插入樹中的位置(即 [context])或用于配置此對(duì)象的小部件(即 [widget])的初始化。
  ///
  /// {@template flutter.widgets.State.initState}
  /// 如果 [State] 的 [build] 方法依賴于可能自身更改狀態(tài)的對(duì)象,例如 [ChangeNotifier] 或 [Stream],
  /// 或其他可以訂閱以接收通知的對(duì)象,請(qǐng)確保在 [initState]、[didUpdateWidget] 和 [dispose] 中適當(dāng)?shù)赜嗛喓腿∠嗛啠?/span>
  ///
  ///  * 在 [initState] 中訂閱對(duì)象。
  ///  * 在 [didUpdateWidget] 中取消訂閱舊對(duì)象,并在更新后的小部件配置需要替換對(duì)象時(shí)訂閱新對(duì)象。
  ///  * 在 [dispose] 中取消訂閱對(duì)象。
  ///
  /// {@endtemplate}
  ///
  /// 你不能從此方法中使用 [BuildContext.dependOnInheritedWidgetOfExactType]。
  /// 但是,[didChangeDependencies] 將在此方法之后立即調(diào)用,
  /// 可以在其中使用 [BuildContext.dependOnInheritedWidgetOfExactType]。
  ///
  /// 此方法的實(shí)現(xiàn)應(yīng)始于調(diào)用繼承的方法,如 `super.initState()`。
  
  
  void initState() {
    assert(_debugLifecycleState == _StateLifecycle.created);
    if (kFlutterMemoryAllocationsEnabled) {
      MemoryAllocations.instance.dispatchObjectCreated(
        library: _flutterWidgetsLibrary,
        className: '$State',
        object: this,
      );
    }
  }

  /// 每當(dāng)小部件配置更改時(shí)調(diào)用。
  ///
  /// 如果父小部件重新構(gòu)建并要求此樹中的位置更新以顯示具有相同 [runtimeType] 和 [Widget.key] 的新小部件,
  /// 則框架將更新此 [State] 對(duì)象的 [widget] 屬性以引用新小部件,然后調(diào)用此方法,并將上一個(gè)小部件作為參數(shù)傳遞。
  ///
  /// 覆蓋此方法以響應(yīng) [widget] 更改(例如,啟動(dòng)隱式動(dòng)畫)。
  ///
  /// 在調(diào)用 [didUpdateWidget] 之后,框架總是會(huì)調(diào)用 [build],這意味著在 [didUpdateWidget] 中調(diào)用 [setState] 是多余的。
  ///
  /// {@macro flutter.widgets.State.initState}
  ///
  /// 此方法的實(shí)現(xiàn)應(yīng)始于調(diào)用繼承的方法,如 `super.didUpdateWidget(oldWidget)`。
  ///
  /// _有關(guān)調(diào)用此方法的更多信息,請(qǐng)參見 [Element.rebuild] 中的討論。_
  
  
  void didUpdateWidget(covariant T oldWidget) {}

  /// {@macro flutter.widgets.Element.reassemble}
  ///
  /// 除了調(diào)用此方法之外,還保證在信號(hào)重組時(shí)將調(diào)用 [build] 方法。因此,大多數(shù)小部件在 [reassemble] 方法中不需要做任何事情。
  ///
  /// 另請(qǐng)參見:
  ///
  ///  * [Element.reassemble]
  ///  * [BindingBase.reassembleApplication]
  ///  * [Image],它使用此方法重新加載圖像。
  
  
  void reassemble() {}

  /// 通知框架此對(duì)象的內(nèi)部狀態(tài)已更改。
  ///
  /// 每當(dāng)更改 [State] 對(duì)象的內(nèi)部狀態(tài)時(shí),請(qǐng)將更改放入傳遞給 [setState] 的函數(shù)中:
  ///
  /// ```dart
  /// setState(() { _myState = newValue; });
  /// ```
  ///
  /// 提供的回調(diào)會(huì)立即同步調(diào)用。它不能返回未來(lái)(回調(diào)不能是 `async`),因?yàn)檫@將不清楚狀態(tài)實(shí)際上是何時(shí)被設(shè)置的。
  ///
  /// 調(diào)用 [setState] 會(huì)通知框架此對(duì)象的內(nèi)部狀態(tài)已更改,以一種可能影響此子樹中的用戶界面的方式,
  /// 這會(huì)導(dǎo)致框架為此 [State] 對(duì)象安排 [build]。
  ///
  /// 如果直接更改狀態(tài)而不調(diào)用 [setState],則框架可能不會(huì)安排 [build],
  /// 并且此子樹的用戶界面可能不會(huì)更新以反映新狀態(tài)。
  ///
  /// 通常建議 [setState] 方法僅用于包裝對(duì)狀態(tài)的實(shí)際更改,而不是與更改相關(guān)的任何計(jì)算。
  /// 例如,在此處,由 [build] 函數(shù)使用的值會(huì)遞增,然后將更改寫入磁盤,
  /// 但只有遞增操作包含在 [setState] 中:
  ///
  /// ```dart
  /// Future<void> _incrementCounter() async {
  ///   setState(() {
  ///     _counter++;
  ///   });
  ///   Directory directory = await getApplicationDocumentsDirectory(); // from path_provider package
  ///   final String dirName = directory.path;
  ///   await File('$dirName/counter.txt').writeAsString('$_counter');
  /// }
  /// ```
  ///
  /// 在框架調(diào)用 [dispose] 后調(diào)用此方法會(huì)引發(fā)錯(cuò)誤。
  /// 你可以通過(guò)檢查 [mounted] 屬性是否為 true 來(lái)確定是否可以調(diào)用此方法。

  
  void setState(VoidCallback fn) {
    assert(() {
      if (_debugLifecycleState == _StateLifecycle.defunct) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() called after dispose(): $this'),
          ErrorDescription(
            'This error happens if you call setState() on a State object for a widget that '
            'no longer appears in the widget tree (e.g., whose parent widget no longer '
            'includes the widget in its build). This error can occur when code calls '
            'setState() from a timer or an animation callback.',
          ),
          ErrorHint(
            'The preferred solution is '
            'to cancel the timer or stop listening to the animation in the dispose() '
            'callback. Another solution is to check the "mounted" property of this '
            'object before calling setState() to ensure the object is still in the '
            'tree.',
          ),
          ErrorHint(
            'This error might indicate a memory leak if setState() is being called '
            'because another object is retaining a reference to this State object '
            'after it has been removed from the tree. To avoid memory leaks, '
            'consider breaking the reference to this object during dispose().',
          ),
        ]);
      }
      if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() called in constructor: $this'),
          ErrorHint(
            'This happens when you call setState() on a State object for a widget that '
            "hasn't been inserted into the widget tree yet. It is not necessary to call "
            'setState() in the constructor, since the state is already assumed to be dirty '
            'when it is initially created.',
          ),
        ]);
      }
      return true;
    }());
    final Object? result = fn() as dynamic;
    assert(() {
      if (result is Future) {
        throw FlutterError.fromParts(<DiagnosticsNode>[
          ErrorSummary('setState() callback argument returned a Future.'),
          ErrorDescription(
            'The setState() method on $this was called with a closure or method that '
            'returned a Future. Maybe it is marked as "async".',
          ),
          ErrorHint(
            'Instead of performing asynchronous work inside a call to setState(), first '
            'execute the work (without updating the widget state), and then synchronously '
            'update the state inside a call to setState().',
          ),
        ]);
      }
      // 我們忽略了其他類型的返回值,以便你可以執(zhí)行以下操作:
      //   setState(() => x = 3);
      return true;
    }());
    _element!.markNeedsBuild();
  }

  /// 當(dāng)此對(duì)象從樹中移除時(shí)調(diào)用。
  ///
  /// 框架在從樹中移除此 [State] 對(duì)象時(shí)調(diào)用此方法。
  /// 在某些情況下,框架將重新將 [State] 對(duì)象插入到樹的另一部分中
  /// (例如,如果包含此 [State] 對(duì)象的子樹由于使用了 [GlobalKey] 而從樹的一個(gè)位置移植到另一個(gè)位置)。
  /// 如果發(fā)生這種情況,框架將調(diào)用 [activate],以便 [State] 對(duì)象有機(jī)會(huì)重新獲取在 [deactivate] 中釋放的任何資源。
  /// 然后還將調(diào)用 [build],以便 [State] 對(duì)象有機(jī)會(huì)適應(yīng)其在樹中的新位置。
  /// 如果框架確實(shí)重新插入此子樹,則將在將子樹從樹中移除的動(dòng)畫幀結(jié)束之前執(zhí)行此操作。
  /// 因此,[State] 對(duì)象可以推遲釋放大多數(shù)資源,直到框架調(diào)用它們的 [dispose] 方法。
  ///
  /// 子類應(yīng)該覆蓋此方法,以清除此對(duì)象與樹中其他元素之間的任何鏈接(例如,如果你向祖先提供了指向后代的 [RenderObject] 的指針)。
  ///
  /// 此方法的實(shí)現(xiàn)應(yīng)該以調(diào)用繼承的方法結(jié)束,如 `super.deactivate()`。
  ///
  /// 另請(qǐng)參見:
  ///
  ///  * [dispose],如果小部件永久從樹中移除,則在 [deactivate] 之后調(diào)用。
  
  
  void deactivate() {}

  /// 當(dāng)此對(duì)象通過(guò) [deactivate] 被移除后,重新插入樹中時(shí)調(diào)用。
  ///
  /// 在大多數(shù)情況下,[State] 對(duì)象在被停用后 _不會(huì)_ 被重新插入樹中,
  /// 并且將調(diào)用其 [dispose] 方法以表示其已準(zhǔn)備好進(jìn)行垃圾回收。
  ///
  /// 但在某些情況下,[State] 對(duì)象在被停用后,框架將其重新插入樹的另一部分中
  /// (例如,如果包含此 [State] 對(duì)象的子樹由于使用了 [GlobalKey] 而從樹的一個(gè)位置移植到另一個(gè)位置)。
  /// 如果發(fā)生這種情況,框架將調(diào)用 [activate],以便 [State] 對(duì)象有機(jī)會(huì)重新獲取在 [deactivate] 中釋放的任何資源。
  /// 然后還將調(diào)用 [build],以便對(duì)象有機(jī)會(huì)適應(yīng)其在樹中的新位置。
  /// 如果框架確實(shí)重新插入此子樹,則將在將子樹從樹中移除的動(dòng)畫幀結(jié)束之前執(zhí)行此操作。
  /// 因此,[State] 對(duì)象可以推遲釋放大多數(shù)資源,直到框架調(diào)用它們的 [dispose] 方法。
  ///
  /// 框架不會(huì)在第一次將 [State] 對(duì)象插入樹中時(shí)調(diào)用此方法。在這種情況下,框架將調(diào)用 [initState]。
  ///
  /// 此方法的實(shí)現(xiàn)應(yīng)該以調(diào)用繼承的方法開始,如 `super.activate()`。
  
  
  void activate() {}

  /// 當(dāng)此對(duì)象永久從樹中移除時(shí)調(diào)用。
  ///
  /// 框架在永遠(yuǎn)不會(huì)再次構(gòu)建此 [State] 對(duì)象時(shí)調(diào)用此方法。在框架調(diào)用 [dispose] 后,
  /// [State] 對(duì)象被視為未安裝,[mounted] 屬性為 false。此時(shí)調(diào)用 [setState] 是錯(cuò)誤的。
  /// 生命周期的這個(gè)階段是終端的:已被處置的 [State] 對(duì)象無(wú)法再次安裝。
  ///
  /// 子類應(yīng)該覆蓋此方法以釋放此對(duì)象保留的任何資源(例如,停止任何活動(dòng)的動(dòng)畫)。
  ///
  /// {@macro flutter.widgets.State.initState}
  ///
  /// 此方法的實(shí)現(xiàn)應(yīng)該以調(diào)用繼承的方法結(jié)束,如 `super.dispose()`。
  
  
  void dispose() {
    assert(_debugLifecycleState == _StateLifecycle.ready);
    assert(() {
      _debugLifecycleState = _StateLifecycle.defunct;
      return true;
    }());
    if (kFlutterMemoryAllocationsEnabled) {
      MemoryAllocations.instance.dispatchObjectDisposed(object: this);
    }
  }

  /// 描述由此小部件表示的用戶界面的部分。
  ///
  /// 框架在許多不同的情況下調(diào)用此方法。例如:
  ///
  ///  * 調(diào)用 [initState] 后。
  ///  * 調(diào)用 [didUpdateWidget] 后。
  ///  * 收到 [setState] 的調(diào)用后。
  ///  * 此 [State] 對(duì)象的依賴項(xiàng)發(fā)生更改后(例如,由先前的 [build] 引用的 [InheritedWidget] 更改)。
  ///  * 在調(diào)用 [deactivate],然后將 [State] 對(duì)象重新插入樹的另一個(gè)位置。
  ///
  /// 該方法可能在每一幀中被調(diào)用,除了構(gòu)建小部件外,不應(yīng)具有任何副作用。
  ///
  /// 框架使用此方法返回的小部件替換此小部件下方的子樹,可以通過(guò)更新現(xiàn)有子樹或刪除子樹并填充新的子樹來(lái)實(shí)現(xiàn),具體取決于此方法返回的小部件是否可以更新現(xiàn)有子樹的根部,由調(diào)用 [Widget.canUpdate] 確定。
  ///
  /// 通常,實(shí)現(xiàn)會(huì)返回一個(gè)新創(chuàng)建的小部件集合,這些小部件通過(guò)此小部件的構(gòu)造函數(shù)、給定的 [BuildContext] 和此 [State] 對(duì)象的內(nèi)部狀態(tài)進(jìn)行配置。
  ///
  /// 給定的 [BuildContext] 包含有關(guān)正在構(gòu)建此小部件的樹中的位置的信息。例如,上下文為該樹中的此位置提供了繼承小部件集。[BuildContext] 參數(shù)始終與此 [State] 對(duì)象的 [context] 屬性相同,并將在此對(duì)象的生命周期內(nèi)保持相同。在此處提供 [BuildContext] 參數(shù)是為了使此方法與 [WidgetBuilder] 的簽名匹配。
  ///
  /// ## 設(shè)計(jì)討論
  ///
  /// ### 為什么在 [State] 上而不是在 [StatefulWidget] 上放置 [build] 方法?
  ///
  /// 在 [StatefulWidget] 上放置一個(gè) `Widget build(BuildContext context)` 方法,
  /// 而不是在 [StatefulWidget] 上放置一個(gè) `Widget build(BuildContext context, State state)` 方法,
  /// 可以為開發(fā)人員在子類化 [StatefulWidget] 時(shí)提供更多的靈活性。
  ///
  /// 例如,[AnimatedWidget] 是 [StatefulWidget] 的子類,為其子類引入了一個(gè)抽象的 `Widget build(BuildContext context)` 方法供其子類實(shí)現(xiàn)。
  /// 如果 [StatefulWidget] 已經(jīng)有一個(gè)接受 [State] 參數(shù)的 [build] 方法,那么 [AnimatedWidget] 將被強(qiáng)制向其子類提供其 [State] 對(duì)象,
  /// 即使其 [State] 對(duì)象是 [AnimatedWidget] 的內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。
  ///
  /// 從概念上講,[StatelessWidget] 也可以以類似的方式實(shí)現(xiàn)為 [StatefulWidget] 的子類。
  /// 如果 [build] 方法在 [StatefulWidget] 而不是在 [State] 上,那就不可能了。
  ///
  /// 將 [build] 函數(shù)放在 [State] 上而不是 [StatefulWidget] 上還有助于避免與隱式捕獲 `this` 相關(guān)的一類與閉包相關(guān)的錯(cuò)誤。
  /// 如果在 [StatefulWidget] 的 [build] 函數(shù)中定義閉包,那么該閉包將隱式捕獲 `this`,即當(dāng)前小部件實(shí)例,
  /// 并且在范圍內(nèi)具有該實(shí)例的(不可變的)字段:
  ///
  /// ```dart
  /// //(這不是有效的 Flutter 代碼)
  /// class MyButton extends StatefulWidgetX {
  ///   MyButton({super.key, required this.color});
  ///
  ///   final Color color;
  ///
  ///   @override
  ///   Widget build(BuildContext context, State state) {
  ///     return SpecialWidget(
  ///       handler: () { print('color: $color'); },
  ///     );
  ///   }
  /// }
  /// ```
  ///
  /// 例如,假設(shè)父級(jí)以藍(lán)色構(gòu)建 `MyButton`,則打印函數(shù)中的 `$color` 引用藍(lán)色,正如預(yù)期的那樣。
  /// 現(xiàn)在,假設(shè)父級(jí)以綠色重建 `MyButton`。第一次構(gòu)建創(chuàng)建的閉包仍然隱式引用原始小部件,
  /// 并且 `$color` 仍然打印藍(lán)色,即使小部件已更新為綠色;如果該閉包的壽命超過(guò)其小部件,它將打印過(guò)時(shí)的信息。
  ///
  /// 相比之下,在 [State] 對(duì)象上的 [build] 函數(shù)中創(chuàng)建的閉包隱式捕獲 [State] 實(shí)例而不是小部件實(shí)例:
  ///
  /// ```dart
  /// class MyButton extends StatefulWidget {
  ///   const MyButton({super.key, this.color = Colors.teal});
  ///
  ///   final Color color;
  ///   // ...
  /// }
  ///
  /// class MyButtonState extends State<MyButton> {
  ///   // ...
  ///   @override
  ///   Widget build(BuildContext context) {
  ///     return SpecialWidget(
  ///       handler: () { print('color: ${widget.color}'); },
  ///     );
  ///   }
  /// }
  /// ```
  ///
  /// 現(xiàn)在,當(dāng)父級(jí)以綠色重建 `MyButton` 時(shí),第一次構(gòu)建創(chuàng)建的閉包仍然引用 [State] 對(duì)象,其在重建過(guò)程中保留,
  /// 但框架已將該 [State] 對(duì)象的 [widget] 屬性更新為引用新的 `MyButton` 實(shí)例,`${widget.color}` 打印綠色,正如預(yù)期的那樣。
  ///
  /// 另請(qǐng)參見:
  ///
  ///  * [StatefulWidget],其中包含有關(guān)性能考慮的討論。
  
  Widget build(BuildContext context);

  /// 當(dāng)此 [State] 對(duì)象的依賴項(xiàng)發(fā)生更改時(shí)調(diào)用。
  ///
  /// 例如,如果上一次調(diào)用 [build] 引用了稍后更改的 [InheritedWidget],框架將調(diào)用此方法通知此對(duì)象有關(guān)更改的信息。
  ///
  /// 此方法在 [initState] 之后立即調(diào)用??梢詮拇朔椒ㄖ邪踩卣{(diào)用 [BuildContext.dependOnInheritedWidgetOfExactType]。
  ///
  /// 由于框架始終在依賴項(xiàng)更改后調(diào)用 [build],所以子類很少覆蓋此方法。有些子類確實(shí)會(huì)覆蓋此方法,因?yàn)楫?dāng)其依賴項(xiàng)發(fā)生更改時(shí),它們需要進(jìn)行一些昂貴的工作(例如網(wǎng)絡(luò)獲?。?/span>
  /// 而且對(duì)于每次構(gòu)建來(lái)說(shuō),這些工作的成本太高了。

  
  
  void didChangeDependencies() {}

  
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    assert(() {
      properties.add(EnumProperty<_StateLifecycle>(
          'lifecycle state', _debugLifecycleState,
          defaultValue: _StateLifecycle.ready));
      return true;
    }());
    properties
        .add(ObjectFlagProperty<T>('_widget', _widget, ifNull: 'no widget'));
    properties.add(ObjectFlagProperty<StatefulElement>('_element', _element,
        ifNull: 'not mounted'));
  }
}

由于Blog編輯器頁(yè)面性能難以支持更多源碼,僅提供以上部分。

到了這里,關(guān)于Flutter源碼分析筆記:Widget類源碼分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • flutter筆記-萬(wàn)物皆是widget

    flutter筆記-萬(wàn)物皆是widget

    這篇文章后就不見寫了,學(xué)flutter主要是為了更好的使用 flutter-webrtc,所以到這里基本就了解了大部分的知識(shí),后續(xù)邊用邊查; 在flutter中所有的view都叫widget,類似文本組件Text也是集成自widget; 創(chuàng)建第一個(gè)項(xiàng)目如下: runApp傳入的參數(shù)就是一個(gè)Widget;所以我們可以傳入Text,示例

    2024年04月28日
    瀏覽(17)
  • Flutter學(xué)習(xí)四:Flutter開發(fā)基礎(chǔ)(一)Widget

    Flutter學(xué)習(xí)四:Flutter開發(fā)基礎(chǔ)(一)Widget

    目錄 0 引言 1 Widget 簡(jiǎn)介 1.1 Widget 概念 1.2?Widget 接口 1.3?Flutter中的四棵樹 1.4?StatelessWidget 1.4.1 簡(jiǎn)介 1.4.2?Context上下文 1.5?StatefulWidget 1.6??State 1.6.1 簡(jiǎn)介 1.6.2?State生命周期 1.7??在 widget 樹中獲取State對(duì)象 1.7.1 通過(guò)Context獲取 1.7.2 通過(guò)GlobalKey獲取 1.8?通過(guò) RenderObject 自定義 Wid

    2024年02月16日
    瀏覽(26)
  • Flutter網(wǎng)絡(luò)請(qǐng)求框架Dio源碼分析以及封裝(一)--請(qǐng)求流程分析

    利用flutter開發(fā)app也已經(jīng)有些時(shí)間了,這個(gè)過(guò)程中最多接觸到的就是網(wǎng)絡(luò)請(qǐng)求相關(guān)的代碼。自己目前項(xiàng)目中使用的是現(xiàn)在市面上最流行的網(wǎng)絡(luò)請(qǐng)求庫(kù)-dio,相對(duì)于flutter自帶的HttpClient來(lái)說(shuō),dio使用起來(lái)更簡(jiǎn)單,功能更強(qiáng)大,支持全局配置、Restful API、FormData、攔截器、 請(qǐng)求取消、

    2024年02月09日
    瀏覽(24)
  • Flutter網(wǎng)絡(luò)請(qǐng)求框架Dio源碼分析以及封裝(二)--Cookie管理分析

    上一篇文章我們簡(jiǎn)單分析了一下Dio發(fā)出請(qǐng)求時(shí)的大致工作流程,這個(gè)只是Dio最基本的功能,而且我們還沒有分析走到httpClientAdapter之后的內(nèi)容。不過(guò)不用著急,這次我們先接著上一次的內(nèi)容,看一下Dio當(dāng)中Cookie管理的問題,因?yàn)橹霸陧?xiàng)目中碰到了這個(gè)問題,回過(guò)頭來(lái)再?gòu)脑?/p>

    2024年02月08日
    瀏覽(20)
  • 透過(guò)源碼理解Flutter中widget、state和element的關(guān)系

    透過(guò)源碼理解Flutter中widget、state和element的關(guān)系

    Flutter中widget、state、element的源碼位于framework.dart中,整個(gè)文件6693行(版本Flutter 3.12.0-14.0.pre.28)。整個(gè)代碼可劃分為若干部分,主要包括key、widget、state、element四部分。 關(guān)于key的代碼65行到272行,這里的key包括ObjectKey、GlobalKey、LabeledGlobalKey、GlobalObjectKey。整個(gè)key體系的代碼還包

    2024年02月12日
    瀏覽(19)
  • flutter開發(fā)實(shí)戰(zhàn)-父子Widget組件調(diào)用方法

    flutter開發(fā)實(shí)戰(zhàn)-父子Widget組件調(diào)用方法

    flutter開發(fā)實(shí)戰(zhàn)-父子Widget組件調(diào)用方法 在最近開發(fā)中遇到了需要父組件調(diào)用子組件方法,子組件調(diào)用父組件的方法。這里記錄一下方案。 父組件使用globalKey.currentState調(diào)用子組件具體方法,子組件通過(guò)方法回調(diào)callback方法調(diào)用父組件的方法。 例如示例中的 例如父組件 父組件使

    2024年02月15日
    瀏覽(29)
  • 【Flutter 開發(fā)實(shí)戰(zhàn)】Dart 基礎(chǔ)篇:List 詳解

    【Flutter 開發(fā)實(shí)戰(zhàn)】Dart 基礎(chǔ)篇:List 詳解

    嗨,各位朋友們,歡迎來(lái)到這篇博客!今天我們將一起踏入 Dart 語(yǔ)言的神奇世界,深入了解 Dart 中的 List 類型。不用擔(dān)心,我會(huì)盡可能用最通俗易懂的語(yǔ)言,讓你對(duì) List 有一個(gè)更深刻的理解。 List - 讓數(shù)據(jù)變得有趣! 在 Dart 語(yǔ)言中,List 就像一個(gè)神奇的盒子,可以放進(jìn)去各種

    2024年01月21日
    瀏覽(25)
  • flutter開發(fā)實(shí)戰(zhàn)-獲取Widget的大小及位置

    flutter開發(fā)實(shí)戰(zhàn)-獲取Widget的大小及位置 最近開發(fā)過(guò)程中需要獲取Widget的大小及位置,這時(shí)候就需要使用到了GlobalKey了和WidgetsBinding.instance.addPostFrameCallback了 該函數(shù)的作用: flutter中的界面組件Widget每一幀渲染在界面上,addPostFrameCallback功能是在每一幀繪制完成后再回調(diào)執(zhí)行自定

    2024年02月13日
    瀏覽(23)
  • flutter開發(fā)實(shí)戰(zhàn)-RepaintBoundary實(shí)現(xiàn)Widget截圖功能

    flutter開發(fā)實(shí)戰(zhàn)-RepaintBoundary實(shí)現(xiàn)Widget截圖功能

    flutter開發(fā)實(shí)戰(zhàn)-RepaintBoundary實(shí)現(xiàn)Widget截圖功能 在開發(fā)中,遇到需要使用截圖,像iOS可以截圖UIView獲取到UIImage,在flutter中可以使用RepaintBoundary實(shí)現(xiàn)截圖功能 相機(jī)拍攝的圖片: RepaintBoundary截圖后的圖片 RepaintBoundary是繪制邊界。 如果CustomPaint有子節(jié)點(diǎn),為了避免子節(jié)點(diǎn)不必要的

    2024年02月15日
    瀏覽(33)
  • flutter開發(fā)實(shí)戰(zhàn)-BackdropFilter高斯模糊子Widget控件

    flutter開發(fā)實(shí)戰(zhàn)-BackdropFilter高斯模糊子Widget。 最近開發(fā)過(guò)程中遇到需要將控件進(jìn)行模糊,比如iOS的effect的模糊效果。那在flutter中就需要用到了BackdropFilter BackdropFilter屬性定義 其中ImageFilter的filter是必須傳的,child為子控件。 ImageFilter一下兩種兩種構(gòu)造方法 設(shè)置背景高斯模糊

    2024年02月14日
    瀏覽(21)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包