如何让Material Autocomplete与Bloc模式协同工作?
解决方案
核心问题分析
你的代码中optionsBuilder仅在文本输入变化时触发,虽然触发了Bloc事件并更新了状态,但BlocBuilder重建时并不会重新调用optionsBuilder,导致无法展示最新的搜索结果。下面是两种适配Bloc模式的可行方案:
方案一:结合Future等待Bloc状态更新
在optionsBuilder中触发搜索事件后,等待Bloc返回匹配当前搜索文本的最新状态,再返回结果集合。需要在Bloc的状态中添加lastSearchText字段,用于区分不同搜索请求的结果,避免返回旧数据。
1. Bloc状态定义
class MyState extends Equatable { final List<GooglePlace> places; final String lastSearchText; const MyState({ this.places = const [], this.lastSearchText = '', }); MyState copyWith({ List<GooglePlace>? places, String? lastSearchText, }) { return MyState( places: places ?? this.places, lastSearchText: lastSearchText ?? this.lastSearchText, ); } @override List<Object> get props => [places, lastSearchText]; }
2. Bloc事件处理(含防抖)
使用防抖避免频繁发起API请求:
on<MakeAutocompleteEvent>( (event, emit) async { final text = event.textEditingValue.text.trim(); if (text.isEmpty) { emit(state.copyWith(places: [], lastSearchText: text)); return; } // 防抖300ms,避免输入过程中频繁请求 await Future.delayed(const Duration(milliseconds: 300)); // 检查是否有新的搜索请求,若有则放弃当前请求 if (state.lastSearchText != text) return; // 调用Google Place API获取结果 final places = await googlePlacesService.getAutocompleteResults(text); emit(state.copyWith(places: places, lastSearchText: text)); }, );
3. UI层代码
BlocBuilder<MyBloc, MyState>( builder: (context, state) { final bloc = context.read<MyBloc>(); return Autocomplete<GooglePlace>( displayStringForOption: (option) => option.description, optionsBuilder: (textEditingValue) async { final inputText = textEditingValue.text.trim(); if (inputText.isEmpty) return const Iterable.empty(); // 触发搜索事件 bloc.add(MakeAutocompleteEvent(textEditingValue)); // 等待Bloc返回匹配当前输入的最新状态 final updatedState = await bloc.stream.firstWhere( (s) => s.lastSearchText == inputText, orElse: () => state, ); return updatedState.places; }, onSelected: (place) { // 处理选中逻辑 print('Selected place: ${place.name}'); }, ); }, );
方案二:使用StreamBuilder作为选项数据源
将Bloc的状态流转换为Autocomplete可识别的结果流,直接在optionsBuilder中返回流的最新结果,确保状态更新时选项自动刷新。
UI层代码
BlocBuilder<MyBloc, MyState>( builder: (context, state) { final bloc = context.read<MyBloc>(); return Autocomplete<GooglePlace>( displayStringForOption: (option) => option.description, optionsBuilder: (textEditingValue) { final inputText = textEditingValue.text.trim(); if (inputText.isEmpty) return const Iterable.empty(); // 触发搜索事件 bloc.add(MakeAutocompleteEvent(textEditingValue)); // 从Bloc流中过滤出匹配当前输入的结果 return bloc.stream .where((s) => s.lastSearchText == inputText) .map((s) => s.places) .first; }, onSelected: (place) { // 处理选中逻辑 }, ); }, );
注:此方案同样需要Bloc状态包含lastSearchText字段,且事件处理中添加防抖逻辑。
关键注意事项
- 防抖处理:必须添加防抖逻辑,否则每输入一个字符都会发起API请求,导致性能问题和不必要的资源消耗。
- 避免旧结果污染:通过
lastSearchText字段匹配当前输入,确保返回的结果属于最新的搜索请求,不会出现输入新文本后展示旧结果的情况。 - 空输入处理:当输入文本为空时,直接返回空集合,避免发起无效请求。
内容的提问来源于stack exchange,提问作者Trung
相关产品推荐
相关产品推荐

