使用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
相关产品推荐
相关产品推荐

