使用auto_route与flutter_bloc导航时BlocListener未触发如何解决
问题根因
BlocListener未触发是三个问题共同导致的:
- 判断逻辑语法错误:
listenWhen中写的c.routeType is SplashRoute恒为false。你在State中存储的routeType是Type类型的类引用对象,is关键字用于判断对象是否为某个类的实例,Type类型的对象不可能是SplashRoute类的实例,因此条件永远不满足。你之前打印SplashRoute == NavigationState.initial().routeType返回true用的是相等判断,和is的逻辑完全不同。 - 监听器注册时序不匹配:你在
BlocProvider的create回调中初始化Cubit时就调用..nav(SplashRoute)发射状态,这个时机远早于SplashPage构建、BlocListener注册到Cubit事件流的时间。flutter_bloc不会向注册晚于事件发射的监听器补发历史事件,因此这次状态变化根本不会被监听到。 - 组件初始状态不会触发BlocListener:BlocListener的触发规则是仅在监听器注册完成后,状态发生符合条件的变更时才执行回调,不会在组件挂载时自动读取当前状态执行一次。你的Cubit初始状态本身就是
SplashRoute,没有后续新的状态变更时,即使监听器注册完成也不会触发回调。
修复步骤
- 修正类型判断逻辑,将
is判断改为类型相等判断,同时增加新旧状态不一致的校验,避免重复触发 - 移除Cubit初始化时无意义的
..nav(SplashRoute)调用,初始状态已经设置为SplashRoute,重复发射相同状态不会触发任何更新 - 闪屏页的初始化跳转逻辑不要依赖BlocListener捕获初始状态,直接在首帧渲染完成后执行初始化任务,任务完成后再触发导航状态变更即可,避免时序问题
修正后核心代码
main.dart 中BlocProvider部分
BlocProvider( // 移除初始化时多余的nav调用 create: (_) => NavigationCubit(), ),
SplashPage 部分
class SplashPage extends StatelessWidget { const SplashPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { // 首帧渲染完成后执行闪屏初始化逻辑 WidgetsBinding.instance.addPostFrameCallback((_) async { // 这里替换为你的实际初始化逻辑,比如权限校验、资源预加载、token校验等 await Future.delayed(const Duration(seconds: 1)); if (!context.mounted) return; // 初始化完成后发射导航状态 context.read<NavigationCubit>().nav(HomeRoute); }); return MultiBlocListener( listeners: [ BlocListener<NavigationCubit, NavigationState>( listenWhen: (p, c) => p.routeType != c.routeType && c.routeType == HomeRoute, listener: (context, state) { LoggerService.simple.i('触发首页导航'); context.pushRoute(const HomeRoute()); }, ), // 其他监听器保持不变 ], child: const Scaffold( body: Center( child: CircularProgressIndicator(), ), ), ); } }
额外注意事项
- 不要在BlocListener的回调中再次调用触发当前状态的Cubit方法,否则容易造成循环触发
- 如果需要在组件初始化时就获取Cubit的当前状态执行逻辑,要么用
addPostFrameCallback在首帧后通过context.read获取当前状态执行,要么使用BlocConsumer在首次build时处理初始状态逻辑,不要依赖BlocListener捕获初始状态。
内容的提问来源于stack exchange,提问作者ph.zeng
相关产品推荐
相关产品推荐

