📚 全栈开发学习系列
阶段一:编程基础 ✅ 已完成
01-08 路线总览 → Python → C语言 → 数据结构 → Git → Linux
阶段二:Web 全栈 ✅ 已完成
09-16 HTML → CSS → JavaScript → HTTP → FastAPI → PostgreSQL → 认证授权 → 博客系统
阶段三:前端深化 ✅ 已完成
17-25 React → TypeScript → Next.js → Tailwind → 工程化 → 测试 → API测试 → 状态管理
阶段四:跨平台 App
✓ 26 跨平台开发:React Native 与 Expo
✓ 27 React Native 进阶:导航与状态管理
28 Flutter 跨平台开发:Dart 语言与 Widget 体系(当前篇)
29 Flutter 进阶:状态管理与网络请求
阶段五:扩展 + 部署
Uni-app(小程序)→ Tauri(桌面)→ Docker Compose → CI/CD → 域名部署

Flutter 跨平台开发:Dart 语言与 Widget 体系

难度:进阶 | 技术栈:Flutter 3.47.2 / Dart 3.13.2 | 阶段四第 3 篇
读完本篇你将能:理解 Flutter 的渲染架构与 React Native 的本质区别,编写 Dart 3.13 基础语法并掌握 Primary Constructors 新特性,使用 StatelessWidget 和 StatefulWidget 构建声明式 UI,运用 Material 和 Cupertino 组件库搭建跨平台界面,组合 Container/Row/Column/Stack 等核心布局 Widget 完成实际页面。

前两篇我们走完了 React Native + Expo 的完整路径——从核心组件到嵌套导航再到持久化状态管理。React Native 的思路是"把 JavaScript 指令翻译成原生组件",就像一位翻译官站在 JS 和原生平台中间。Flutter 走了完全不同的路:它不借原生组件,而是自带画布,用自己的渲染引擎在画布上绘制每一个像素。这意味着 Flutter 在 Android 上画的按钮和在 iOS 上画的按钮,用的是同一套绘制代码,而非各自平台的原生控件。

这种"自带画布"的架构带来一个直接后果:Flutter 的编程语言不是 JavaScript,而是 Google 专为它打造的 Dart。所以本篇需要同时掌握两件事——一门新语言和一个全新的 UI 声明体系。别担心,你已有的 TypeScript 和 React 知识会让这个过渡比想象中轻松。

📑 本文目录
01Flutter 框架概述:架构分层与渲染管线
02Dart 语言基础:语法、类型与异步
03Widget 体系核心:声明式 UI 的三层抽象
04Material 与 Cupertino 组件库
05布局 Widget 实战:从容器到层叠
06常见错误与动手练习

Flutter 框架概述:架构分层与渲染管线

Flutter 是什么:自带画布的跨平台框架

Flutter 最初叫"Sky",是 Google 在 2015 年的一个实验项目,目标只有一个:在移动设备上实现每秒 60 帧的流畅渲染。2017 年正式发布 1.0 后,Flutter 逐步扩展到 Web、桌面端和嵌入式设备。截至本文写作时,最新稳定版是 Flutter 3.47.2(2026 年 8 月 27 日发布),搭载 Dart 3.13.2。

Flutter 与 React Native 最根本的区别在渲染策略。React Native 把 JS 组件映射到 iOS 的 UIView 或 Android 的 View——两个平台上的"按钮"长得不一样,因为它们就是不同的原生控件。Flutter 不借用任何原生 UI 组件,它用自带的渲染引擎(Impeller)直接在 Skia/Impeller 画布上绘制一切:按钮的文字、圆角、阴影、手势区域,全都是画出来的。这就是为什么 Flutter 应用在 Android 和 iOS 上看起来像素级一致。

三层架构:Framework → Engine → Embedder

Flutter 的代码栈分为三层,每层职责清晰:

层级 语言 职责
Framework Dart Widget 树、Material/Cupertino 组件、动画、手势、渲染层抽象
Engine C++ Impeller 渲染引擎、Dart 运行时、文本排版、Platform Channel 通信
Embedder 平台语言 将 Flutter 内容嵌入原生窗口(Android Activity / iOS UIKit / Windows HWND)

你在写 Flutter 应用时,90% 时间都在 Framework 层用 Dart 写 Widget。Engine 层你几乎不会直接接触,但它决定了你的应用性能——3.47 版本最大的变化是 Impeller 渲染引擎在桌面端(macOS/Windows/Linux)默认启用,之前它只在移动端是默认的。

💡 小贴士
3.47 之前,桌面端用 Skia 渲染,着色器在运行时编译,首次绘制可能卡顿。Impeller 预编译着色器到 .impeller 文件,消除了首帧卡顿。如果你的 Flutter 桌面应用之前有冷启动卡顿问题,升级到 3.47 会明显改善。

渲染管线:Widget → Element → RenderObject

Flutter 的渲染管线有三棵树,这是理解 Flutter 性能和调试的基础:

Widget 树
不可变描述
→
Element 树
生命周期管理
→
RenderObject 树
测量布局绘制
• Widget:不可变的 UI 配置描述。每次 setState 触发重建时,Flutter 会创建一棵全新的 Widget 树——但只是描述,不含任何渲染逻辑
• Element:Widget 的实例化对象,管理生命周期。通过 diff 算法对比新旧 Widget 树,只更新变化的部分——这和 React 的 Reconciliation 概念几乎一样
• RenderObject:真正干活的层。负责测量尺寸、计算布局、绘制到画布。Flutter 的布局约束(Constraint)和绘制(Paint)都在这里

对比 React Native:React Native 的渲染管线是 JS 组件 → Bridge → 原生组件,中间的 Bridge 序列化/反序列化开销是旧版 RN 的性能瓶颈。Flutter 的三棵树全在同一个 Dart 进程内,没有跨语言通信开销。这也是为什么 Flutter 的动画性能通常优于 React Native。

创建第一个 Flutter 项目

确认 Flutter SDK 已安装后,执行以下命令创建项目:

Shell
flutter create my_first_app
cd my_first_app
flutter run

flutter create 会生成一个计数器模板项目。在 macOS 上默认编译为 macOS 应用,用 flutter run -d chrome 可以切换到 Web,-d ios 切换到 iOS 模拟器。

Dart 语言基础:语法、类型与异步

你已经熟悉 TypeScript,Dart 的学习曲线会非常短。两者都是强类型、支持类型推断、有空安全机制的语言。但 Dart 有自己的设计取舍——比如它没有 union type,却有了更完善的面向对象体系。

变量与类型推断

Dart
var name = 'Flutter';  // 推断为 String
final version = 3.47;  // 运行时常量
const pi = 3.14;  // 编译时常量
int? count;  // 可空类型,初始为 null
late String label;  // 延迟初始化

四个关键区别于 TypeScript 的地方:final 只能赋值一次但运行时确定值,const 编译时就确定值——TS 没有这个区分。? 后缀表示可空,和 TS 的 string | null 类似但语法更简洁。late 表示"我保证用之前会赋值",跳过空安全检查但保留类型约束。

Dart 3.13 新特性:Primary Constructors

Dart 3.13 正式稳定了 Primary Constructors——这是从 Dart 3.12 实验特性转正的语法糖,让你一行代码定义带字段的类。先看传统写法:

Dart - 传统写法
1
2
3
4
5
6
7
8
9
class Point {
  final double x;
  final double y;
  Point(this.x, this.y);
  double distanceTo(Point other) =>
    sqrt(pow(x - other.x, 2) + pow(y - other.y, 2));
}

用 Primary Constructors 后,声明字段和构造函数压缩到类头一行:

Dart 3.13 - Primary Constructor
1
2
3
4
5
class Point(final double x, final double y);
extension PointExt on Point {
  double distanceTo(Point other) =>
    sqrt(pow(x - other.x, 2) + pow(y - other.y, 2));

参数 final double x 直接在类头声明,自动生成对应字段和构造函数。这里还用了 extension 为类追加方法——Dart 的扩展方法语法和 Swift 很像,不需要像 TypeScript 那样用 declaration merging。

空安全与类型系统

Dart 从 2.12 开始强制空安全(sound null safety),语法和 TS 类似但有差异:

概念 TypeScript Dart
可空类型 string | null String?
空值断言 value! value!
空值合并 a ?? b a ?? b
可选链 a?.b a?.b
联合类型 string | number 不支持,用 sealed class
💡 小贴士
Dart 没有 union type,但提供了 sealed class 作为替代方案。定义 sealed class Result 后,Dart 编译器会在 switch 中强制穷尽检查所有子类型,类似 Rust 的 enum。

异步编程:Future 与 Stream

Dart 的异步模型和 JS 几乎一样:Future 对应 Promise,async/await 语法完全相同。Stream 则对应 RxJS 的 Observable——用于多次发射事件的场景。

Dart - async/await
1
2
3
4
5
6
7
8
9
10
11
Future<String> fetchUser() async {
  await Future.delayed(Duration(seconds: 1));
  return '张三';
}
Stream<int> counter(int max) async* {
  for (var i = 0; i < max; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

注意 async* 后的星号——这是 Dart 的异步生成器语法,函数返回 Stream 而非 Future。yield 逐次发射值,消费者用 await for 或 .listen() 接收。在 Flutter 中,Stream 广泛用于定时器、传感器数据、WebSocket 等持续事件流的处理。

Widget 体系核心:声明式 UI 的三层抽象

你已掌握 React 的组件思想——JSX 描述 UI,state 变化触发重渲染。Flutter 的 Widget 体系几乎是同一套思路,只不过用 Dart 替代了 JSX,用 Widget 类替代了 React.Component。

一切皆 Widget

Flutter 的设计哲学是"一切皆 Widget"——对齐方式是 Widget(Center),边距是 Widget(Padding),手势检测是 Widget(GestureDetector),甚至主题色也是 Widget(Theme)。这和 React 中"一切皆组件"的理念完全一致,但 Flutter 走得更彻底——没有 CSS,没有单独的样式系统,所有视觉表现都通过组合 Widget 实现。

一个 Widget 的基本定义:

Dart - StatelessWidget
class Greeting extends StatelessWidget {
  final String name;
  const Greeting({super.key, required this.name});
  @override
  Widget build(BuildContext context) =>
    Text('你好,$name!');
}

几个关键点对应到 React 概念:build() 方法等于 React 的 render() 或函数组件本身。required 关键字等于 TS 中必填 props。super.key 是 Flutter 的标识系统,用于 Element 树的 diff 算法——作用类似 React 的 key prop。$name 是 Dart 的字符串插值,等价于 TS 的 ${name}。

StatelessWidget vs StatefulWidget

Flutter 的 Widget 分两类,选择哪一类取决于"UI 是否需要随时间变化":

对比项 StatelessWidget StatefulWidget
对应 React 函数组件(无 state) useState / class 组件
数据来源 仅构造函数参数 构造函数 + 内部 State
触发重建 父 Widget 传入新参数 setState() 调用
适用场景 静态文本、纯展示组件 表单、动画、计数器、交互

StatefulWidget 完整示例:计数器

flutter create 生成的默认模板就是一个计数器。拆解它的结构:

Dart - counter_app.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import 'package:flutter/material.dart';
class CounterApp extends StatefulWidget {
  const CounterApp({super.key});
  @override
  State<CounterApp> createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
  int _count = 0;
  void _increment() {
    setState(() { _count++; });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('计数器')),
      body: Center(
        child: Text('点击次数:$_count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

注意几个关键点:

1 两个类:CounterApp(Widget)和 _CounterAppState(State)。Flutter 的 StatefulWidget 强制拆成 Widget + State 两个类——Widget 是不可变的,State 持有可变数据。下划线前缀 _CounterAppState 是 Dart 的私有标记(文件级可见)
2 setState 是核心:setState() 通知框架"状态变了,请重新 build"。和 React 的 setState 概念完全一致——不调用它,UI 不会更新
3 嵌套 Widget 树:Scaffold → Center → Text 是父子关系。在 JSX 中你会写成 <Scaffold><Center><Text/></Center></Scaffold>,Dart 中用命名参数 child: 嵌套
4 const 优化:const Text('计数器') 告诉编译器这个 Widget 永远不变,可以复用同一个实例。Flutter 会在 Element 树 diff 时跳过 const Widget 的对比,提升性能
💡 小贴士
Dart 的命名参数用花括号包裹:{required this.name}。调用时必须写参数名:Greeting(name: '张三')。这比 JS 的位置参数更安全,重构时改参数顺序不会引入 bug。

State 生命周期

StatefulWidget 的 State 对象有一套生命周期回调,和 React 的 useEffect/useMount 类似但更细粒度:

Dart - State 生命周期
1
2
3
4
5
6
7
8
9
10
11
@override
void initState() {  // 插入树时调用一次
  super.initState();
  // 初始化 controller、发起请求
}
@override
void dispose() {  // 从树移除时调用
  // 释放 controller、取消订阅
  super.dispose();
}

initState 对应 React 的 useEffect(() => {}, [])(挂载时执行一次),dispose 对应 useEffect return 的清理函数(卸载时执行)。Flutter 还有 didChangeDependencies(依赖变化时)和 didUpdateWidget(父 Widget 更新时),但日常开发最常用的就是 initState 和 dispose 这对。

Material 与 Cupertino 组件库

上一章的计数器代码中出现了 Scaffold、AppBar、FloatingActionButton——这些都属于 Material 组件库。Flutter 内置了两套完整的 UI 组件系统:Material Design(Google 风格)和 Cupertino(iOS 风格)。在 3.47 版本中,这两套库迎来了重大变化。

Material Design 3:Google 的设计语言

Material 组件库遵循 Google 的 Material Design 3 设计规范。使用 Material 组件需要先在根节点包裹 MaterialApp,它会注入主题、路由、本地化等全局配置。核心组件一览:

结构组件
Scaffold(页面骨架)、AppBar(顶栏)、NavigationBar(底部导航)、Drawer(侧边栏)、BottomSheet(底部弹窗)
交互组件
FloatingActionButton(悬浮按钮)、ElevatedButton(提升按钮)、TextButton(文本按钮)、Switch(开关)、Slider(滑块)、Chip(标签)
展示组件
Card(卡片)、ListTile(列表项)、GridView(网格)、DataTable(数据表)、SnackBar(消息条)
输入组件
TextField(文本输入)、DropdownButton(下拉选择)、DatePicker(日期选择)、Checkbox(复选框)、RadioButton(单选按钮)

Cupertino:iOS 风格组件

如果你需要让 Flutter 应用看起来像原生 iOS 应用,使用 Cupertino 组件库。需要在根节点包裹 CupertinoApp 而非 MaterialApp。关键差异:

功能 Material Cupertino
页面骨架 Scaffold CupertinoPageScaffold
顶栏 AppBar CupertinoNavigationBar
按钮 ElevatedButton CupertinoButton
列表 ListView + ListTile CupertinoListSection

3.47 新变化:独立化包 material_ui / cupertino_ui

Flutter 3.47 将 Material 和 Cupertino 组件从核心 SDK 中解耦为独立的 pub 包——material_ui 和 cupertino_ui。3.47 版本中核心 SDK 仍包含这些库(向后兼容),但你可以选择 opt-in 使用独立包,获得更快的 bug 修复和组件更新——独立包按自己的周节奏发版,不必等 Flutter 核心版本。

YAML - pubspec.yaml (opt-in 独立包)
dependencies:
  flutter:
    sdk: flutter
  material_ui: ^1.0.0  # 独立 Material 包
  cupertino_ui: ^1.0.0  # 独立 Cupertino 包
💡 小贴士
新项目不需要立即迁移到独立包——核心 SDK 仍然包含 Material 和 Cupertino。独立包是为需要紧跟上游更新、或想减少核心依赖体积的成熟项目准备的。迁移指南可搜索关键词 "Flutter material_ui migration" 查看。

自适应:根据平台切换组件风格

大多数 Flutter 应用不需要"纯 Material"或"纯 Cupertino"——而是根据运行平台自适应。Flutter 提供了 Platform.isIOS 判断当前平台:

Dart - 自适应按钮
1
2
3
4
5
6
7
import 'dart:io' show Platform;
Widget buildButton(VoidCallback? onPressed) {
  return Platform.isIOS
    ? CupertinoButton(onPressed: onPressed, child: const Text('确认'))
    : ElevatedButton(onPressed: onPressed, child: const Text('确认'));
}

注意 import 'dart:io' 引入了 Platform 类——但 Web 端不支持 dart:io,如果你的 Flutter 项目同时编译 Web,需要用条件导入或 defaultTargetPlatform 替代。

布局 Widget 实战:从容器到层叠

Flutter 没有 CSS,所有布局都靠 Widget 组合实现。如果你熟悉 CSS Flexbox,Flutter 的布局体系会让你感到亲切——Row 就是 flex-direction:row,Column 就是 flex-direction:column。但 Flutter 把间距、对齐、弹性都拆成了独立的 Widget,而不是 CSS 属性。

Container:万能容器

Container 是 Flutter 中最常用的布局 Widget——它像 CSS 中的 div,可以同时设置 padding、margin、decoration(背景色/圆角/边框)、约束(宽高范围):

Dart - Container
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Container(
  width: 200,
  padding: const EdgeInsets.all(16),
  margin: const EdgeInsets.symmetric(vertical: 8),
  decoration: BoxDecoration(
    color: Colors.blue[50],
    borderRadius: BorderRadius.circular(12),
  ),
  child: const Text('内容'),
)
// 等价于 CSS:
// width:200px; padding:16px; margin:8px 0;
// background:#e3f2fd; border-radius:12px;

EdgeInsets 是 Flutter 替代 CSS margin/padding 值的工具:.all(16) 四方向均等,.symmetric(vertical: 8, horizontal: 12) 分纵横,.only(left: 4, top: 8) 指定方向。

Row 与 Column:弹性布局

Row 和 Column 是 Flutter 的 Flexbox 等价物。关键参数 mainAxisAlignment(主轴对齐)和 crossAxisAlignment(交叉轴对齐),对应 CSS 的 justify-content 和 align-items:

CSS Flexbox Flutter 说明
justify-content: center MainAxisAlignment.center 主轴居中
justify-content: space-between MainAxisAlignment.spaceBetween 两端对齐
align-items: center CrossAxisAlignment.center 交叉轴居中
flex: 1 Expanded(flex: 1) 弹性占据剩余空间
Dart - Row + Expanded
1
2
3
4
5
6
7
8
9
Row(
  mainAxisAlignment: MainAxisAlignment.spaceBetween,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    const Icon(Icons.person),
    Expanded(child: const Text('用户名')),
    IconButton(icon: const Icon(Icons.edit), onPressed: () {}),
  ],
)

Expanded 包裹的子 Widget 会占据主轴剩余空间——上面的例子中,"用户名"文本会填满图标和编辑按钮之间的所有空间。多个 Expanded 可以用 flex: 参数按比例分配空间。

Stack:层叠布局

CSS 的 position: relative/absolute 在 Flutter 中是 Stack + Positioned:

Dart - Stack + Positioned
Stack(
  children: [
    Image.network('https://example.com/bg.jpg'),
    Positioned(
      bottom: 16, right: 16,
      child: const CircleAvatar(child: Icon(Icons.edit)),
    ),
  ],
)

Stack 的第一个子元素是底层,后面的子元素依次叠在上面。Positioned 用 top/bottom/left/right 定位——和 CSS absolute 的偏移属性完全一致。Stack 适合做头像角标、图片标注、浮层引导等需要层叠的场景。

💡 小贴士
Flutter 布局有一个独特约束模型:父 Widget 向下传递约束("你的宽度范围是 0-375,高度不限"),子 Widget 向上返回尺寸("我选择 200x50")。这和 CSS 的"子元素影响父容器"的双向影响不同。理解这一点可以避免很多"为什么我的 Widget 没有撑满"的困惑——答案通常是:父 Widget 没有给它 Expanded 包裹。

常见错误与排坑指南

⚠️ 错误 1:StatefulWidget 修改数据后 UI 不刷新
在 React 中你直接 setState() 就行,Flutter 的概念类似但调用方式不同。初学者常犯的错误是直接修改 _count 字段却忘记包裹 setState()。
修复方式:所有会导致 UI 变化的数据修改必须放在 setState(() { ... }) 回调中。框架在回调结束后标记 Element 为 dirty,下一帧自动重建。
⚠️ 错误 2:异步 gaps 中使用 BuildContext
在 await 之后使用 context 是 Flutter 最常见的运行时崩溃之一。Widget 可能在 await 期间已经被卸载,此时访问 context 会抛出 BuildContext is not mounted。
修复方式:在 await 之后添加 if (!context.mounted) return; 守卫。这是 Dart 3 引入的标准模式,比旧版 mounted 检查更简洁。
⚠️ 错误 3:Row/Column 子元素溢出
当 Row 中的子 Widget 总宽度超出屏幕宽度时,Flutter 会在屏幕底部显示黄黑相间的警告条:RenderFlex overflowed by N pixels。这是 Flutter 约束模型的直接体现——父 Widget 给了无限宽度,子 Widget 不知道自己该缩小还是滚动。
修复方式:对需要弹性伸缩的子 Widget 包裹 Expanded 或 Flexible;对可能超出屏幕的内容用 SingleChildScrollView 包裹。
⚠️ 错误 4:在 build 方法中执行副作用
build() 方法会在每一帧被调用,如果在里面发网络请求、写文件、甚至 print 调试,会导致严重的性能问题和不可预测的行为。
修复方式:一次性副作用放 initState();监听外部数据源放 didChangeDependencies();清理资源放 dispose()。build() 只负责返回 Widget 树,不做任何其他事。

动手练习

Level 1:基础验证 — Dart 语法练习
在 DartPad(dartpad.dev)中完成以下任务,不安装任何东西即可练习:
1. 声明一个 Record 类型表示用户信息(name, age, isActive),并用 Primary Constructor 定义一个 User 类
2. 用 sealed class 定义一个 Result 联合类型(Success/Failure),写一个 switch 表达式处理两种分支
3. 用 async/await 写一个函数,模拟 2 秒延迟后返回一个随机整数
验证标准:代码无编译错误,DartPad 输出区显示正确结果。
Level 2:组合应用 — 个人名片卡片
用 Flutter 创建一个个人名片页面,综合运用本篇所有知识点:
1. 用 StatelessWidget 搭建静态名片布局(头像 + 姓名 + 职业标签)
2. 头像用 Stack + Positioned 在右下角添加在线状态圆点
3. 用 Row + Expanded 排列"关注 | 粉丝 | 文章"三列数据
4. 添加 StatefulWidget 的"点赞"按钮,点击后数字 +1,用 setState() 刷新
验证标准:页面在 Android/iOS 预览器中都正确显示,点击按钮数字递增无报错。
Level 3:开放挑战 — 平台自适应设置页
构建一个设置页面,要求在 iOS 上自动使用 Cupertino 风格、在 Android 上使用 Material 风格:
1. 用 Platform.isIOS 或 defaultTargetPlatform 判断当前平台
2. iOS 分支用 CupertinoPageScaffold + CupertinoNavigationBar
3. Android 分支用 Scaffold + AppBar
4. 至少实现 3 个设置项(开关 + 选择器 + 输入框),数据用 StatefulWidget 管理
验证标准:切换平台预览时,UI 风格自动切换,交互行为符合各自平台规范。
🏷️ 知识回顾
Flutter 架构 Impeller 渲染引擎 Dart 3.13 Primary Constructor Sealed Class Null Safety async/await Stream
StatelessWidget StatefulWidget Element Tree setState() Widget 生命周期 Material Design 3 Cupertino
Container Row/Column Expanded Stack/Positioned 约束模型 平台自适应
📖 下一篇预告
本篇我们掌握了 Dart 语言基础和 Widget 声明式 UI 体系,但应用的数据从哪里来?状态如何在组件树中流转?网络请求怎么发起和缓存?
第 29 篇:Flutter 进阶 — 状态管理与网络请求 将深入探讨:Provider 与 InheritedWidget 的依赖注入原理,Riverpod 作为新一代状态管理方案的 API 设计,Dart http 包与 dio 拦截器模式的网络请求封装,以及 FutureBuilder/StreamBuilder 实现数据驱动的 UI 更新。我们还会对比 Flutter 状态管理与 React Native 中 Zustand/TanStack Query 的思路差异,帮你建立跨框架的状态管理心智模型。