- Flutter開發實例解析
- 王睿
- 3875字
- 2021-11-12 11:11:37
2.3 Flutter組件化
Flutter框架在設計時參考了前端流行的React框架,因此Flutter也是一個組件化框架,在Flutter中一切皆組件(Widget)。
組件表示一個可復用的功能單元,可以是UI組件,如文本、圖片,也可以是純功能組件,如路由、狀態管理器。需要注意的是,通常組件由Component一詞表示,Widget一般表示圖形控件。Flutter中的命名習慣有一點特殊,它將Widget作為組件的意思。因此可以理解為React中的Component對應于Flutter中的Widget。
對于組件來說,有3個核心要素。
1)屬性:父組件可以向子組件傳遞一些數據或回調方法,供子組件使用。通過屬性,數據能夠在組件樹中一層層向下傳遞。
2)狀態:位于組件內部,僅供組件內部使用的狀態變量。比如一個表單組件,需要將用戶輸入暫存在狀態中,在用戶單擊“提交”按鈕時統一提交。狀態通過setState方法更新,更新后會觸發布局重新構建。
3)布局構建:在組件中包含build方法,用于構建UI布局。在build方法中,根據父組件傳入的屬性,以及組件內部的狀態,生成對應的布局。每當屬性或狀態發生變化時,build方法都會重新執行,這是一種響應式布局開發模式。
在Flutter中提供了3大類組件:StatelessWidget、StatefulWidget、InheritedWidget,其中前兩種最為常用,在本節中進行講解。第三種將在后續章節中進行介紹。
2.3.1 組件化思想
傳統移動端開發是基于頁面加視圖的概念來開發的。比如在Android中,頁面的載體是Activity,頁面的視圖元素則由眾多視圖(View)組成。
隨著前端React、Vue的流行,它們所采取的組件化思想迅速流行起來。組件化思想概括來說就是“一切皆組件”,在應用中不再區別頁面與視圖,統統都是組件。
前端組件化相較于傳統移動端開發,具備一定優勢,具體包括以下幾點。
1)概念簡化:在傳統的Android開發中,需要了解不同的概念,如Activity、Application、ViewGroup等,只有把這些都研究透徹才能夠進行開發。而在組件化架構中,這些概念都被統一成組件,頁面也是組件、視圖也是組件,它們以同樣的原理工作。通過這種簡化,降低了框架的復雜性,也降低了開發者的學習成本。
2)函數式思想:組件化架構通過統一概念,可以以一種函數式嵌套的形式進行搭建。這種搭建方式相較于傳統架構方式,不僅更加優雅清晰,同時為更高級的特性提供了支撐。
3)聲明式UI:在傳統開發中,開發者需要手動維護界面元素的布局和內容,尤其是涉及元素增減時,開發者需要自己知道哪些要改變。當數據變化時,開發者也要手動更新對應的視圖元素。有了聲明式UI,開發者只需要維護組件間的關系,以及建立組件與狀態間的響應式關聯。當視圖需要改變時,通過Virtual DOM機制,框架會自動進行視圖變更。同時基于響應式訂閱,數據變化時視圖組件能夠自動更新,大大提高了開發效率。
目前業界普遍認可組件化思想是一種更為先進的應用開發模式,能夠大大提高開發效率。
2.3.2 無狀態組件StatelessWidget
StatelessWidget是最簡單的組件形式,它不包含狀態,僅接收來自父組件的輸入屬性,并構建布局展示。無狀態組件在實際中被大量應用,由于僅有輸入、輸出,不保存中間狀態,行為像函數一樣,在同類的React框架中將這種組件稱為函數式組件。
無狀態組件繼承StatelessWidget類,并覆寫build方法,在build方法中進行布局創建。比如創建標題組件(H1、H2),分別表示不同等級的標題,具體代碼如下:


在實際使用時,用一個縱向排列組件的Column布局,對H1和H2組件的實例進行封裝,并分別向兩個標題組件傳入不同的輸入文本,具體代碼如下:

運行代碼,效果如圖2-2所示。

圖2-2 無狀態組件示例
2.3.3 有狀態組件StatefulWidget
StatefulWidget表示含有狀態的組件,實際中也被大量應用。在Flutter中要創建有狀態組件,首先從StatefulWidget中派生一個類,并覆寫類的createState方法。之后要再基于State類派生一個狀態類,并將這個類的實例作為createState方法的返回值。在狀態類中進行兩項工作,首先是定義狀態,并覆寫build方法進行布局搭建。具體代碼示例如下:

在上面的代碼中,可以看到在StatefulWidget中,布局構建build作為State類的方法,這是Flutter中的一個特點,也是與其他框架(如React)的使用差異。在Flutter中,StatefulWidget類只用于接收輸入屬性,而狀態維護與布局渲染均放到State類中進行。運行上述代碼,代碼執行效果如圖2-3所示。
在上述代碼運行程序中,最開始屏幕上僅有一個“+1”按鈕,隨著不斷單擊,每次單擊totalCount的值會遞增,并通過setState方法更新狀態,狀態更新又會觸發build重新構建,在build重新構建中,會根據totalCount的值判斷“Bingo”文本是否展示。
2.3.4 組件的生命周期
組件生命周期指的是組件創建、展示、銷毀的過程。這個過程需要在合適的時機進行初始化、資源釋放等操作。如果在組件銷毀時沒有將該釋放的資源釋放,垃圾回收器(GC)就很可能無法回收組件,從而造成內存泄漏問題。

圖2-3 有狀態組件示例
生命周期概念只存在于StatefulWidget,StatelessWidget是沒有生命周期概念的。在首次創建StatefulWidget組件時,首先會調用其構造方法,之后調用createState方法創建狀態實例。
StatefulWidget包含有以下生命周期方法。
1 createState狀態創建生命周期
創建狀態實例,在組件的創建過程中只調用一次。代碼實現如下:

2 initState狀態初始化生命周期
initState是State中的方法,是組件的創建生命周期,在組件創建過程中只被調用一次。需要注意的是,在這個方法中還無法使用BuildContext。在實際應用中,通常在這個方法里發起網絡請求,或訂閱狀態管理器的數據。代碼實現如下:

3 didChangeDependencies依賴變化生命周期
當組件第一次創建時,didChangeDependencies會在initState之后調用。除此之外,如果組件依賴InheritedWidget,當InheritedWidget發生變化時,會觸發didChangeDependencies方法。
4 build布局構建生命周期
build方法用于創建頁面布局。組件第一次創建的時候,會調用一次build方法。之后每當狀態發生改變時build都會被調用。有兩種方式能觸發狀態改變,一是使用setState更新狀態,二是didUpdateWidget被調用。
5 didUpdateWidget組件更新生命周期
當父組件觸發重建時會觸發子組件的didUpdateWidget。在didUpdateWidget方法中,會以參數的形式傳入舊組件,供開發者進行對比。
6 setState狀態更新方法
setState方法用于進行狀態更新,將要更新的狀態傳入setState中進行更新,會觸發調用build方法更新布局。例如,在前面的計數器示例中,調用setState更新累計計數,代碼實現如下:

7 deactivate組件失活生命周期
當組件將要從組件樹中刪除時觸發,意味著組件將要被銷毀。但這不表示組件一定會被銷毀,比如當組件從組件樹中移除,觸發了deactivate生命周期,隨后在當前幀結束之前又被插入回組件樹,此時盡管調用了deactivate方法,但事實上組件實例并沒有被銷毀,依舊在組件樹上。
8 dispose銷毀生命周期
當組件被永遠從狀態樹中移除時觸發,表示組件被銷毀。通常在這個方法中進行一些資源釋放操作,如釋放監聽、定時器等。
9 StatefulWidget生命周期流轉圖
StatefulWidget從創建到最終銷毀的各個生命周期,可以梳理為整體生命周期流轉圖,如圖2-4所示。在實際開發中,build是最常用的生命周期,用于創建組件的布局。initState和dispose也經常使用,用于狀態的初始化與銷毀。需要注意的是,一些在initState中創建的對象,如定時器、監聽等,需要在dispose時及時釋放,從而避免內存泄漏。當需要更新狀態時,調用setState方法,會出發build方法進行布局更新。

圖2-4 StatefulWidget生命周期流轉圖
需要注意的是,生命周期系列方法中不包含狀態類的構造函數,在狀態類的構造函數中訪問不到Widget的輸入屬性。
2.3.5 Material和Cupertino組件庫
Flutter提供了兩套UI組件庫,Material組件庫和Cupertino組件庫,前者用于開發Android Material Design設計風格應用,后者用于開發iOS風格應用。采用哪套組件庫并不會影響跨端,開發者可根據自身應用的設計風格進行選擇。
由于Flutter采用自繪制,因此這兩套組件庫都非系統原生,而是Flutter自己又實現了一遍。兩套組件庫實現的質量很高,以至于做到與原生UI難以分辨的程度。
做到這種程度是非常不容易的,試想如何才能通過自繪制高度還原一個平臺的使用體驗呢?首先控件要長得像,比如按鈕、文本框等UI要與目標系統保持一致??丶L得像就做到高度還原了嗎?還不夠。不同平臺還有一些細膩的交互效果,比如整個應用的頁面過場方式,默認字體、主題等。把這些都做到才能真的實現以假亂真。
Flutter的組件庫設計非常細致,兩套組件庫都實現了以假亂真的效果。這兩套組件庫各提供了大量組件,但整體來說可以歸為三類:應用級組件、頁面級組件、視圖組件。
1 應用級組件
在Flutter組件樹中,最根級組件通常為WidgetsApp組件,用于提供App全局通用能力,比如路由、多語言、字體主題等。WidgetsApp是一個基類,兩套組件庫均基于它進行派生,Material組件庫對應的為MaterialApp組件,Cupertino組件庫對應的為CupertinoApp組件。這兩套組件在WidgetsApp組件的基礎上,對目標平臺下的默認字體、主題、過場動效等進行了詳細適配。
2 頁面級組件
在不同的設計風格下,頁面有著固定的展示方式。比如Material Design設計風格下的頁面,標題欄始終位于頂部,并且具有特定UI樣式,Fab位置始終位于右下方,且距離頁面邊距固定。對于這種固定的頁面組織形式,如果由開發者自行實現,會增加開發者的工作量,同時不同人開發出來的代碼展示效果也難以實現統一。
因此在Flutter組件庫中,提供了用于頁面架構布局的組件Scaffold。Scaffold意為頁面腳手架,Flutter為開發者搭建好了頁面框架,在使用時只需要填充設置項即可。這樣大大提高了開發效率,可以方便、快速地開發出高質量頁面。
由于不同平臺頁面展示風格不同,在兩套組件庫中分別提供了Scaffold組件。Material組件庫對應組件為Scaffold組件。Cupertino組件庫對應組件為CupertinoPageScaffold、CupertinoTab-Scaffold。
3 視圖級組件
組件庫中絕大多數組件都是視圖組件,用于展示屏幕中的不同視圖元素,比如按鈕、文本框、對話框等。兩套組件庫分別提供了各個平臺下的視圖組件。比如按鈕組件,在Cupertino下提供有CupertinoButton,在Material下提供有FlatButton、IconButton、FloatingActionButton、OutlineButton、RaisedButton等。兩套組件庫提供的組件可在官方文檔站中查看。
? Material組件庫: https://flutter.dev/docs/development/ui/widgets/material。
? Cupertino組件庫: https://flutter.dev/docs/development/ui/widgets/cupertino。
通過對比可以看出,Material組件庫中的組件要遠比Cupertino組件庫豐富。原因在于Flut-ter和Material Design都由Google出品,Material組件庫由Material Design官方團隊進行維護,因此質量相對更高。
使用了一個組件庫后,是不是就不能使用另一個組件庫中的組件了呢?并不是,組件庫組件是支持混用的。組件庫組件實際上都是Flutter組件,也就是說,不論是Material組件庫中的按鈕,還是Cupertino組件庫中的按鈕,它們的底層實現機制是相同的。在實際開發中組件混用也是經常采用的,比如本書中技術頭條實例,就在MaterialApp中使用了Cupertino搜索框組件,以實現更好的UI展現效果。
- Node.js Design Patterns
- Android項目開發入門教程
- Python數據可視化:基于Bokeh的可視化繪圖
- 信息可視化的藝術:信息可視化在英國
- Java虛擬機字節碼:從入門到實戰
- Reactive Programming With Java 9
- Visual C#通用范例開發金典
- GameMaker Essentials
- 現代C:概念剖析和編程實踐
- Maven for Eclipse
- R語言數據分析從入門到實戰
- HTML5+CSS+JavaScript深入學習實錄
- 計算機輔助設計與繪圖技術(AutoCAD 2014教程)(第三版)
- Netty 4核心原理與手寫RPC框架實戰
- Java項目驅動開發教程