Flutter 应用开发中状态管理是重要的一环,目前成熟的状态管理插件有 provider,provider,getx 等,本文基于 getx 的使用及基本原理展开讨论,文末有具体工程示例链接;
1. GetX 的核心架构
GetX 是一个轻量级的状态管理、依赖注入和路由管理框架,它的核心目标——>
减少样板代码,最大化开发效率
从更新原理及工程整合上与 provider 及 bloc 的差异
| 维度 | Provider | BLoC | GetX |
|---|---|---|---|
| 是否依赖 context | 强 | 中 | ❌ 不需要 |
| 状态更新模型 | rebuild | event → state | 响应式 / 命令式 |
| 样板代码 | 少 | 多 | 极少 |
| 生命周期 | Widget 驱动 | BLoC 显式控制 | Route + SmartManagement |
| 工程整合度 | 低 | 中 | 高 |
1.1 响应式编程模型
GetX 的响应式编程基于观察者模式(Observer Pattern)实现。当一个被观察的变量(使用.obs标记)发生变化时,所有依赖该变量的 UI 组件都会自动重建。
内部实现上,GetX 使用了一个名为 GetStream的流(Stream)来管理状态变化。当可观察对象的值发生变化时,会触发流中的事件,从而通知所有监听者。
// 简化的内部实现原理
class RxImpl<T> {
final _subject = GetStream<T>();
T get value => _subject.value;
set value(T val) {
_subject.add(val); // 触发流事件
}
}
1.2 依赖注入系统
GetX 的依赖注入系统基于服务定位器模式(Service Locator Pattern)。它维护了一个全局的依赖实例映射表,通过类型或标签来查找和注册依赖项。
// 简化的内部实现原理
class GetInstance {
static final _singl = <String, dynamic>{};
void put<S>(S dependency, {String? tag}) {
_singl[_getKey(S, tag)] = dependency;
}
S find<S>({String? tag}) {
return _singl[_getKey(S, tag)] as S;
}
String _getKey(Type type, String? tag) => tag == null ? type.toString() : type.toString() + tag;
}
1.3 路由管理系统
GetX 的路由管理是对 Flutter 原生导航 API 的封装,它维护了一个全局的路由历史栈,并提供了更简洁的 API 来管理页面导航。
// 简化的内部实现原理
class GetNavigation {
final _history = <GetPage>[];
Future<T?> to<T>(GetPage page) {
_history.add(page);
return Navigator.push(...); // 调用Flutter原生导航
}
void back() {
_history.removeLast();
Navigator.pop(...); // 调用Flutter原生导航
}
}
2. GetX 的工作原理
2.1 状态管理原理
简单状态管理
简单状态管理使用 GetBuilder和 update()方法:
- 当调用
update()时,GetX 会通知所有使用GetBuilder构建的与该控制器关联的 UI 组件进行重建 - 内部使用唯一 ID 来标识每个控制器实例,确保只有相关的 UI 组件会重建
// 简化的内部实现原理
void update([List<String>? ids]) {
if (ids == null) {
// 通知所有监听该控制器的UI组件
_notifyAllListeners();
} else {
// 只通知特定ID的UI组件
_notifyListenersById(ids);
}
}
响应式状态管理
响应式状态管理使用 .obs 与 Obx()
-
当
.obs变量的值发生变化时,会触发内部的GetStream -
Obx()组件会监听这些流的变化,并在值变化时自动重建 UI -
GetX 使用了一个智能的差异算法,只重建依赖变化值的组件,而不是整个页面
sequenceDiagram
participant A as Obx声明阶段
participant B as 数据访问阶段
participant C as 数据更新阶段
participant D as UI重建阶段
A->>B: 创建_ObxState和RxNotifier
B->>C: 通过代理绑定观察者
C->>D: 触发setState重建
Note right of D: 最小范围局部刷新
// 简化的内部实现原理
class Obx extends StatefulWidget {
@override
_ObxState createState() => _ObxState();
}
class _ObxState extends State<Obx> {
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
// 订阅可观察变量的变化
_subscription = _getStream().listen((_) {
if (mounted) setState(() {}); // 触发UI重建
});
}
@override
Widget build(BuildContext context) {
// 构建UI时会访问可观察变量,从而建立依赖关系
return widget.builder();
}
}
sequenceDiagram
participant WidgetTree as Widget树
participant ObxWidget as ObxWidget
participant _ObxState as _ObxState
participant RxNotifier as RxNotifier
participant RxObject as Rx
participant GetStream as GetStream
WidgetTree->>ObxWidget: 插入Obx组件
ObxWidget->>_ObxState: createState()
_ObxState->>RxNotifier: 创建_observer实例
_ObxState->>GetStream: 订阅监听(subs = _observer.listen)
loop 构建阶段
_ObxState->>RxInterface: proxy = _observer
RxObject-->>GetStream: 访问.value触发addListener
RxObject->>RxNotifier: 注册监听关系
RxInterface-->>_ObxState: 恢复proxy原始值
end
Note over RxObject,RxNotifier: 建立双向绑定关系
par 数据更新时
RxObject->>GetStream: 广播变化事件
GetStream->>RxNotifier: 通知_observer
RxNotifier->>_ObxState: 调用_updateTree
_ObxState->>WidgetTree: setState局部重建
end
2.2 依赖注入原理
GetX 的依赖注入系统支持多种注入方式:
-
常规注入(
Get.put):立即创建实例并注册 -
懒加载注入(
Get.lazyPut):只在首次使用时创建实例 -
异步注入(
Get.putAsync):异步创建实例 -
一次性注入(
Get.create):每次获取都创建新实例
内部实现上,GetX 使用工厂模式(Factory Pattern)来管理依赖的创建方式:
// 简化的内部实现原理
void lazyPut<S>(InstanceBuilderCallback<S> builder) {
_factories[_getKey(S)] = builder; // 存储工厂函数
}
S find<S>() {
final key = _getKey(S);
if (_singl.containsKey(key)) {
return _singl[key]; // 返回已存在的实例
}
if (_factories.containsKey(key)) {
final instance = _factories[key](); // 调用工厂函数创建实例
_singl[key] = instance; // 缓存实例
return instance;
}
throw Exception('未找到依赖项');
}
2.3 路由管理原理
GetX 的路由管理系统在内部维护了一个页面堆栈,并提供了丰富的导航方法:
-
基本导航:
Get.to()Get.back() -
命名路由:
Get.toNamed()Get.offNamed() -
替换导航:
Get.off()Get.offAll()
每个路由操作都会更新内部的页面堆栈,并调用 Flutter 原生的导航 API:
// 简化的内部实现原理
Future<T?> toNamed<T>(String routeName, {dynamic arguments}) {
final route = _findRouteByName(routeName);
if (route != null) {
_currentRoute = route;
return Navigator.of(context).pushNamed(routeName, arguments: arguments);
}
throw Exception('未找到路由');
}
3. GetX 的优化机制
3.1 内存管理
GetX 提供了智能的内存管理机制:
-
自动释放:当控制器不再被使用时,默认会被自动释放
-
永久实例:使用
permanent: true参数可以创建永久实例 -
智能实例:使用
fenix: true参数可以在需要时重新创建已释放的实例
// 自动释放机制简化实现
void _autoRelease() {
if (!_isPermanent && _getBuilderCount() == 0) {
_singl.remove(_getKey(this.runtimeType));
}
}
3.2 性能优化
GetX 在性能方面做了多项优化:
-
局部更新:只重建依赖变化数据的 UI 部分
-
懒加载:只在需要时创建依赖实例
-
按需构建:使用
GetBuilderid 参数可以更精确地控制 UI 更新范围
// 局部更新机制简化实现
void update([List<String>? ids]) {
if (ids == null) {
_notifyAllListeners();
} else {
// 只通知特定ID的监听器
for (final id in ids) {
_notifyListenersById(id);
}
}
}
flowchart TD
A[main.dart 启动] --> B[AppStoreInternal 单例初始化]
B --> C[AppStore 构造函数执行]
C --> D[_connectivity.onConnectivityChanged.listen]
D --> E[等待网络状态变化]
F[LottieJsonWidget initData] --> G[延迟1秒]
G --> H[初始化环境配置]
H --> I[LoginUtils.setLoginRegion]
I --> J[LoginUtils.getIpCountryCode]
J --> K[LoginPluginApi.requestIpRegionIfNecessary]
E --> L[_updateConnectionStatus 被调用]
L --> M[networkChange 执行]
M --> N{网络状态判断}
N -->|有网络| O[await initUserInfo]
N -->|无网络| P[设置离线时间]
O --> Q[RESTApis.getUserInfo]
Q --> R[await getUserPrivilege]
R --> S[notifyListeners]
K --> T[异步获取IP地区]
T --> U[AppStoreInternal.store.setIpCountryCode]
U --> V[notifyListeners]
S --> W[UI更新]
V --> W
X[App.onMounted] --> Y[AppStoreInternal.store.setMainContext]
Y --> Z[检查网络状态]
Z --> AA[AppUpgradeUtils.checkUpgrade]
BB[AppPages useMounted] --> CC[init 函数]
CC --> DD{网络和登录状态检查}
DD -->|有网络且已登录| EE[ApplicationController.queryUserConfig]
DD -->|无网络或未登录| FF[跳过配置加载]
EE --> GG[PrintReportController.uploadIsarToService]
style A fill:#e1f5fe
style C fill:#fff3e0
style L fill:#f3e5f5
style O fill:#e8f5e8
style T fill:#fff8e1
style W fill:#fce4ec
工程示例
lib/
├── app/
│ ├── controllers/ # 控制器(状态管理)
│ ├── modules/ # 各功能模块的UI页面
│ ├── routes/ # 路由管理
│ ├── translations/ # 国际化翻译
│ └── bindings/ # 依赖注入绑定
└── main.dart # 应用入口