下面内容只关注 GetxController / GetBuilder / Obx / 局部状态组件这些部分。
GetX 状态管理实践说明
本文介绍在项目中如何使用 GetxController 、GetBuilder 、Obx / GetX 等组件来组织业务逻辑和控制 UI 更新。
GetxController 的角色与生命周期
GetxController 用来承载页面或模块的业务状态与逻辑,通常搭配 StatelessWidget 使用,无需再写 StatefulWidget。
- 常用生命周期方法:
- onInit:Controller 创建时调用,做依赖注入、初始请求、订阅等。
- onReady:首帧渲染后调用,适合做需要 UI 已经渲染的操作(弹窗、导航等)。
- onClose:Controller 销毁时调用,用于取消订阅、关闭 Stream、释放资源。
推荐习惯:
- 把原来写在 StatefulWidget
initState / dispose里面的逻辑迁移到 Controller 的onInit / onClose中,UI 层尽量保持"傻瓜视图"。
GetX 中的两种状态管理方案
GetX 内置两类状态管理方式:简单状态管理(GetBuilder)与响应式状态管理(Obx / GetX)。
方案一:简单状态管理(GetBuilder + GetxController)
适用场景:不想使用 Rx 类型(.obs),希望显式控制刷新时机。
-
写法示例:
dartclass CounterController extends GetxController { int count = 0; void increment() { count++; update(); // 手动触发使用该 controller 的 GetBuilder 重建 } }dartclass CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { final controller = Get.put(CounterController()); return Scaffold( body: Center( child: GetBuilder<CounterController>( builder: (c) => Text('Count: ${c.count}'), ), ), floatingActionButton: FloatingActionButton( onPressed: controller.increment, ), ); } } -
特点:
- 无需
.obs,状态是普通字段。 - 只有调用
update()的时候,使用该 Controller 的 GetBuilder 才会重建。 - 适合页面级、大块 UI、不太频繁刷新场景。
- 无需
方案二:响应式状态管理(Obx / GetX + Rx)
适用场景 :已经在使用 .obs,或希望局部 UI 随状态变化自动刷新。
-
写法示例:
dartclass CounterController extends GetxController { var count = 0.obs; void increment() => count++; }dartclass CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { final controller = Get.put(CounterController()); return Scaffold( body: Center( child: Obx(() => Text('Count: ${controller.count}')), // 或 // child: GetX<CounterController>( // builder: (c) => Text('Count: ${c.count}'), // ), ), floatingActionButton: FloatingActionButton( onPressed: controller.increment, ), ); } } -
特点:
- 变量通过
.obs变为 Rx 类型(如 RxInt、RxString)。 - 一旦值变化,Obx / GetX 所在的小部件自动重建,无需写
update()。 - 适合高频、小区域更新,如计数器、进度、网络状态、Switch 等。
- 变量通过
两种方案的混用
在同一个项目、同一个 Controller 中,可以同时使用:
- 一部分状态使用普通字段 +
GetBuilder。 - 一部分状态使用
.obs+Obx / GetX。
经验规则:
- 页面大块区域、刷新不频繁 → 优先 GetBuilder。
- 小范围、高频刷新 → 优先 Obx / GetX。
GetBuilder 的生命周期回调
GetBuilder 本质上是一个 StatefulWidget,内部有完整的 State 生命周期,对外通过参数暴露部分回调:[1]
- 常用回调参数:
initState:对应 State.initState,适合调用 Controller 方法、发请求等。didChangeDependencies:父级依赖变化时触发,用得不多。didUpdateWidget:父组件重建、参数改变时触发。dispose:组件销毁时触发,适合释放本地资源。
示例:
dart
GetBuilder<CounterController>(
initState: (_) {
// widget 创建时执行
},
dispose: (_) {
// widget 销毁时执行
},
builder: (c) => Text('Count: ${c.count}'),
);
建议:
- 页面 /模块的"生命周期逻辑"优先放在 Controller.onInit/onClose。
- 某个局部 Widget 有特别的创建 / 销毁逻辑时,再使用 GetBuilder 的
initState / dispose。
局部状态组件:ValueBuilder 与 ObxValue
对于"只在一个小部件内部使用"的临时状态,可以使用局部状态组件,而不必放入 Controller:
-
ValueBuilder (简单本地状态):
dart ValueBuilder<bool>( initialValue: false, builder: (value, update) => Switch( value: value, onChanged: update, // update(newValue) ), ); -
ObxValue(本地 Rx 状态):
dartObxValue<RxBool>( (data) => Switch( value: data.value, onChanged: data, // 相当于 (v) => data.value = v ), false.obs, );
使用建议:
- 仅在该 Widget 内使用且与全局业务无关的状态 → 用 ValueBuilder / ObxValue。
- 会被多个 Widget 或页面共享的状态 → 放入 GetxController,再用 GetBuilder / Obx 监听。
快速选型表
| 需求场景 | 状态写法 | UI 组件 | 刷新方式 |
|---|---|---|---|
| 不想用 Rx,页面级 / 大块区域 | 普通字段 | GetBuilder | 手动 update() |
已使用 .obs,局部自动刷新 |
.obs(RxXX 类型) |
Obx / GetX | 值变化自动刷新 |
| 单个小 widget 内部的临时简单状态 | 普通字段 | ValueBuilder | 调用 updateFn |
| 单个小 widget 内部的临时响应式状态 | .obs |
ObxValue | 值变化自动刷新 |
在这种"页面加载时请求 API"的需求里,推荐把请求放在 GetxController 的生命周期 里做,而不是放在页面 Widget 里。
方案一:在 onInit 里请求
适合"只要创建了这个 Controller(进入页面)就立刻请求"的场景。
dart
class ArticleController extends GetxController {
int pageCount = 0; // 简单状态
var likeCount = 0.obs; // 响应式状态
var isFavorite = false.obs;
var loading = false.obs; // 加载状态
var article = Rxn<Article>(); // 文章详情
@override
void onInit() {
super.onInit();
increasePageCount(); // 原来的逻辑
fetchArticle(); // 页面加载时请求 API
}
Future<void> fetchArticle() async {
loading.value = true;
try {
final data = await api.getArticleDetail(); // 这里调用你的 repository / service
article.value = data;
// article 是 Rx,使用 Obx 的地方会自动刷新
// 如果你有依赖简单状态的 GetBuilder,需要的话再调用 update()
// update();
} finally {
loading.value = false;
}
}
void increasePageCount() {
pageCount++;
update(); // 刷新 GetBuilder
}
void like() => likeCount++;
void toggleFavorite() => isFavorite.toggle();
}
页面里依然混用 GetBuilder + Obx:
dart
class ArticlePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = Get.put(ArticleController());
return Scaffold(
appBar: AppBar(title: const Text('Article Detail')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 顶部浏览次数(简单状态)
GetBuilder<ArticleController>(
builder: (c) => Text('页面浏览次数:${c.pageCount}'),
),
const SizedBox(height: 16),
// 中间部分:加载中 / 内容(响应式状态)
Obx(() {
if (controller.loading.value) {
return const CircularProgressIndicator();
}
final article = controller.article.value;
if (article == null) {
return const Text('暂无数据');
}
return Text(article.title); // 文章标题
}),
const SizedBox(height: 16),
// 点赞 + 收藏(响应式状态)
Obx(
() => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('点赞:${controller.likeCount}'),
const SizedBox(width: 16),
Icon(
controller.isFavorite.value
? Icons.favorite
: Icons.favorite_border,
color: controller.isFavorite.value ? Colors.red : null,
),
],
),
),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: controller.increasePageCount,
child: const Text('增加浏览次数 (GetBuilder)'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: controller.like,
child: const Text('点赞 (Obx)'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: controller.toggleFavorite,
child: const Text('收藏切换 (Obx)'),
),
],
),
],
),
);
}
}
方案二:在 onReady 里请求(需要等页面渲染后)
如果你的 API 请求需要在"首帧 UI 出来之后"再做,比如要先弹一个对话框提示用户,将请求放在 onReady:
dart
@override
void onReady() {
super.onReady();
fetchArticle(); // 首帧渲染完成后请求
}
不再建议的做法
- 不建议再在页面的
initState里请求,而是优先放到 GetxController.onInit / onReady,这样视图层更干净,也更符合 GetX 推荐的结构。