Flutter中Riverpod监听结合NavigatorState导航时currentState为null如何解决
问题根因
- 你把导航逻辑写在了
MaterialApp的builder回调里同步执行,这个回调属于组件同步构建阶段,此时Navigator还没完成和_navigatorKey的绑定,currentState必然是null。 - 之前Bloc的写法能正常运行,是因为
BlocListener的回调是在组件构建完成后的副作用阶段触发,根本不会在同步build流程里执行导航操作。 - 另外直接在build流程里写导航本身就是违规操作:Flutter要求build函数是纯函数,只负责返回组件,不能执行导航、弹窗这类副作用,不然只要组件触发重建就会重复执行跳转,后续就算解决了null问题也会出现页面乱跳的bug。
修复方案
直接用Riverpod官方提供的ref.listen处理状态变化对应的副作用即可,这个API的设计初衷就是干这个的——响应状态变化执行导航、弹Toast这类操作,回调触发时机安全,不会出现在build阶段拿不到NavigatorState的问题,也不会误触发重复跳转。
修正后的完整代码如下:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:workbench/app_pods.dart'; import 'package:workbench/app_router.dart'; import 'package:workbench/app_routes.dart'; class App extends StatelessWidget { App({Key? key}) : super(key: key); final _navigatorKey = GlobalKey<NavigatorState>(); @override Widget build(BuildContext context) { return Consumer( builder: (context, ref, child) { // 用listen监听认证状态变化,专门处理导航类副作用 ref.listen<bool>( authProvider, (previous, next) { final navigator = _navigatorKey.currentState; // 空判断兜底,避免组件销毁阶段触发回调导致空指针 if (navigator == null) return; if (next) { navigator.pushNamedAndRemoveUntil<void>( AppRoutes.home, (route) => false, ); } else { navigator.pushNamedAndRemoveUntil<void>( AppRoutes.signin, (route) => false, ); } }, // 冷启动时直接根据初始认证状态跳转,不需要额外写启动页判断 fireImmediately: true, ); return MaterialApp( navigatorKey: _navigatorKey, debugShowCheckedModeBanner: false, initialRoute: AppRoutes.splash, onGenerateRoute: AppRouter.generateRoute, theme: ThemeData( colorSchemeSeed: const Color(0xFF243EA5), ), // builder只做全局组件包装,不要放业务导航逻辑 builder: (context, child) { return child ?? const SizedBox.shrink(); } ); } ); } }
注意事项
- 记住API职责分离:
ref.watch只用来获取状态值构建Widget,ref.listen专门用来处理副作用,不要混着用。 - 不要对
currentState用!强制解包,加空判断兜底,极端场景(比如组件正在销毁)不会触发崩溃。 MaterialApp的builder回调只适合做全局组件包装(比如套全局Loading、做全局字体缩放适配),不要放业务逻辑。
内容的提问来源于stack exchange,提问作者Blue Waters
相关产品推荐
相关产品推荐

