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

Flutter PaginatedDataTable搜索功能实现方法咨询

为Flutter PaginatedDataTable实现搜索功能

核心思路

  • 保留原始全量用户数据,避免重复请求API
  • 通过TextEditingController监听搜索输入,实时过滤数据
  • 让PaginatedDataTable始终使用过滤后的列表作为数据源

具体实现步骤

1. 改造页面为StatefulWidget并添加状态变量

将当前页面改为StatefulWidget,维护搜索相关状态:

class UserManagePage extends StatefulWidget {
  const UserManagePage({super.key});

  @override
  State<UserManagePage> createState() => _UserManagePageState();
}

class _UserManagePageState extends State<UserManagePage> {
  late List<User> _originalUsers = []; // 存储API返回的全量数据
  late List<User> _filteredUsers = []; // 存储过滤后的数据
  final TextEditingController _searchController = TextEditingController();
  int sortColumnIndex = 0;
  bool isAscending = true;
  // 其他变量(width/height)根据你的代码定义
  double get width => MediaQuery.of(context).size.width;
  double get height => MediaQuery.of(context).size.height;

  @override
  void dispose() {
    _searchController.dispose();
    super.dispose();
  }
}

2. 处理StreamBuilder数据并添加搜索框

在StreamBuilder获取到数据后初始化原始列表,同时在表头添加搜索输入框:

StreamBuilder<List<User>>(
  stream: fetchUsersFromModel().asStream(),
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      // 首次获取数据时初始化全量列表
      if (_originalUsers.isEmpty) {
        _originalUsers = snapshot.data!;
        _filteredUsers = List.from(_originalUsers);
      }

      // 监听搜索输入变化
      _searchController.addListener(() {
        _filterUsers(_searchController.text);
      });

      return PaginatedDataTable(
        sortColumnIndex: sortColumnIndex,
        sortAscending: isAscending,
        columns: [
          const DataColumn(label: Text("Id")),
          DataColumn(
            label: const Text("Name"),
            onSort: (index, ascending) => _onSort(index, ascending),
          ),
          DataColumn(
            label: const Text("Username"),
            onSort: (index, ascending) => _onSort(index, ascending),
          ),
          DataColumn(
            label: const Text("Email"),
            onSort: (index, ascending) => _onSort(index, ascending),
          ),
          DataColumn(
            label: const Text("Roles"),
            onSort: (index, ascending) => _onSort(index, ascending),
          ),
          const DataColumn(label: Text("Actions")),
        ],
        header: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            // 搜索输入框
            SizedBox(
              width: 220,
              child: TextField(
                controller: _searchController,
                decoration: const InputDecoration(
                  hintText: "搜索用户",
                  prefixIcon: Icon(Icons.search),
                  border: OutlineInputBorder(
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                  ),
                  contentPadding: EdgeInsets.symmetric(vertical: 12),
                ),
              ),
            ),
            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: const 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: dataSource(_filteredUsers),
      );
    } else if (snapshot.hasError) {
      return Text("${snapshot.error}");
    }
    return const Center(child: CircularProgressIndicator());
  },
);

3. 实现数据过滤逻辑

添加过滤函数,根据搜索词匹配用户的姓名、用户名、邮箱或角色:

void _filterUsers(String query) {
  if (query.trim().isEmpty) {
    _filteredUsers = List.from(_originalUsers);
  } else {
    final lowerQuery = query.toLowerCase();
    _filteredUsers = _originalUsers.where((user) {
      final nameMatch = user.name?.toLowerCase().contains(lowerQuery) ?? false;
      final usernameMatch = user.username?.toLowerCase().contains(lowerQuery) ?? false;
      final emailMatch = user.email?.toLowerCase().contains(lowerQuery) ?? false;
      final roleMatch = user.roles?.first.name?.toLowerCase().contains(lowerQuery) ?? false;
      return nameMatch || usernameMatch || emailMatch || roleMatch;
    }).toList();
  }
  setState(() {});
}

4. 兼容排序功能

实现表头排序回调,确保排序基于当前过滤后的列表:

void _onSort(int columnIndex, bool ascending) {
  setState(() {
    sortColumnIndex = columnIndex;
    isAscending = ascending;

    switch (columnIndex) {
      case 1: // Name列排序
        _filteredUsers.sort((a, b) {
          final valA = a.name?.toLowerCase() ?? "";
          final valB = b.name?.toLowerCase() ?? "";
          return ascending ? valA.compareTo(valB) : valB.compareTo(valA);
        });
        break;
      case 2: // Username列排序
        _filteredUsers.sort((a, b) {
          final valA = a.username?.toLowerCase() ?? "";
          final valB = b.username?.toLowerCase() ?? "";
          return ascending ? valA.compareTo(valB) : valB.compareTo(valA);
        });
        break;
      case 3: // Email列排序
        _filteredUsers.sort((a, b) {
          final valA = a.email?.toLowerCase() ?? "";
          final valB = b.email?.toLowerCase() ?? "";
          return ascending ? valA.compareTo(valB) : valB.compareTo(valA);
        });
        break;
      case 4: // Roles列排序
        _filteredUsers.sort((a, b) {
          final valA = a.roles?.first.name?.toLowerCase() ?? "";
          final valB = b.roles?.first.name?.toLowerCase() ?? "";
          return ascending ? valA.compareTo(valB) : valB.compareTo(valA);
        });
        break;
    }
  });
}

5. 保持MyTable类兼容性

无需大幅修改MyTable类,确保它接收过滤后的列表即可:

DataTableSource dataSource(List<User> userList) => MyTable(datasList: userList, context: context);

关键注意事项

  • 必须保留原始全量数据,避免搜索操作丢失未匹配的条目
  • 过滤和排序操作都基于当前展示的列表,保证交互一致性
  • 使用setState触发UI更新,若使用GetX等状态管理工具,可将状态移至Controller中实现全局管理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 14:45:33