使用BlocListener搭配Navigator.pushNamed出现导航栈页面重复问题
Bloc Listener导航栈重复问题解决方案
问题根源
你所有页面共享了同一个全局FieldsBloc实例,且所有页面的BlocConsumer都在监听同一个bloc的状态。当你在顶层页面触发事件emitFieldSuccessfulState时,所有还留在导航栈中的历史页面的listener都会被同时触发,每个listener都会执行一次pushNamed操作,最终导致导航栈重复插入多个页面。
另外你当前所有页面的listener都写死跳转到ExpertisePage.routeName,也不符合多页面依次跳转的逻辑,每个页面需要配置对应自己的下一级跳转目标。
解决方案
方案1:增加页面激活状态判断(快速修复)
在每个页面的listener中先判断当前页面是否为导航栈顶层的激活页面,只有激活页面才执行跳转逻辑,修改后的listener代码如下:
listener: (context, state) { if (state is FieldSuccessfulState) { // 仅当前页面处于顶层激活状态时才执行跳转 if (ModalRoute.of(context)?.isCurrent != true) return; SchedulerBinding.instance.addPostFrameCallback((_) { // 替换为当前页面对应的目标路由,不要所有页面写死同一个跳转目标 Navigator.of(context).pushNamed(ExpertisePage.routeName); }); } }
方案2:给状态增加触发标识+使用listenWhen过滤(规范实现)
- 修改
FieldSuccessfulState增加触发页面标识字段:
class FieldSuccessfulState extends FieldsState { var data; // 标识触发该状态的页面路由名 final String triggerRoute; FieldSuccessfulState(this.triggerRoute, [this.data]); }
- Bloc中emit成功状态时传入当前触发事件对应的页面路由名:
// 示例:处理Category页的跳转事件时,传入CategoryPage的路由名 yield FieldSuccessfulState(CategoryPage.routeName);
- 每个页面的
BlocConsumer增加listenWhen参数提前过滤状态,避免无效触发:
BlocConsumer<FieldsBloc, FieldsState>( listenWhen: (prev, current) { // 同时满足:当前页面激活、状态为成功状态、状态是当前页面触发的 final isCurrent = ModalRoute.of(context)?.isCurrent ?? false; return isCurrent && current is FieldSuccessfulState && current.triggerRoute == CategoryPage.routeName; }, listener: (context, state) { if (state is FieldSuccessfulState) { SchedulerBinding.instance.addPostFrameCallback((_) { Navigator.of(context).pushNamed(ExpertisePage.routeName); }); } }, builder: (context, state) { // 原有builder逻辑保持不变 } )
内容的提问来源于stack exchange,提问作者Hammas Ali
相关产品推荐
相关产品推荐

