Flutter:如何为FutureBuilder加载的ListView列表实现搜索筛选功能
实现方案
你当前的核心问题是没有对接口返回的员工数据集做本地过滤,同时每次输入触发setState都会重新请求Firebase接口,存在不必要的性能损耗。按以下步骤修改即可:
核心修改点
- 新增两个List变量分别存储全量员工数据和筛选后的员工数据,避免重复请求接口
- 搜索内容变化时,对全量数据做模糊匹配筛选,刷新筛选列表即可
- 匹配逻辑默认支持姓名、部门、职位三个字段的大小写不敏感模糊搜索,可自行增减匹配字段
修改后完整代码
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'directory_details_screen.dart'; class Directory extends StatefulWidget { @override _DirectoryState createState() => _DirectoryState(); } class _DirectoryState extends State<Directory> { TextEditingController _searchController = TextEditingController(); // 新增变量:存储全量员工数据、筛选后员工数据、加载状态 List<Employee> _allEmployees = []; List<Employee> _filteredEmployees = []; bool _isLoading = true; Future getEmpData() async { var response = await http.get(Uri.parse('你的Firebase数据库地址')); var jsonData = jsonDecode(response.body); List<Employee> employees = []; for (var e in jsonData) { Employee employee = Employee( e["displayName"], e["department"], e["jobTitle"], e["mobilePhone"], e["workEmail"], e["photoUrl"], e["workPhoneExtension"]); employees.add(employee); } // 赋值全量数据和初始筛选数据 _allEmployees = employees; _filteredEmployees = employees; setState(() { _isLoading = false; }); } @override void initState() { super.initState(); // 页面初始化时请求一次数据即可 getEmpData(); _searchController.addListener(_onSearchChanged); } @override void dispose() { _searchController.removeListener(_onSearchChanged); _searchController.dispose(); super.dispose(); } _onSearchChanged() { String searchKey = _searchController.text.toLowerCase(); setState(() { if (searchKey.isEmpty) { // 搜索内容为空时展示全部数据 _filteredEmployees = _allEmployees; } else { // 按匹配规则过滤,可自行增减匹配字段 _filteredEmployees = _allEmployees.where((employee) { return employee.displayName.toLowerCase().contains(searchKey) || employee.department.toLowerCase().contains(searchKey) || employee.jobTitle.toLowerCase().contains(searchKey); }).toList(); } }); } Widget build(BuildContext context) { return Scaffold( body: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: TextField( controller: _searchController, decoration: InputDecoration( labelText: 'Search', suffixIcon: Icon(Icons.search), ), ), ), SizedBox(height: 10), Expanded( child: _isLoading ? Center(child: CircularProgressIndicator()) : _filteredEmployees.isEmpty ? Center(child: Text('未找到匹配的员工信息')) : ListView.builder( itemCount: _filteredEmployees.length, itemBuilder: (context, i) { Employee employee = _filteredEmployees[i]; return Card( child: ListTile( leading: CircleAvatar( backgroundImage: NetworkImage(employee.photoUrl), ), isThreeLine: true, title: Text(employee.displayName, style: TextStyle(fontWeight: FontWeight.bold)), subtitle: Text("Department: " + employee.department + "\n" + "Title: " + employee.jobTitle), onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => EmployeeDetails( employee: employee))); }, ), ); }), ), ], ), ); } } class Employee { final String displayName, department, jobTitle, mobilePhone, workEmail, photoUrl, workPhoneExtension; Employee(this.displayName, this.department, this.jobTitle, this.mobilePhone, this.workEmail, this.photoUrl, this.workPhoneExtension); }
额外说明
如果需要增加匹配字段(比如邮箱、手机号),只需要在_onSearchChanged的过滤逻辑里新增对应的判断条件即可。
内容的提问来源于stack exchange,提问作者Bryan S
相关产品推荐
相关产品推荐

