You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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字段,且事件处理中添加防抖逻辑。


关键注意事项

  1. 防抖处理:必须添加防抖逻辑,否则每输入一个字符都会发起API请求,导致性能问题和不必要的资源消耗。
  2. 避免旧结果污染:通过lastSearchText字段匹配当前输入,确保返回的结果属于最新的搜索请求,不会出现输入新文本后展示旧结果的情况。
  3. 空输入处理:当输入文本为空时,直接返回空集合,避免发起无效请求。

内容的提问来源于stack exchange,提问作者Trung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:03:20