Widget是什么
Widget 是 Flutter 功能的抽象描述,是視圖的配置信息,同樣也是數(shù)據(jù)的映射,是 Flutter 開發(fā)框架中最基本的概念。前端框架中常見的名詞,比如視圖(View)、視圖控制器(View Controller)、活動(Activity)、應用(Application)、布局(Layout)等,在 Flutter 中都是 Widget。事實上,F(xiàn)lutter 的核心設計思想便是一切皆 Widget。所以,我們學習 Flutter,首先得從學會使用 Widget 開始。
Widget渲染過程
我們進行App開發(fā)時,最關注的問題就是:如何結構化的組織視圖數(shù)據(jù),提供給渲染引擎從而完成頁面繪制,通常情況下,不同的UI框架處理方式也不同,但都會用到視圖樹(View Tree)的思想。
Flutter將視圖樹的概念進行了擴展,把視圖數(shù)據(jù)的組織和渲染抽象為:Widget,Element,RenderObject三個部分。他們的關系如下圖所示:
Widget
Widget 是 Flutter 世界里對視圖的一種結構化描述,你可以把它看作是前端中的“控件”或“組件”。Widget 是控件實現(xiàn)的基本邏輯單位,里面存儲的是有關視圖渲染的配置信息,包括布局、渲染屬性、事件響應信息等。Flutter 將 Widget 設計成不可變的,所以當視圖渲染的配置信息發(fā)生變化時,F(xiàn)lutter 會選擇重建 Widget 樹的方式進行數(shù)據(jù)更新,以數(shù)據(jù)驅動 UI 構建的方式簡單高效。但是,因為涉及到大量對象的銷毀和重建,所以會對垃圾回收造成壓力。不過,Widget 本身并不涉及實際渲染位圖,所以它只是一份輕量級的數(shù)據(jù)結構,重建的成本很低。另外,由于 Widget 的不可變性,可以以較低成本進行渲染節(jié)點復用,因此在一個真實的渲染樹中可能存在不同的 Widget 對應同一個渲染節(jié)點的情況,這無疑又降低了重建 UI 的成本。
Element
Element是Widget的一個實例化對象,承載視圖構建的上下文數(shù)據(jù)。
Flutter渲染過程,可以分為三步
- 首頁通過Widget樹生成對應的Element樹
- 然后創(chuàng)建相應的RenderObject并關聯(lián)到Element.renderObject屬性上
- 最后構建成RenderObject樹,從而完成最終的渲染
Element同時持有Widget和RenderObject。但真正負責最后渲染的其實是RenderObject。既然如此,為什么需要Element呢?
因為Widget具有不可變性,Element卻是可變的,Element樹這一層將Widget樹的變化做了抽象,可以只將真正修改的部分同步到真實的RenderObject樹上,最大程度降低對真實渲染視圖的修改,提高渲染效率,而不是銷毀整個視圖樹重建。
Widget中的State
Widget有StatelessWidget和StatefulWidget兩種類型。StatefulWidget應對有交互需要動態(tài)變化效果的場景,StatelessWidget用于處理靜態(tài)的,無狀態(tài)的試圖展示。在 Flutter 中,Widget 采用由父到子、自頂向下的方式進行構建,父 Widget 控制著子 Widget 的顯示樣式,其樣式配置由父 Widget 在構建時提供。用這種方式構建出的 Widget,
有些(比如 Text、Container、Row、Column 等)在創(chuàng)建時,除了這些配置參數(shù)之外不依賴于任何其他信息,換句話說,它們一旦創(chuàng)建成功就不再關心、也不響應任何數(shù)據(jù)變化進行重繪。這樣的 Widget 被稱為 StatelessWidget(無狀態(tài)組件)。示意圖如下:
有一些Widget(比如Image、Checkbox)的展示,除了父Widget初始化時傳入的靜態(tài)配置之外,還需要處理用戶的交互或其內(nèi)部的數(shù)據(jù)變化,并體現(xiàn)在UI上,示意圖如下:
從定義上來看,StatefulWidget好像是萬能,任何場景都可以使用StatefulWidget,事實果真如此嗎?
回顧前面提到的Widget更新機制如果我們的根布局是一個StatefulWidget,其State中每更新一次UI,都將是一整個頁面所有Widget的銷毀和重建。雖然Flutter內(nèi)部通過Element層可以最大程度的降低對真實渲染視圖的修改。但大量的Widget對象的銷毀和重建是無法避免的。所以,我們在開發(fā)過程中。要做到正確評估你的試圖呈現(xiàn)需求,避免無謂的StatefulWidget的使用。
總結
命令式編程強調(diào)精確控制過程細節(jié);而聲明式編程強調(diào)通過意圖輸出結果整體。對應到 Flutter 中,意圖是綁定了組件狀態(tài)的 State,結果則是重新渲染后的組件。在 Widget 的生命周期內(nèi),應用到 State 中的任何更改都將強制 Widget 重新構建。其中,對于組件完成創(chuàng)建后就無需變更的場景,狀態(tài)的綁定是可選項。這里“可選”就區(qū)分出了 Widget 的兩種類型,即:StatelessWidget 不帶綁定狀態(tài),而 StatefulWidget 帶綁定狀態(tài)。當你所要構建的用戶界面不隨任何狀態(tài)信息的變化而變化時,需要選擇使用 StatelessWidget,反之則選用 StatefulWidget。前者一般用于靜態(tài)內(nèi)容的展示,而后者則用于存在交互反饋的內(nèi)容呈現(xiàn)中。