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

Flutter使用flutter_bloc时TextField文本搜索无响应问题求助

问题根源

  1. 全量加载与搜索逻辑共用同一套状态,无法区分返回的是全量数据还是搜索结果
  2. 搜索触发时未清空Widget内维护的search列表,新的搜索结果仅通过addAll追加到原有全量数据后,视觉上无变化
  3. 滚动分页逻辑未适配搜索场景,搜索后滚动仍请求全量数据,不会加载下一页搜索结果
  4. 搜索分页参数searchPage未在新搜索触发时重置,多次搜索后分页逻辑错乱
  5. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 20:36:02