本介绍描述了 Flutter 所使用的声明式风格与许多其他 UI 框架所使用的命令式风格之间的概念差异。

为什么要用声明式 UI?

#

从 Win32 到 Web,再到 Android 和 iOS 的框架,通常都采用命令式 UI 编程风格。这可能是你最熟悉的风格——你手动构建一个功能齐全的 UI 实体,例如 UIView 或等效实体,然后在 UI 改变时使用方法和 setter 对其进行变异。

为了减轻开发者编程如何在各种 UI 状态之间转换的负担,Flutter 则让开发者描述当前的 UI 状态,并将转换留给框架。

然而,这需要稍微改变一下操作 UI 的思维方式。

如何在声明式框架中改变 UI

#

请看下面的简化示例

View B (contained by view A) morphs from containing two views, c1 and c2, to containing only view c3.

在命令式风格中,你通常会找到 ViewB 的所有者,使用选择器或 `findViewById` 或类似方法检索实例 `b`,并对其调用变异(并隐式使其失效)。例如

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

你可能还需要在 ViewB 的构造函数中复制此配置,因为 UI 的真理源可能会比实例 `b` 本身更长寿。

在声明式风格中,视图配置(例如 Flutter 的 Widget)是不可变的,并且只是轻量级的“蓝图”。要改变 UI,widget 会触发自身的重建(在 Flutter 中最常见的是通过在 StatefulWidget 上调用 `setState()`),并构建一个新的 Widget 子树。

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

在这里,当 UI 改变时,Flutter 不是变异旧实例 `b`,而是构造新的 Widget 实例。框架在幕后使用 RenderObject 管理传统 UI 对象的许多职责(例如维护布局状态)。RenderObject 在帧之间持续存在,Flutter 的轻量级 Widget 会告诉框架在状态之间变异 RenderObject。Flutter 框架会处理其余部分。