Flutter Bloc环境下ListView结合TextField搜索无响应问题求助
问题原因及解决方案
你的问题核心是在BlocBuilder中直接覆盖了用于搜索过滤的数据源,且ListView的构建依赖变量被Bloc状态重建时重置:
- 每次BlocBuilder因组件重建(比如
setState触发)执行时,只要当前状态还是CustomerGetAllLoadedState,就会把_customersFromRepo重新赋值为state.customers(全量数据),覆盖了你过滤后的结果。 - 你只用了一个列表变量同时承担「全量数据源」和「过滤后显示数据」的角色,导致数据逻辑混乱。
修复步骤
1. 拆分数据源变量
在State类中声明两个独立的列表:
// 保存从Bloc获取的全量原始数据,仅在Bloc状态更新时赋值 List<Customer> _allCustomers = []; // 保存过滤后的数据,用于ListView显示,搜索时更新 List<Customer> _filteredCustomers = [];
2. 修改BlocBuilder逻辑
在BlocBuilder中仅更新全量数据源,并初始化过滤列表,ListView基于_filteredCustomers构建:
BlocBuilder<CustomerBloc, CustomerState>( builder: (context, state) { if (state is CustomerLoadingState) { return const Center(child: CircularProgressIndicator()); } if (state is CustomerGetAllLoadedState) { // 仅当全量数据变化时更新,避免每次重建都重置 if (_allCustomers != state.customers) { _allCustomers = state.customers; // 初始化过滤列表为全量数据 _filteredCustomers = List.from(_allCustomers); } return SizedBox( height: h * 0.5, width: w * 0.5, child: _filteredCustomers.isNotEmpty ? ListView.builder( itemCount: _filteredCustomers.length, itemBuilder: (context, index) => Card( key: ValueKey(_filteredCustomers[index].id), child: Text(_filteredCustomers[index].name), ), ) : const Center(child: Text("无客户数据")), ); } // 处理其他状态,比如错误状态 return const Center(child: Text("加载失败")); }, )
3. 调整过滤方法
基于全量数据源_allCustomers进行过滤,更新_filteredCustomers:
void _runFilter(String enteredKeyword) { List<Customer> results = []; if (enteredKeyword.trim().isEmpty) { // 搜索为空时,恢复全量数据 results = List.from(_allCustomers); } else { final lowerKeyword = enteredKeyword.toLowerCase(); results = _allCustomers .where((customer) => customer.name.toString().toLowerCase().contains(lowerKeyword)) .toList(); } setState(() { _filteredCustomers = results; }); }
关键说明
- 拆分数据源后,全量数据只在Bloc返回新的全量列表时更新,不会被搜索操作修改,保证过滤的原始数据始终正确。
- BlocBuilder中加入了
_allCustomers != state.customers的判断,避免每次组件重建都重复赋值,提升性能。 - ListView的构建依赖
_filteredCustomers,当搜索触发setState后,组件重建时会使用最新的过滤列表渲染,解决了不更新的问题。
内容的提问来源于stack exchange,提问作者Nio74
相关产品推荐
相关产品推荐

