GetX:数据拉取与StateMiXin

  1. 什么是 StateMixin?
  2. StateMixin 的核心方法
  3. StateMixin 的示例
    1. 步骤
    2. 注意StateMixin<T>泛型的选择
      1. 如果返回值是Json
      2. 如果返回值是字符串

什么是 StateMixin

StateMixin<T>GetX 提供的一个状态管理混入类(mixin),它的作用是 简化异步请求的状态管理和 UI 更新

核心思想:

  • 异步请求通常有四种状态:加载中 / 成功 / 空 / 错误
  • StateMixin 提供了统一的状态管理机制,让 UI 自动响应状态变化,无需手动管理多个变量(如 isLoadingisErrordata)。

优势

  1. 统一管理异步状态:loading / success / empty / error 一目了然。
  2. 减少冗余代码:不需要单独定义 isLoadingisErrordata 等变量。
  3. UI 自动刷新obx 根据状态自动渲染,不用手动刷新界面。
  4. 可与 GetConnect、Dio 等请求工具结合使用

注意事项

  • 不是必须的:如果你不想用 StateMixin,也可以用普通 Rx + Obx 管理状态。
  • 适合页面初始化或单次请求:当状态频繁变化或者有复杂逻辑时,可以选择更灵活的 Rx 变量方案。

StateMixin 的核心方法

方法 作用
change(T? data, {RxStatus status}) 更新数据和状态,通常在 Controller 中调用
RxStatus.loading() 表示加载中状态
RxStatus.success() 表示请求成功状态
RxStatus.empty() 表示请求成功但数据为空
RxStatus.error(String message) 表示请求失败或出错

change() 是用于:

  1. 更新当前的数据 (data)
  2. 更新当前的状态 (RxStatus)

它是 状态管理和数据绑定的桥梁,用它可以让 UI 根据状态自动刷新,而不需要手动 setState() 或管理多个 Rx 变量。

StateMixin 的示例

import 'package:flutter/material.dart';
import 'package:get/get.dart';

// ------------------ 1. 创建 GetConnect API --------------------

class PostApi extends GetConnect {
  /// 初始化
  /// class GetConnect extends GetConnectInterface {
  /// abstract class GetConnectInterface with GetLifeCycleBase {
  /// onInit() → 实例创建时触发
  @override
  void onInit() {
    // 设置基础 URL
    httpClient.baseUrl = "https://jsonplaceholder.typicode.com";
    super.onInit();
  }

  /// get("/posts"):①发送Get请求 ②制定请求路径:baseUrl+/posts
  /// ③返回一个Future<Response>对象
  /// https://jsonplaceholder.typicode.com/posts
  Future<Response> getPosts() => get("/posts");
}

// -------------------2. 创建 Controller ---------------------------------

// with StateMixin<List<dynamic>> : 混入 StateMixin<List<dynamic>>
//    StateMixin 会提供 change() 方法和 RxStatus 状态,让 UI 自动刷新。
//    StateMixin<T> 中的泛型 T 决定 state(response的body的类型) 的类型。
class PostController extends GetxController with StateMixin<List<dynamic>> {
  final api = Get.find<PostApi>();

  void fetchPosts() async {
    // data 这里传 null,因为还没有获取到数据。
    // status: RxStatus.loading() 表示 当前正在加载数据,UI 会显示 loading 指示器(在页面里通过 obx 渲染)。
    change(null, status: RxStatus.loading()); // ✅ 设置加载状态

    try {
      final response = await api.getPosts();
      if (response.statusCode == 200) {
        // 请求成功,获得数据
        final data = response.body;
        if (data.isEmpty) {
          // 数据为空
          // UI 会显示 “暂无数据” 的占位界面。
          change([], status: RxStatus.empty()); // 数据为空
        } else {
          // UI 会显示列表内容,obx 自动监听 change() 并刷新界面。
          change(data, status: RxStatus.success()); // 请求成功
        }
      } else {
        // 请求失败,显示错误信息
        // RxStatus.error() 会让 UI 显示错误提示。
        change(
          null,
          status: RxStatus.error("请求失败: ${response.statusCode}"),
        ); // 请求失败
      }
    } catch (e) {
      // 捕获请求异常(如网络超时、解析失败等)。
      // 将状态设置为 error,UI 会显示错误信息。
      change(null, status: RxStatus.error(e.toString())); // 异常
    }
  }
}

// -----------------------3. 创建 Binding --------------------------------
/// 延迟注入的核心好处就是 按需创建对象,避免顺序依赖和不必要的资源占用。
/// 通过 Get.lazyPut,我们可以在需要的时候再创建实例,而不是在应用启动时就创建所有实例。
class PostBinding extends Bindings {
  @override
  void dependencies() {
    // ✅ 延迟注入 PostApi
    Get.lazyPut<PostApi>(() => PostApi());
    // ✅ 延迟注入 PostController
    Get.lazyPut<PostController>(() => PostController());
  }
}

// -----------------------4.创建页面 ------------------------------------

class PostPage extends StatelessWidget {
  PostPage({super.key});

  final PostController controller = Get.find<PostController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("GetConnect + StateMixin 示例")),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () {
              controller.fetchPosts(); // 点击按钮触发请求
            },
            child: Text("加载数据"),
          ),
          Expanded(
            // 页面使用 controller.obx(...) 监听状态,
            // 在Controller中调用 change() 会自动触发 UI 更新。
            child: controller.obx(
              // 这个 state 对应的是请求的结果数据
              // List<dynamic>? state :这个是由StateMixin<List<dynamic>>决定的
              (state) => ListView.builder(
                itemCount: state!.length,
                itemBuilder: (context, index) {
                  final post = state[index];
                  return ListTile(
                    title: Text(post["title"]),
                    subtitle: Text(post["body"]),
                  );
                },
              ),
              onLoading: Center(
                child: CircularProgressIndicator(),
              ), // loading 状态
              onEmpty: Center(child: Text("暂无数据")), // empty 状态
              onError: (err) => Center(child: Text("错误: $err")), // error 状态
            ),
          ),
        ],
      ),
    );
  }
}

// -----------------------4.路由配置(将 Binding 和页面绑定)---------------------

void main() {
  runApp(
    GetMaterialApp(
      initialRoute: '/posts',
      getPages: [
        GetPage(
          name: '/posts',
          page: () => PostPage(),
          binding: PostBinding(), // ✅ 页面进入时自动注入依赖
        ),
      ],
    ),
  );
}

步骤

  1. Controller混入StateMixin
  2. Controller里面:数据或者状态变化的时候调用change(T? data, {RxStatus status})
  3. 页面:controller.obx监听状态的变化
  4. 页面:不同状态,不同的UI

注意StateMixin<T>泛型的选择

StateMixin<T> 中的泛型 T 决定 state 的类型

state 的类型可以是:

  • List<dynamic> → 列表
  • Map<String, dynamic> → JSON 对象
  • String → 文本
  • 甚至是自定义模型类 MyModel

StateMixin<List<dynamic>>,所以 state 是一个列表。

如果返回值是Json

假设接口返回一个单独的 JSON 对象:

{
  "id": 1,
  "title": "Hello",
  "body": "World"
}

Controller 可以定义为:

class PostController extends GetxController with StateMixin<Map<String, dynamic>> {
  	// ......
      final response = await api.getSinglePost();
      if (response.statusCode == 200) {
        final data = response.body; // Map<String, dynamic>
        change(data, status: RxStatus.success());
   // ......
}

页面使用:

controller.obx(
  (state) => Column(
    children: [
      Text("ID: ${state!['id']}"),
      Text("Title: ${state['title']}"),
      Text("Body: ${state['body']}"),
    ],
  ),
  onLoading: Center(child: CircularProgressIndicator()),
  onError: (err) => Center(child: Text("错误: $err")),
);

如果返回值是字符串

如果接口直接返回一个字符串,比如 "Hello World"

class StringController extends GetxController with StateMixin<String> {
  void fetchMessage() async {
    change(null, status: RxStatus.loading());
    await Future.delayed(Duration(seconds: 1));
    change("Hello World", status: RxStatus.success());
  }
}

页面使用:

controller.obx(
  (state) => Center(child: Text(state!)), // state 就是字符串
  onLoading: Center(child: CircularProgressIndicator()),
  onError: (err) => Center(child: Text("错误: $err")),
);

×

喜欢就点赞,疼爱就打赏