Flutter中setState引发无限循环:列表添加逻辑单次执行优化
解决Flutter Bloc中列表重复添加数据的问题
你的核心问题是在BlocBuilder的builder回调里修改了外部的cards列表——builder属于Widget build流程的一部分,每次父组件调用setState、Bloc状态更新,甚至系统触发的重绘(比如屏幕旋转),都会重新执行这个回调,导致循环重复添加数据。
下面是两种高效的解决方案,优先推荐第一种:
方案1:将列表状态移到Bloc内部(最优解)
Bloc的核心职责就是管理状态,把cards列表交给Bloc维护,彻底避免Widget层的状态混乱:
步骤1:修改Bloc状态结构
让BlackjackkDrawLoaded状态包含完整的卡牌列表:
class BlackjackkDrawLoaded extends BlackjackkState { final List<String> cards; final Bjdraw bjdraw; BlackjackkDrawLoaded({required this.cards, required this.bjdraw}); }
步骤2:在Bloc中处理卡牌添加逻辑
处理DrawCardEvent时,合并新卡牌到现有列表,再发射新状态:
on<DrawCardEvent>((event, emit) async { // 原有获取卡牌数据的逻辑 final bjdraw = await fetchDrawCard(event.deck_id, event.draw_count); // 获取当前状态中已有的卡牌列表(无则为空) final currentCards = state is BlackjackkDrawLoaded ? (state as BlackjackkDrawLoaded).cards : []; // 合并新卡牌 final updatedCards = [ ...currentCards, ...bjdraw.cards.map((card) => card.image) ]; // 发射包含完整列表的新状态 emit(BlackjackkDrawLoaded(cards: updatedCards, bjdraw: bjdraw)); });
步骤3:Widget层只负责渲染
BlocBuilder直接读取状态中的列表渲染,不再修改外部变量:
BlocBuilder<BlackjackkBloc, BlackjackkState>( builder: (context, state) { if (state is BlackjackkDrawLoaded) { return Column( children: state.cards.map((imgUrl) => Text(imgUrl)).toList(), ); } return Text("o"); }, ),
方案2:用BlocListener监听状态变化(不修改Bloc时使用)
如果暂时不想改动Bloc的状态结构,可以用BlocListener替代在builder里做副作用操作——BlocListener的listener回调只会在状态变化时触发一次,不会随build重复执行:
BlocListener<BlackjackkBloc, BlackjackkState>( listener: (context, state) { if (state is BlackjackkDrawLoaded) { setState(() { // 仅在状态更新时添加新卡牌 cards.addAll(state.bjdraw.cards.map((card) => card.image)); }); } }, child: BlocBuilder<BlackjackkBloc, BlackjackkState>( builder: (context, state) { if (state is BlackjackkDrawLoaded) { return Column( children: cards.map((imgUrl) => Text(imgUrl)).toList(), ); } return Text("o"); }, ), ),
关键注意事项
永远不要在build方法(包括BlocBuilder的builder回调)里执行有副作用的操作(比如修改列表、发起网络请求、更新状态变量)——build应该是纯函数,只根据当前状态返回对应的Widget,否则必然会出现重复执行、状态混乱的问题。
内容的提问来源于stack exchange,提问作者LaurenTsai
相关产品推荐
相关产品推荐

