GetX 状态管理实践

下面内容只关注 GetxController / GetBuilder / Obx / 局部状态组件这些部分。


GetX 状态管理实践说明

本文介绍在项目中如何使用 GetxControllerGetBuilderObx / 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),希望显式控制刷新时机。

  • 写法示例:

    dart 复制代码
    class CounterController extends GetxController {
      int count = 0;
    
      void increment() {
        count++;
        update(); // 手动触发使用该 controller 的 GetBuilder 重建
      }
    }
    dart 复制代码
    class 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 随状态变化自动刷新。

  • 写法示例:

    dart 复制代码
    class CounterController extends GetxController {
      var count = 0.obs;
    
      void increment() => count++;
    }
    dart 复制代码
    class 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 状态):

    dart 复制代码
    ObxValue<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 推荐的结构。
相关推荐
tangweiguo030519873 小时前
Kotlin vs Dart vs Swift:语法对比全解
flutter
tangweiguo030519874 小时前
Flutter多品牌应用架构实战:从配置驱动到编译部署的完整解决方案
flutter
Bryce李小白5 小时前
FlutterBoost适配Flutter3.38.4版本生成补丁包
flutter
tangweiguo030519875 小时前
Flutter Packages 设计与实践:构建可维护的模块化应用
flutter
小a杰.6 小时前
Flutter 的编译技术核心
flutter
hudawei9967 小时前
flutter setState(() { … }) 作用
flutter
ujainu小7 小时前
Flutter 结合 local_auth 3.0.0 实现跨平台本地生物识别认证
flutter·local_auth
ujainu小8 小时前
Flutter 本地存储权威指南:sqflite 2.4.2 全平台集成与实战
flutter·sqflite
ujainu小9 小时前
Flutter 生物认证权威指南:local_auth 3.0.0 全平台集成与实战
flutter·local_auth