Flutter使用flutter_bloc时TextField文本搜索无响应问题求助
问题根源
- 全量加载与搜索逻辑共用同一套状态,无法区分返回的是全量数据还是搜索结果
- 搜索触发时未清空Widget内维护的
search列表,新的搜索结果仅通过addAll追加到原有全量数据后,视觉上无变化 - 滚动分页逻辑未适配搜索场景,搜索后滚动仍请求全量数据,不会加载下一页搜索结果
- 搜索分页参数
searchPage未在新搜索触发时重置,多次搜索后分页逻辑错乱 - 在
BlocBuilder的builder回调中直接修改局部数据,build方法多次触发会导致数据重复添加
解决方案
1. 拆分SearchState,区分全量和搜索状态
abstract class SearchState {} class AllRestaurantStateInitial extends SearchState {} // 全量加载相关状态 class LoadingAllRestaurantState extends SearchState {} class LoadedAllRestaurantState extends SearchState { final List<SearchData> data; final bool isFirstPage; LoadedAllRestaurantState(this.data, {this.isFirstPage = false}); } class ErrorAllRestaurantState extends SearchState { final String message; ErrorAllRestaurantState(this.message); } // 搜索相关状态 class LoadingSearchRestaurantState extends SearchState {} class LoadedSearchRestaurantState extends SearchState { final List<SearchData> data; final bool isFirstPage; LoadedSearchRestaurantState(this.data, {this.isFirstPage = false}); } class ErrorSearchRestaurantState extends SearchState { final String message; ErrorSearchRestaurantState(this.message); }
2. 修改SearchBloc逻辑,新增模式标识
class SearchBloc extends Bloc<SearchEvent, SearchState> { AllRestaurantDataService allRestaurantDataService; SearchRestaurantDataService searchRestaurantDataService; int page = 0; int searchPage = 0; bool isFetching = false; // 新增模式和关键字标识 bool isSearchMode = false; String currentSearchKeyword = ''; SearchBloc(this.allRestaurantDataService, this.searchRestaurantDataService) : super(AllRestaurantStateInitial()); @override Stream<SearchState> mapEventToState(SearchEvent event,) async* { if (event is LoadAllRestaurantEvent) { isSearchMode = false; yield LoadingAllRestaurantState(); try { List<SearchData> searchData = await allRestaurantDataService.makeRequestAllRestaurant(page) as List<SearchData>; yield LoadedAllRestaurantState(searchData, isFirstPage: page == 0); page++; } catch (e) { yield ErrorAllRestaurantState(e.toString()); } } else if (event is RefreshAllRestaurantEvent) { isSearchMode = false; page = 0; isFetching = false; yield LoadingAllRestaurantState(); try { List<SearchData> searchData = await allRestaurantDataService.makeRequestAllRestaurant(page) as List<SearchData>; yield LoadedAllRestaurantState(searchData, isFirstPage: true); page++; } catch (e) { yield ErrorAllRestaurantState(e.toString()); } } else if (event is LoadSearchRestaurantEvent) { // 新搜索触发时重置分页参数 if (event.search != currentSearchKeyword) { searchPage = 0; isFetching = false; currentSearchKeyword = event.search; } isSearchMode = true; yield LoadingSearchRestaurantState(); try { List<SearchData> searchData = await searchRestaurantDataService.makeRequestSearchRestaurant(event.search, searchPage) as List<SearchData>; yield LoadedSearchRestaurantState(searchData, isFirstPage: searchPage == 0); searchPage++; } catch (e) { yield ErrorSearchRestaurantState(e.toString()); } } } }
3. 修改SearchPage的BlocBuilder逻辑,适配多状态处理
仅需要替换原有Expanded包裹的BlocBuilder部分:
Expanded( child: Container( child: BlocBuilder<SearchBloc, SearchState>( builder: (context, state) { if (state is AllRestaurantStateInitial || (state is LoadingAllRestaurantState && search.isEmpty) || (state is LoadingSearchRestaurantState && search.isEmpty)) { return Center(child: PlatformCircularProgressIndicator()); } // 处理全量加载成功 else if (state is LoadedAllRestaurantState) { if (state.isFirstPage) search.clear(); search.addAll(state.data); BlocProvider.of<SearchBloc>(context).isFetching = false; isLoading = false; } // 处理搜索成功 else if (state is LoadedSearchRestaurantState) { if (state.isFirstPage) search.clear(); search.addAll(state.data); BlocProvider.of<SearchBloc>(context).isFetching = false; isLoading = false; } else if (state is ErrorAllRestaurantState) { return Center(child: Text(state.message)); } else if (state is ErrorSearchRestaurantState) { return Center(child: Text(state.message)); } return ListView.builder( controller: scrollController ..addListener(() { final bloc = BlocProvider.of<SearchBloc>(context); if (scrollController.position.atEdge && !bloc.isFetching) { if (scrollController.position.pixels != 0) { bloc.isFetching = true; isLoading = true; // 根据当前模式加载下一页数据 if (bloc.isSearchMode) { bloc.add(LoadSearchRestaurantEvent(bloc.currentSearchKeyword)); } else { bloc.add(LoadAllRestaurantEvent()); } setState(() {}); } } }), shrinkWrap: true, primary: false, scrollDirection: Axis.vertical, itemCount: search.length + (isLoading ? 1 : 0), itemBuilder: (context, index) { if (index < search.length) { return buildAllSearchRestaurant(search[index]); } else { Timer(Duration(milliseconds: 30), () { if (scrollController.hasClients) { scrollController.jumpTo(scrollController.position.maxScrollExtent); } }); return Center(child: PlatformCircularProgressIndicator()); } }, ); } ), ), )
补充说明
如果需要实现清空搜索框回到全量列表的逻辑,只需要在搜索框清空回调中触发RefreshAllRestaurantEvent即可。
内容的提问来源于stack exchange,提问作者Mohammed Nabil
相关产品推荐
相关产品推荐

