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

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,
  ),
),

问题原因

  1. 全局数据未纳入状态管理:当前rows是全局变量,修改其中的值后,UserManagePageState无法感知变化,不会触发界面重建。
  2. 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('确认')),
      ],
    );
  }
}

关键修改点

  1. 将全局rows移到UserManagePageState中,通过initState初始化,纳入组件状态管理。
  2. UserManageDate接收rows参数,不再依赖全局变量,降低耦合度。
  3. 按钮点击事件中,修改status后调用notifyListeners(),触发PaginatedDataTable刷新对应行的内容。
  4. 补充了原代码中缺失的UserAdd组件,避免编译错误。

内容的提问来源于stack exchange,提问作者Roinheart Lau

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:55:54