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

使用BlocBuilder重建Stack内部Widget仅触发一次如何解决?

问题解答

可以使用BlocBuilder重建Stack内部的Widget,你遇到的仅重建一次的问题是代码实现有误导致的,具体根因和修复方案如下:

问题根因

  • Bloc中的异步逻辑缺少await修饰,导致加载中、搜索完成两个状态几乎同步触发,UI没有明显的切换感知,甚至可能被框架优化为仅触发一次重建
  • GoogleMapDetailsList作为StatefulWidget,在组件类型无变化、未设置唯一Key的情况下,Flutter只会更新其传入参数,不会销毁重建实例,因此initState只会在首次创建时执行一次
  • 未将搜索参数纳入Bloc的状态管理,依赖外部变量传值可能出现数据不同步的问题
  • 如果状态类没有正确覆写==和hashCode,Bloc会认为前后两次GoogleMapSearchInProgressState是相同状态,不会触发重建

修复方案

1. 修正Bloc逻辑,完善状态/事件定义

首先引入equatable库正确处理状态相等判断,将搜索参数纳入事件和状态管理,补全异步逻辑的await修饰:

// 状态基类继承Equatable,正确判断状态是否相等
abstract class GoogleMapSearchState extends Equatable {
  const GoogleMapSearchState();
  @override
  List<Object> get props => [];
}

class GoogleMapSearchInitialState extends GoogleMapSearchState {}
class GoogleMapSearchLoadInProgressState extends GoogleMapSearchState {}

// 搜索完成状态携带搜索参数
class GoogleMapSearchInProgressState extends GoogleMapSearchState {
  final String search;
  const GoogleMapSearchInProgressState({required this.search});
  @override
  List<Object> get props => [search];
}

// 搜索事件携带搜索参数
abstract class GoogleMapSearchEvent extends Equatable {
  const GoogleMapSearchEvent();
  @override
  List<Object> get props => [];
}
class StartSearchEvent extends GoogleMapSearchEvent {
  final String search;
  const StartSearchEvent({required this.search});
  @override
  List<Object> get props => [search];
}

// Bloc实现
class GoogleMapSearchBloc extends Bloc<GoogleMapSearchEvent, GoogleMapSearchState> {
  GoogleMapSearchBloc() : super(GoogleMapSearchInitialState());

  @override
  Stream<GoogleMapSearchState> mapEventToState(
    GoogleMapSearchEvent event,
  ) async* {
    if (event is StartSearchEvent) {
      yield GoogleMapSearchLoadInProgressState();
      // 补全await等待异步逻辑执行完成
      await Future.delayed(const Duration(seconds: 5));
      // 搜索完成状态携带传入的搜索参数
      yield GoogleMapSearchInProgressState(search: event.search);
    }
  }
}

2. 修正UI层代码,给组件添加唯一Key

给GoogleMapDetailsList添加唯一ValueKey,确保搜索值变化时框架会销毁旧组件、创建新实例,触发initState生命周期:

Column(
  children: [
    searchWidget(context),
    Expanded(
      child: Stack(
        children: [
          googleMap(),
          BlocBuilder<GoogleMapSearchBloc, GoogleMapSearchState>(
            builder: (context, state) {
              if (state is GoogleMapSearchInProgressState) {
                // 添加ValueKey,搜索值变化时自动重建组件
                return GoogleMapDetailsList(
                  key: ValueKey(state.search),
                  search: state.search,
                  goToPlaceCallback: _goToPlace,
                );
              } else if (state is GoogleMapSearchLoadInProgressState) {
                return const CircularProgressIndicator();
              } else {
                return Container();
              }
            },
          ),
        ],
      ),
    ),
  ],
),

Row searchWidget(BuildContext context) {
  return Row(
    children: [
      ButtonTemplate(
          textPaddingInset: EdgeInsets.zero,
          text: 'Search',
          buttonColor: Theme.of(context).accentColor,
          onPressed: () {
            setState(() {
              searchResult = true;
            });
            // 触发事件时传入当前最新的搜索值
            BlocProvider.of<GoogleMapSearchBloc>(context)
                .add(StartSearchEvent(search: search));
          })
    ],
  );
}

修改后每次触发搜索事件时,Bloc会按顺序抛出加载状态、携带最新搜索值的完成状态,BlocBuilder检测到状态变化后会重建对应的子组件,加了唯一Key的GoogleMapDetailsList每次都会重新创建实例,initState会正常触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 16:36:04