Widget是什么
Widget 是 Flutter 功能的抽象描述丧没,是視圖的配置信息鹰椒,同樣也是數(shù)據(jù)的映射,是 Flutter 開發(fā)框架中最基本的概念呕童。前端框架中常見的名詞漆际,比如視圖(View)、視圖控制器(View Controller)夺饲、活動(Activity)奸汇、應(yīng)用(Application)、布局(Layout)等往声,在 Flutter 中都是 Widget擂找。事實上,F(xiàn)lutter 的核心設(shè)計思想便是一切皆 Widget浩销。所以贯涎,我們學(xué)習(xí) Flutter,首先得從學(xué)會使用 Widget 開始慢洋。
Widget渲染過程
我們進行App開發(fā)時塘雳,最關(guān)注的問題就是:如何結(jié)構(gòu)化的組織視圖數(shù)據(jù),提供給渲染引擎從而完成頁面繪制且警,通常情況下粉捻,不同的UI框架處理方式也不同,但都會用到視圖樹(View Tree)的思想斑芜。
Flutter將視圖樹的概念進行了擴展肩刃,把視圖數(shù)據(jù)的組織和渲染抽象為:Widget,Element杏头,RenderObject三個部分盈包。他們的關(guān)系如下圖所示:
Widget
Widget 是 Flutter 世界里對視圖的一種結(jié)構(gòu)化描述,你可以把它看作是前端中的“控件”或“組件”醇王。Widget 是控件實現(xiàn)的基本邏輯單位呢燥,里面存儲的是有關(guān)視圖渲染的配置信息,包括布局寓娩、渲染屬性叛氨、事件響應(yīng)信息等呼渣。Flutter 將 Widget 設(shè)計成不可變的,所以當視圖渲染的配置信息發(fā)生變化時寞埠,F(xiàn)lutter 會選擇重建 Widget 樹的方式進行數(shù)據(jù)更新屁置,以數(shù)據(jù)驅(qū)動 UI 構(gòu)建的方式簡單高效。但是仁连,因為涉及到大量對象的銷毀和重建蓝角,所以會對垃圾回收造成壓力。不過饭冬,Widget 本身并不涉及實際渲染位圖使鹅,所以它只是一份輕量級的數(shù)據(jù)結(jié)構(gòu),重建的成本很低昌抠。另外患朱,由于 Widget 的不可變性,可以以較低成本進行渲染節(jié)點復(fù)用扰魂,因此在一個真實的渲染樹中可能存在不同的 Widget 對應(yīng)同一個渲染節(jié)點的情況麦乞,這無疑又降低了重建 UI 的成本。
Element
Element是Widget的一個實例化對象劝评,承載視圖構(gòu)建的上下文數(shù)據(jù)。
Flutter渲染過程倦淀,可以分為三步
- 首頁通過Widget樹生成對應(yīng)的Element樹
- 然后創(chuàng)建相應(yīng)的RenderObject并關(guān)聯(lián)到Element.renderObject屬性上
- 最后構(gòu)建成RenderObject樹蒋畜,從而完成最終的渲染
Element同時持有Widget和RenderObject。但真正負責最后渲染的其實是RenderObject撞叽。既然如此姻成,為什么需要Element呢?
因為Widget具有不可變性愿棋,Element卻是可變的科展,Element樹這一層將Widget樹的變化做了抽象,可以只將真正修改的部分同步到真實的RenderObject樹上糠雨,最大程度降低對真實渲染視圖的修改才睹,提高渲染效率,而不是銷毀整個視圖樹重建甘邀。
Widget中的State
Widget有StatelessWidget和StatefulWidget兩種類型琅攘。StatefulWidget應(yīng)對有交互需要動態(tài)變化效果的場景,StatelessWidget用于處理靜態(tài)的松邪,無狀態(tài)的試圖展示坞琴。在 Flutter 中,Widget 采用由父到子逗抑、自頂向下的方式進行構(gòu)建剧辐,父 Widget 控制著子 Widget 的顯示樣式寒亥,其樣式配置由父 Widget 在構(gòu)建時提供。用這種方式構(gòu)建出的 Widget荧关,
有些(比如 Text溉奕、Container、Row羞酗、Column 等)在創(chuàng)建時腐宋,除了這些配置參數(shù)之外不依賴于任何其他信息,換句話說檀轨,它們一旦創(chuàng)建成功就不再關(guān)心胸竞、也不響應(yī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的使用钙皮。
總結(jié)
命令式編程強調(diào)精確控制過程細節(jié)蜂科;而聲明式編程強調(diào)通過意圖輸出結(jié)果整體。對應(yīng)到 Flutter 中株灸,意圖是綁定了組件狀態(tài)的 State崇摄,結(jié)果則是重新渲染后的組件。在 Widget 的生命周期內(nèi)慌烧,應(yīng)用到 State 中的任何更改都將強制 Widget 重新構(gòu)建逐抑。其中,對于組件完成創(chuàng)建后就無需變更的場景屹蚊,狀態(tài)的綁定是可選項厕氨。這里“可選”就區(qū)分出了 Widget 的兩種類型进每,即:StatelessWidget 不帶綁定狀態(tài),而 StatefulWidget 帶綁定狀態(tài)命斧。當你所要構(gòu)建的用戶界面不隨任何狀態(tài)信息的變化而變化時田晚,需要選擇使用 StatelessWidget,反之則選用 StatefulWidget国葬。前者一般用于靜態(tài)內(nèi)容的展示贤徒,而后者則用于存在交互反饋的內(nèi)容呈現(xiàn)中。