Flutter:如何在DataTableSource类内调用setState更新数据表格?
如何在Flutter的DataTableSource内部点击按钮后刷新数据表格
问题背景
你需要在DataTableSource子类(MyTable)的按钮点击后,刷新PaginatedDataTable的数据,目前删除接口调用完成后表格没有更新。
可行解决方案
方案一:通过回调函数通知父组件刷新
这种方法不需要引入额外状态管理,核心是让MyTable在操作完成后通知有状态的父组件触发刷新:
修改
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)), ); } // 其余代码保持不变 }在父组件中传入回调,触发刷新
在创建MyTable实例时,传入onDataChanged回调,通过setState让父组件重新构建,从而刷新表格:DataTableSource dataSource(List<User> userList) => MyTable( datasList: userList, context: context, onDataChanged: () { setState(() {}); // 触发父组件刷新,重新获取最新数据 }, );完善
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,直接利用其响应式状态管理可以更高效地更新表格:
改造
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); } } }修改
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; }主页面替换为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
相关产品推荐
相关产品推荐

