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
相关产品推荐
相关产品推荐

