Flutter中PaginatedDataTable的IconButton状态更新后界面不刷新问题
Flutter PaginatedDataTable 点击按钮后状态更新但界面不刷新的解决方法
我在Flutter的PaginatedDataTable组件的DataCell中添加了多个IconButton,点击其中一个按钮时修改了rows[index]['status']的值,期望该IconButton能根据此状态值切换显示不同图标。但点击后状态值已更新,界面却未同步刷新,相关代码如下:
import 'package:flutter/material.dart'; List<Map<String, dynamic>> devData() { List<Map<String, dynamic>> data = []; for (var i = 1; i < 15; i++) { data.add({ 'username': 'account$i', 'nickname': i % 2 == 0 ? 'Peter$i' : 'Charles$i', 'status': i % 2 == 0 ? 0 : 1, 'group': i % 2 == 0 ? 2 : 3, }); } return data; } List<Map<String, dynamic>> rows = devData(); class UserManagePage extends StatefulWidget { const UserManagePage({super.key}); @override State<StatefulWidget> createState() => UserManagePageState(); } class UserManagePageState extends State<UserManagePage> { UserManagePageState() { rows = devData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton( hoverColor: Colors.transparent, onPressed: () { Navigator.pop(context); }, icon: const Icon(Icons.arrow_back_ios_new), iconSize: 16, color: const Color.fromRGBO(100, 100, 100, 0.5), ), centerTitle: true, automaticallyImplyLeading: false, backgroundColor: WidgetStateColor.transparent, title: const Text('用户管理', style: TextStyle( fontFamily: 'AlimamaFangYuanTi', fontSize: 18, fontWeight: FontWeight.w900))), body: Container( padding: const EdgeInsets.all(5), alignment: const Alignment(0, 0), child: PaginatedDataTable( horizontalMargin: 5, columnSpacing: 30, header: Row(children: [ IconButton( onPressed: () { showDialog( context: context, builder: (context) { return const UserAdd(); }); }, icon: const Icon(Icons.person_add_alt_1)) ]), columns: const [ DataColumn(label: Flexible(child: Center(child: Text('Account')))), DataColumn(label: Flexible(child: Center(child: Text('Username')))), DataColumn(label: Flexible(child: Center(child: Text('Group')))), DataColumn(label: Flexible(child: Center(child: Text('Operation')))) ], source: UserManageDate(context: context), )), ); } } class UserManageDate extends DataTableSource { BuildContext context; UserManageDate({required this.context}); @override DataRow? getRow(int index) { List<String> groups = ['Super Admin', 'Admin', 'Doctor', 'Nurse']; return DataRow(cells: [ DataCell(Text('${rows[index]['username']}')), DataCell(Text('${rows[index]['nickname']}')), DataCell(Text(groups[rows[index]['group']])), DataCell( Flex( direction: Axis.horizontal, children: [ IconButton( onPressed: () {}, icon: const Icon( Icons.edit, size: 20, color: Colors.green, ), ), //edit IconButton( onPressed: () { rows[index]['status'] = rows[index]['status'] == 1 ? 0 : 1; ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(rows[index]['status'] == 1 ? 'Enabled' : 'Disabled'), action: SnackBarAction(label: 'Action', onPressed: () {}), duration: const Duration(milliseconds: 500))); }, icon: Icon( rows[index]['status'] == 1 ? Icons.lock : Icons.lock_open_rounded, size: 20, color: rows[index]['status'] == 1 ? Colors.blue : Colors.grey, ), ), //enable/disable IconButton( onPressed: () {}, icon: const Icon( Icons.delete, size: 20, color: Colors.grey, ), ) //delete:soft delete ], ), onTap: () { print('11111222'); }) ]); } @override bool get isRowCountApproximate => false; @override int get rowCount => rows.length; @override int get selectedRowCount => 0; }
关键按钮代码:
IconButton( onPressed: () { rows[index]['status'] = rows[index]['status'] == 1 ? 0 : 1; ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(rows[index]['status'] == 1 ? 'Enabled' : 'Disabled'), action: SnackBarAction(label: 'Action', onPressed: () {}), duration: const Duration(milliseconds: 500))); }, icon: Icon( rows[index]['status'] == 1 ? Icons.lock : Icons.lock_open_rounded, size: 20, color: rows[index]['status'] == 1 ? Colors.blue : Colors.grey, ), ),
问题原因
- 全局数据未纳入状态管理:当前
rows是全局变量,修改其中的值后,UserManagePageState无法感知变化,不会触发界面重建。 - DataTableSource未通知刷新:
PaginatedDataTable的数据源DataTableSource继承自ChangeNotifier,但修改数据后没有调用notifyListeners()通知组件刷新。
修复步骤及修改后代码
修改思路
- 将
rows移到UserManagePageState中作为状态变量,确保数据变更能被组件感知。 - 让
UserManageDate持有rows的引用,在数据修改后调用notifyListeners()触发表格刷新。
修改后的完整代码
import 'package:flutter/material.dart'; List<Map<String, dynamic>> devData() { List<Map<String, dynamic>> data = []; for (var i = 1; i < 15; i++) { data.add({ 'username': 'account$i', 'nickname': i % 2 == 0 ? 'Peter$i' : 'Charles$i', 'status': i % 2 == 0 ? 0 : 1, 'group': i % 2 == 0 ? 2 : 3, }); } return data; } class UserManagePage extends StatefulWidget { const UserManagePage({super.key}); @override State<StatefulWidget> createState() => UserManagePageState(); } class UserManagePageState extends State<UserManagePage> { late List<Map<String, dynamic>> rows; late UserManageDate dataSource; @override void initState() { super.initState(); rows = devData(); dataSource = UserManageDate(context: context, rows: rows); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton( hoverColor: Colors.transparent, onPressed: () { Navigator.pop(context); }, icon: const Icon(Icons.arrow_back_ios_new), iconSize: 16, color: const Color.fromRGBO(100, 100, 100, 0.5), ), centerTitle: true, automaticallyImplyLeading: false, backgroundColor: WidgetStateColor.transparent, title: const Text('用户管理', style: TextStyle( fontFamily: 'AlimamaFangYuanTi', fontSize: 18, fontWeight: FontWeight.w900))), body: Container( padding: const EdgeInsets.all(5), alignment: const Alignment(0, 0), child: PaginatedDataTable( horizontalMargin: 5, columnSpacing: 30, header: Row(children: [ IconButton( onPressed: () { showDialog( context: context, builder: (context) { return const UserAdd(); }); }, icon: const Icon(Icons.person_add_alt_1)) ]), columns: const [ DataColumn(label: Flexible(child: Center(child: Text('Account')))), DataColumn(label: Flexible(child: Center(child: Text('Username')))), DataColumn(label: Flexible(child: Center(child: Text('Group')))), DataColumn(label: Flexible(child: Center(child: Text('Operation')))) ], source: dataSource, )), ); } } class UserManageDate extends DataTableSource { final BuildContext context; final List<Map<String, dynamic>> rows; UserManageDate({required this.context, required this.rows}); @override DataRow? getRow(int index) { List<String> groups = ['Super Admin', 'Admin', 'Doctor', 'Nurse']; return DataRow(cells: [ DataCell(Text('${rows[index]['username']}')), DataCell(Text('${rows[index]['nickname']}')), DataCell(Text(groups[rows[index]['group']])), DataCell( Flex( direction: Axis.horizontal, children: [ IconButton( onPressed: () {}, icon: const Icon( Icons.edit, size: 20, color: Colors.green, ), ), //edit IconButton( onPressed: () { rows[index]['status'] = rows[index]['status'] == 1 ? 0 : 1; // 通知数据源刷新表格 notifyListeners(); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text(rows[index]['status'] == 1 ? '已启用' : '已禁用'), action: SnackBarAction(label: '关闭', onPressed: () {}), duration: const Duration(milliseconds: 500))); }, icon: Icon( rows[index]['status'] == 1 ? Icons.lock : Icons.lock_open_rounded, size: 20, color: rows[index]['status'] == 1 ? Colors.blue : Colors.grey, ), ), //enable/disable IconButton( onPressed: () {}, icon: const Icon( Icons.delete, size: 20, color: Colors.grey, ), ) //delete:soft delete ], ), onTap: () { print('11111222'); }) ]); } @override bool get isRowCountApproximate => false; @override int get rowCount => rows.length; @override int get selectedRowCount => 0; } // 补充原代码缺失的UserAdd组件 class UserAdd extends StatelessWidget { const UserAdd({super.key}); @override Widget build(BuildContext context) { return AlertDialog( title: const Text('添加用户'), content: const Text('添加用户表单'), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('取消')), TextButton(onPressed: () => Navigator.pop(context), child: const Text('确认')), ], ); } }
关键修改点
- 将全局
rows移到UserManagePageState中,通过initState初始化,纳入组件状态管理。 UserManageDate接收rows参数,不再依赖全局变量,降低耦合度。- 按钮点击事件中,修改
status后调用notifyListeners(),触发PaginatedDataTable刷新对应行的内容。 - 补充了原代码中缺失的
UserAdd组件,避免编译错误。
内容的提问来源于stack exchange,提问作者Roinheart Lau
相关产品推荐
相关产品推荐

