本簡介將說明 Flutter 採用的宣告式(declarative)風格,與許多其他 UI 框架所使用的命令式(imperative)風格之間的概念差異。

為什麼選擇宣告式 UI?

#

從 Win32 到 Web、Android 及 iOS 等框架,通常都採用命令式的 UI 程式設計風格。這可能是你最熟悉的風格——你會手動建立一個功能完整的 UI 實體,例如 UIView 或類似物件,並在 UI 狀態改變時,透過方法與 setter 來修改它。

為了減輕開發者在不同 UI 狀態間切換時,必須手動編寫過渡邏輯的負擔,Flutter 採用不同的方式:讓開發者描述當前的 UI 狀態,並將狀態過渡的細節交由框架處理。

然而,這需要你在操作 UI 的思維上做出些許轉變。

如何在宣告式框架中變更 UI

#

請參考下方這個簡化的範例:

View B(由 View A 包含)從包含兩個視圖 c1 和 c2,變成只包含視圖 c3。

在命令式風格中,你通常會到 ViewB 的擁有者那裡,透過 selector 或與 findViewById 等方式取得 b 的實例,然後對其進行修改(並隱含地使其失效)。例如:

java
// Imperative style
b.setColor(red)
b.clearChildren()
ViewC c3 = new ViewC(...)
b.add(c3)

你可能也需要在 ViewB 的建構函式(constructor)中複製這個設定,因為 UI 的真實資料來源(source of truth)可能比 b 實例本身存活得更久。

在宣告式(declarative)風格中,視圖設定(例如 Flutter 的元件 (Widgets))是不可變的,且僅是輕量的「藍圖」。若要變更 UI,元件(Widget)會觸發自身重建(最常見的方式是在 Flutter 的 StatefulWidgets 上呼叫 setState()),並建立一個新的元件 (Widget) 子樹。

dart
// Declarative style
return ViewB(color: red, child: const ViewC());

在這裡,當 UI 發生變化時,Flutter 並不是去改變舊的實例 b
而是建立新的 Widget 實例。

這個框架在背後利用 RenderObjects 來管理許多傳統 UI 物件的職責(例如維護版面配置的狀態)。

RenderObjects 會在不同畫面幀之間持續存在,
而 Flutter 輕量級的 Widgets 則會告訴框架在不同狀態間去變更 RenderObjects。

剩下的部分則由 Flutter 框架自動處理。