You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:如何在DataTableSource类内调用setState更新数据表格?

如何在Flutter的DataTableSource内部点击按钮后刷新数据表格

问题背景

你需要在DataTableSource子类(MyTable)的按钮点击后,刷新PaginatedDataTable的数据,目前删除接口调用完成后表格没有更新。

可行解决方案

方案一:通过回调函数通知父组件刷新

这种方法不需要引入额外状态管理,核心是让MyTable在操作完成后通知有状态的父组件触发刷新:

  1. 修改MyTable类,添加刷新回调
    在构造函数中新增一个VoidCallback类型的参数,用于在删除完成后通知父组件:

    class MyTable extends DataTableSource {
      MyTable({required this.datasList, required this.context, required this.onDataChanged});
      final List<User> datasList;
      final BuildContext context;
      final VoidCallback onDataChanged; // 新增刷新回调
      final UserController userController = Get.put(UserController());
    
      Widget Button(String title, Color color, String id) {
        return MaterialButton(
          onPressed: () async {
            await deleteUser(id);
            onDataChanged(); // 删除完成后触发父组件刷新
          },
          child: Text(
            title,
            style: const TextStyle(color: Colors.white),
          ),
          color: color,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
        );
      }
    
      // 其余代码保持不变
    }
    
  2. 在父组件中传入回调,触发刷新
    在创建MyTable实例时,传入onDataChanged回调,通过setState让父组件重新构建,从而刷新表格:

    DataTableSource dataSource(List<User> userList) =>
        MyTable(
          datasList: userList,
          context: context,
          onDataChanged: () {
            setState(() {}); // 触发父组件刷新,重新获取最新数据
          },
        );
    
  3. 完善deleteUser方法的异步处理
    确保接口请求完成后再触发刷新,同时增加状态判断:

    Future deleteUser(var id) async {
      Uri url = Uri.parse("${BASE_URL}user/$id");
      final response = await http.delete(url, headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
        'Authorization': 'Bearer $TOKEN',
      });
    
      if (response.statusCode == 200) {
        Get.snackbar("Deleted $id", "删除成功",
            duration: const Duration(seconds: 3),
            colorText: Colors.black,
            backgroundColor: Colors.white);
      } else {
        Get.snackbar("Error", "删除失败",
            colorText: Colors.white,
            backgroundColor: Colors.red);
      }
    }
    

方案二:结合GetX状态管理(更简洁)

你已经在使用GetX,直接利用其响应式状态管理可以更高效地更新表格:

  1. 改造UserController,管理用户列表状态
    将用户列表改为RxList,并封装删除方法,删除后直接更新列表:

    class UserController extends GetxController {
      final RxList<User> userList = <User>[].obs;
    
      // 初始化获取用户列表
      Future<void> fetchUsers() async {
        final users = await fetchUsersFromModel();
        userList.assignAll(users);
      }
    
      // 封装删除方法
      Future<void> deleteUser(String id) async {
        Uri url = Uri.parse("${BASE_URL}user/$id");
        final response = await http.delete(url, headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
          'Authorization': 'Bearer $TOKEN',
        });
    
        if (response.statusCode == 200) {
          userList.removeWhere((user) => user.id == id); // 直接更新响应式列表
          Get.snackbar("Deleted $id", "删除成功",
              duration: const Duration(seconds: 3),
              colorText: Colors.black,
              backgroundColor: Colors.white);
        } else {
          Get.snackbar("Error", "删除失败",
              colorText: Colors.white,
              backgroundColor: Colors.red);
        }
      }
    }
    
  2. 修改MyTable,直接使用Controller的状态
    不再传入datasList,而是从Controller获取数据,删除后调用notifyListeners()刷新表格:

    class MyTable extends DataTableSource {
      MyTable({required this.context});
      final BuildContext context;
      final UserController userController = Get.find<UserController>(); // 用Get.find获取已注入的Controller
    
      Widget Button(String title, Color color, String id) {
        return MaterialButton(
          onPressed: () async {
            await userController.deleteUser(id);
            notifyListeners(); // 通知DataTableSource刷新表格行
          },
          child: Text(
            title,
            style: const TextStyle(color: Colors.white),
          ),
          color: color,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
        );
      }
    
      @override
      DataRow? getRow(int index) {
        final user = userController.userList[index];
        return DataRow.byIndex(index: index, cells: [
          DataCell(Text(user.id.toString())),
          DataCell(ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 100),
            child: Text(
              user.name.toString(),
              overflow: TextOverflow.ellipsis,
            ),
          )),
          // 其余cell替换为user对象的属性
          DataCell(Row(
            children: [
              Button("Edit", Colors.lightBlue, user.id.toString()),
              const SizedBox(width: 5),
              Button("Delete", Colors.red, user.id.toString()),
            ],
          )),
        ]);
      }
    
      @override
      bool get isRowCountApproximate => false;
    
      @override
      int get rowCount => userController.userList.length;
    
      @override
      int get selectedRowCount => 0;
    }
    
  3. 主页面替换为GetX的Obx组件
    去掉StreamBuilder,用Obx监听Controller的状态变化,自动刷新表格:

    Obx(() {
      if (userController.userList.isEmpty) {
        return const Center(child: CircularProgressIndicator());
      }
      return PaginatedDataTable(
        sortColumnIndex: sortColumnIndex,
        sortAscending: isAscending,
        columns: [
          DataColumn(label: const Text("Id"), onSort: onSort),
          DataColumn(label: const Text("Name"), onSort: onSort),
          DataColumn(label: const Text("Username"), onSort: onSort),
          DataColumn(label: const Text("Email"), onSort: onSort),
          DataColumn(label: const Text("Roles"), onSort: onSort),
          DataColumn(label: const Text("Actions"), onSort: onSort),
        ],
        header: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              "Manage Users",
              style: TextStyle(fontSize: width * 0.04, fontWeight: FontWeight.normal),
            ),
            MaterialButton(
              onPressed: () {
                showMaterialModalBottomSheet(
                    context: context,
                    builder: (context) => SizedBox(
                          height: height * 0.9,
                          child: BottomSheetWidget(),
                        ),
                    shape: const RoundedRectangleBorder(
                        borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(15), topRight: Radius.circular(15))));
              },
              color: const Color.fromRGBO(30, 119, 66, 1),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
              child: Text(
                "Add User",
                style: TextStyle(color: Colors.white, fontSize: width * 0.03),
              ),
            )
          ],
        ),
        source: MyTable(context: context),
      );
    })
    

方案选择

  • 如果项目未使用状态管理,方案一快速有效,不需要重构现有代码。
  • 如果已经在用GetX,方案二更简洁,响应式状态管理可以自动处理数据更新,减少手动刷新的代码。

内容的提问来源于stack exchange,提问作者Mohammed Bekele

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 21:50:28