Flutter中PopupMenuItem关闭后如何更新界面UI
问题根本原因
DataTableSource变更数据源后必须主动调用notifyListeners()才能通知表格刷新,你之前删除操作只调用了后端接口,没有触发这个通知。- 你定义的
_resultData是final不可修改类型,后端删除成功后本地缓存的列表数据完全没更新,表格渲染用的还是旧数据。 - 上层有状态Widget没有监听删除成功的事件,也没有调用
setState触发重绘,PaginatedDataTable不会自动重建。 - 额外优化点:每个行的
DataCell里嵌套FutureBuilder重复拉取品牌列表属于冗余逻辑,完全没必要,父组件已经拿到全量数据可以直接用。
具体修改步骤
1. 修改ResultDataSource类
class ResultDataSource extends DataTableSource{ var brandsController = BrandsController(BrandsRepository()); ResultDataSource({ @required List<Result> resultData, @required this.onRowSelect, // 新增删除成功回调 @required this.onDeleteSuccess, }) : _resultData = resultData, assert(resultData != null); // 去掉final,允许修改数据源 List<Result> _resultData; final OnRowSelect onRowSelect; final Function(int deletedIndex) onDeleteSuccess; // 新增删除本地数据并刷新表格的方法 void deleteItem(int index) { _resultData.removeAt(index); // 必须调用这个方法通知DataTable刷新 notifyListeners(); } @override DataRow getRow(int index) { assert(index >= 0); if (index >= _resultData.length) { return null; } final _result = _resultData[index]; return DataRow.byIndex( index: index, cells: <DataCell>[ DataCell(Text('${_result.name}',style: TextStyle(fontSize: 13.0, fontWeight: FontWeight.w600, color: secondaryColor))), DataCell(Text('${_result.country}',style: TextStyle(fontSize: 13.0, color: secondaryColor))), DataCell(Text('${_result.isActive.toString() == "true" ? "Active" : "Inactive"}',style: TextStyle(fontSize: 13.0, fontWeight: FontWeight.w500, color: primaryColor),)), DataCell( // 删掉冗余的FutureBuilder,不需要每行都拉取品牌列表 PopupMenuButton( elevation: 20.0, icon: Icon( Icons.more_horiz, color: secondaryColor.withOpacity(0.5), ), itemBuilder: (context) =>[ // 其他菜单项逻辑保持不变,仅修改删除项的onTap逻辑 PopupMenuItem( // 改成异步等待接口返回成功 onTap: () async { await brandsController.deleteBrand(_result.id.toString()); // 先更新数据源自身的列表,通知表格刷新 deleteItem(index); // 回调上层父组件,同步更新父级的数据源 onDeleteSuccess(index); }, child:Row( children: [ SvgPicture.asset("assets/icons/garbage.svg", height: 18.0,color: Colors.blueGrey, ), SizedBox(width:10.0), Text("Delete", style: TextStyle(color: Colors.blueGrey, fontSize: 13.0),) ], ), value: 4, ), ], ) ), ], ); } // 剩余原有方法保持不变 @override bool get isRowCountApproximate => false; @override int get rowCount => _resultData.length; @override int get selectedRowCount => 0; }
2. 修改上层有状态Widget代码
首先在State类中定义本地存储的品牌列表:
class _YourWidgetState extends State<YourWidget> { List<Result> _currentBrandList; // 其他原有逻辑保持不变 @override Widget build(BuildContext context) { return FutureBuilder<List<Result>>( // 把原来每个cell里的拉取逻辑移到父组件这里,全局只拉取一次 future: brandsController.fetchBrandList(), builder: (context, snapshot) { if (snapshot.hasData) { // 接口返回后给本地列表赋值 _currentBrandList = snapshot.data; return ListView( padding: const EdgeInsets.all(16), shrinkWrap: true, children: [ Theme( data: Theme.of(context).copyWith( dividerColor: Colors.blueGrey.shade100.withOpacity(0.4), ), child: PaginatedDataTable( rowsPerPage: _currentBrandList.length ?? 1, showCheckboxColumn: true, dataRowHeight: 60, columns: [ // 列定义保持原有逻辑不变,此处省略 ], source: ResultDataSource( onRowSelect: (index) => () {}, resultData: _currentBrandList, // 实现删除成功的回调,更新父组件状态触发重绘 onDeleteSuccess: (deletedIndex) { setState(() { _currentBrandList.removeAt(deletedIndex); }); }, ), ), ), ], ); } // 其他加载、错误状态逻辑保持不变 return const CircularProgressIndicator(); }, ); } }
内容的提问来源于stack exchange,提问作者Davis
相关产品推荐
相关产品推荐

