<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                通過上一篇文章,我們已經深入理解了 Widget 是 Flutter 構建界面的基石,也認識了 Widget、Element、RenderObject 是如何互相配合,實現圖形渲染工作的。Flutter 在底層做了大量的渲染優化工作,使得我們只需要通過組合、嵌套不同類型的 Widget,就可以構建出任意功能、任意復雜度的界面。 同時,我們通過前面的學習,也已經了解到 Widget 有 StatelessWidget 和 StatefulWidget 兩種類型。StatefulWidget 應對有交互、需要動態變化視覺效果的場景,而 StatelessWidget 則用于處理靜態的、無狀態的視圖展示。StatefulWidget 的場景已經完全覆蓋了 StatelessWidget,因此我們在構建界面時,往往會大量使用 StatefulWidget 來處理靜態的視圖展示需求,看起來似乎也沒什么問題。 那么,StatelessWidget 存在的必要性在哪里?StatefulWidget 是否是 Flutter 中的萬金油?在今天這篇文章中,我將著重和你介紹這兩種類型的區別,從而幫你更好地理解 Widget,掌握不同類型 Widget 的正確使用時機。 ## UI 編程范式 要想理解 StatelessWidget 與 StatefulWidget 的使用場景,我們首先需要了解,在 Flutter 中,如何調整一個控件(Widget)的展示樣式,即 UI 編程范式。 如果你有過原生系統(Android、iOS)或原生 JavaScript 開發經驗的話,應該知道視圖開發是命令式的,需要精確地告訴操作系統或瀏覽器用何種方式去做事情。比如,如果我們想要變更界面的某個文案,則需要找到具體的文本控件并調用它的控件方法命令,才能完成文字變更。 下述代碼分別展示了在 Android、iOS 及原生 Javascript 中,如何將一個文本控件的展示文案更改為 Hello World: ~~~ // Android 設置某文本控件展示文案為 Hello World TextView textView = (TextView) findViewById(R.id.txt); textView.setText("Hello World"); // iOS 設置某文本控件展示文案為 Hello World UILabel *label = (UILabel *)[self.view viewWithTag:1234]; label.text = @"Hello World"; // 原生 JavaScript 設置某文本控件展示文案為 Hello World document.querySelector("#demo").innerHTML = "Hello World!"; ~~~ 與此不同的是,**Flutter 的視圖開發是聲明式的,其核心設計思想就是將視圖和數據分離,這與 React 的設計思路完全一致**。 對我們來說,如果要實現同樣的需求,則要稍微麻煩點:除了設計好 Widget 布局方案之外,還需要提前維護一套文案數據集,并為需要變化的 Widget 綁定數據集中的數據,使 Widget 根據這個數據集完成渲染。 但是,當需要變更界面的文案時,我們只要改變數據集中的文案數據,并通知 Flutter 框架觸發 Widget 的重新渲染即可。這樣一來,開發者將無需再精確關注 UI 編程中的各個過程細節,只要維護好數據集即可。比起命令式的視圖開發方式需要挨個設置不同組件(Widget)的視覺屬性,這種方式要便捷得多。 **總結來說,命令式編程強調精確控制過程細節;而聲明式編程強調通過意圖輸出結果整體。**對應到 Flutter 中,意圖是綁定了組件狀態的 State,結果則是重新渲染后的組件。在 Widget 的生命周期內,應用到 State 中的任何更改都將強制 Widget 重新構建。 其中,對于組件完成創建后就無需變更的場景,狀態的綁定是可選項。這里“可選”就區分出了 Widget 的兩種類型,即:StatelessWidget 不帶綁定狀態,而 StatefulWidget 帶綁定狀態。**當你所要構建的用戶界面不隨任何狀態信息的變化而變化時,需要選擇使用 StatelessWidget,反之則選用 StatefulWidget。**前者一般用于靜態內容的展示,而后者則用于存在交互反饋的內容呈現中。 接下來,我分別和你介紹 StatelessWidget 和 StatefulWidget,從源碼分析它們的區別,并總結一些關于 Widget 選型的基本原則。 ## StatelessWidget 在 Flutter 中,Widget 采用由父到子、自頂向下的方式進行構建,父 Widget 控制著子 Widget 的顯示樣式,其樣式配置由父 Widget 在構建時提供。 用這種方式構建出的 Widget,有些(比如 Text、Container、Row、Column 等)在創建時,除了這些配置參數之外不依賴于任何其他信息,換句話說,它們一旦創建成功就不再關心、也不響應任何數據變化進行重繪。在 Flutter 中,**這樣的 Widget 被稱為 StatelessWidget(無狀態組件)**。 這里有一張 StatelessWidget 的示意圖,如下所示: :-: ![](https://img.kancloud.cn/3e/c9/3ec97a9f584132c2bcdbca60fd2888cc_442x422.png) 圖 1 StatelessWidget 示意圖 接下來,我以 Text 的部分源碼為例,和你說明 StatelessWidget 的構建過程。 ~~~ class Text extends StatelessWidget { // 構造方法及屬性聲明部分 const Text(this.data, { Key key, this.textAlign, this.textDirection, // 其他參數 ... }) : assert(data != null), textSpan = null, super(key: key); final String data; final TextAlign textAlign; final TextDirection textDirection; // 其他屬性 ... @override Widget build(BuildContext context) { ... Widget result = RichText( // 初始化配置 ... ) ); ... return result; } } ~~~ 可以看到,在構造方法將其屬性列表賦值后,build 方法隨即將子組件 RichText 通過其屬性列表(如文本 data、對齊方式 textAlign、文本展示方向 textDirection 等)初始化后返回,之后 Text 內部不再響應外部數據的變化。 那么,什么場景下應該使用 StatelessWidget 呢? 這里,我有一個簡單的判斷規則:**父 Widget 是否能通過初始化參數完全控制其 UI 展示效果?**如果能,那么我們就可以使用 StatelessWidget 來設計構造函數接口了。 我準備了兩個簡單的小例子,來幫助你理解這個判斷規則。 第一個小例子是,我需要創建一個自定義的彈窗控件,把使用 App 過程中出現的一些錯誤信息提示給用戶。這個組件的父 Widget,能夠完全在子 Widget 初始化時將組件所需要的樣式信息和錯誤提示信息傳遞給它,也就意味著父 Widget 通過初始化參數就能完全控制其展示效果。所以,我可以采用繼承 StatelessWidget 的方式,來進行組件自定義。 第二個小例子是,我需要定義一個計數器按鈕,用戶每次點擊按鈕后,按鈕顏色都會隨之加深。可以看到,這個組件的父 Widget 只能控制子 Widget 初始的樣式展示效果,而無法控制在交互過程中發生的顏色變化。所以,我無法通過繼承 StatelessWidget 的方式來自定義組件。那么,這個時候就輪到 StatefulWidget 出場了。 ## StatefulWidget 與 StatelessWidget 相對應的,有一些 Widget(比如 Image、Checkbox)的展示,除了父 Widget 初始化時傳入的靜態配置之外,還需要處理用戶的交互(比如,用戶點擊按鈕)或其內部數據的變化(比如,網絡數據回包),并體現在 UI 上。 換句話說,這些 Widget 創建完成后,還需要關心和響應數據變化來進行重繪。在 Flutter 中,**這一類 Widget 被稱為 StatefulWidget(有狀態組件)**。這里有一張 StatefulWidget 的示意圖,如下所示: :-: ![](https://img.kancloud.cn/8a/e7/8ae7bf36f618a999da8847cbb4da4bf6_762x502.png) 圖 2 StatefulWidget 示意圖 看到這里,你可能有點困惑了。因為,我在上一篇文章“Widget,構建 Flutter 界面的基石”中和你分享到,Widget 是不可變的,發生變化時需要銷毀重建,所以談不上狀態。那么,這到底是怎么回事呢? 其實,StatefulWidget 是以 State 類代理 Widget 構建的設計方式實現的。接下來,我就以 Image 的部分源碼為例,和你說明 StatefulWidget 的構建過程,來幫助你理解這個知識點。 和上面提到的 Text 一樣,Image 類的構造函數會接收要被這個類使用的屬性參數。然而,不同的是,Image 類并沒有 build 方法來創建視圖,而是通過 createState 方法創建了一個類型為 \_ImageState 的 state 對象,然后由這個對象負責視圖的構建。 這個 state 對象持有并處理了 Image 類中的狀態變化,所以我就以 \_imageInfo 屬性為例來和你展開說明。 \_imageInfo 屬性用來給 Widget 加載真實的圖片,一旦 State 對象通過 \_handleImageChanged 方法監聽到 \_imageInfo 屬性發生了變化,就會立即調用 \_ImageState 類的 setState 方法通知 Flutter 框架:“我這兒的數據變啦,請使用更新后的 \_imageInfo 數據重新加載圖片!”。而,Flutter 框架則會標記視圖狀態,更新 UI。 ~~~ class Image extends StatefulWidget { // 構造方法及屬性聲明部分 const Image({ Key key, @required this.image, // 其他參數 }) : assert(image != null), super(key: key); final ImageProvider image; // 其他屬性 ... @override _ImageState createState() => _ImageState(); ... } class _ImageState extends State<Image> { ImageInfo _imageInfo; // 其他屬性 ... void _handleImageChanged(ImageInfo imageInfo, bool synchronousCall) { setState(() { _imageInfo = imageInfo; }); } ... @override Widget build(BuildContext context) { final RawImage image = RawImage( image: _imageInfo?.image, // 其他初始化配置 ... ); return image; } ... } ~~~ 可以看到,在這個例子中,Image 以一種動態的方式運行:監聽變化,更新視圖。與 StatelessWidget 通過父 Widget 完全控制 UI 展示不同,StatefulWidget 的父 Widget 僅定義了它的初始化狀態,而其自身視圖運行的狀態則需要自己處理,并根據處理情況即時更新 UI 展示。 好了,至此我們已經通過 StatelessWidget 與 StatefulWidget 的源碼,理解了這兩種類型的 Widget。這時,你可能會問,既然 StatefulWidget 不僅可以響應狀態變化,又能展示靜態 UI,那么 StatelessWidget 這種只能展示靜態 UI 的 Widget,還有存在的必要嗎? ## StatefulWidget 不是萬金油,要慎用 對于 UI 框架而言,同樣的展示效果一般可以通過多種控件實現。從定義來看,StatefulWidget 仿佛是萬能的,替代 StatelessWidget 看起來合情合理。于是 StatefulWidget 的濫用,也容易因此變得順理成章,難以避免。 但事實是,StatefulWidget 的濫用會直接影響 Flutter 應用的渲染性能。 接下來,在今天這篇文章的最后,我就再帶你回顧一下 Widget 的更新機制,來幫你意識到完全使用 StatefulWidget 的代價: > Widget 是不可變的,更新則意味著銷毀 + 重建(build)。StatelessWidget 是靜態的,一旦創建則無需更新;而對于 StatefulWidget 來說,在 State 類中調用 setState 方法更新數據,會觸發視圖的銷毀和重建,也將間接地觸發其每個子 Widget 的銷毀和重建。 那么,這意味著什么呢? 如果我們的根布局是一個 StatefulWidget,在其 State 中每調用一次更新 UI,都將是一整個頁面所有 Widget 的銷毀和重建。 在上一篇文章中,我們了解到,雖然 Flutter 內部通過 Element 層可以最大程度地降低對真實渲染視圖的修改,提高渲染效率,而不是銷毀整個 RenderObject 樹重建。但,大量 Widget 對象的銷毀重建是無法避免的。如果某個子 Widget 的重建涉及到一些耗時操作,那頁面的渲染性能將會急劇下降。 因此,**正確評估你的視圖展示需求,避免無謂的 StatefulWidget 使用,是提高 Flutter 應用渲染性能最簡單也是最直接的手段**。 在接下來的第 29 篇文章“為什么需要做狀態管理,怎么做?”中,我會繼續帶你學習 StatefulWidget 常見的幾種狀態管理方法,與你更為具體地介紹在不同場景中,該選用何種 Widget 的基本原則。這些原則,你都可以根據實際需要應用到后續工作中。 ## 總結 好了,今天關于 StatelessWidget 與 StatefulWidget 的介紹,我們就到這里了。我們一起來回顧下今天的主要知識點。 首先,我帶你了解了 Flutter 基于聲明式的 UI 編程范式,并通過閱讀兩個典型 Widget(Text 與 Image)源碼的方式,與你一起學習了 StatelessWidget 與 StatefulWidget 的基本設計思路。 由于 Widget 采用由父到子、自頂向下的方式進行構建,因此在自定義組件時,我們可以根據父 Widget 是否能通過初始化參數完全控制其 UI 展示效果的基本原則,來判斷究竟是繼承 StatelessWidget 還是 StatefulWidget。 然后,針對 StatefulWidget 的“萬金油”誤區,我帶你重新回顧了 Widget 的 UI 更新機制。盡管 Flutter 會通過 Element 層去最大程度降低對真實渲染視圖的修改,但大量的 Widget 銷毀重建無法避免,因此避免 StatefulWidget 的濫用,是最簡單、直接地提升應用渲染性能的手段。 需要注意的是,除了我們主動地通過 State 刷新 UI 之外,在一些特殊場景下,Widget 的 build 方法有可能會執行多次。因此,我們不應該在這個方法內部,放置太多有耗時的操作。而關于這個 build 方法在哪些場景下會執行,以及為什么會執行多次,我會在下一篇文章“提到生命周期,我們是在說什么?”中,與你一起詳細分析。 ## 思考題 Flutter 工程應用模板是計數器示例應用 Demo,這個 Demo 的根節點是一個 StatelessWidget。請在保持原有功能的情況下,將這個 Demo 改造為根節點為 StatefulWidget 的 App。你能通過數據打點,得出這兩種方式的性能差異嗎?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看