Flutter Web中使用Riverpod Consumer组件作为顶层组件致应用崩溃
问题概述
使用Flutter 3.3.5 + Riverpod实现导航与状态管理,移动端运行正常,但Web端启动时MaterialWidget直接崩溃,抛出类型错误:Expected a value of type 'ProviderListenable', but got one of type 'Null'。
核心报错点
崩溃发生在MaterialWidget的build方法中调用ref.watch(navigatorKeyProvider)和ref.watch(scaffoldMessengerKeyProvider)时,说明这两个Provider在Web环境下未被正确注册,导致ref.watch获取到null值。
代码回顾
class App extends StatelessWidget { const App({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return ProviderScope( overrides: <Override>[ mainNavigationProvider.overrideWithProvider(mainNavigationProviderImpl), ], child: const MaterialWidget(), ); } } class MaterialWidget extends HookConsumerWidget { const MaterialWidget({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { const String appTitle = 'Flutter App'; return MaterialApp( title: appTitle, theme: lightTheme, darkTheme: darkTheme, scrollBehavior: scrollBehaviour, supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: AppLocalizations.localizationsDelegates, localeResolutionCallback: (locale, supportedLocales) => AppLocalizations.checkLocale(locale, supportedLocales), debugShowCheckedModeBanner: false, navigatorKey: ref.watch(navigatorKeyProvider), scaffoldMessengerKey: ref.watch(scaffoldMessengerKeyProvider), home: const NavigatorWidget(), ); } }
异常信息
══╡ WIDGETS库捕获异常 ╞═══════════════════════════════════════════════════════════
构建MaterialWidget(dirty, state: _ConsumerState#7cdf4)时抛出TypeErrorImpl:
期望类型为'ProviderListenable'的值,但实际得到'Null'类型
相关错误组件:
MaterialWidget
MaterialWidget:file:///Users/chiddy/Development/flutter_app/lib/app/app.dart:16:20
lib/app/app.dart:16
调用栈:
dart-sdk/lib/internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49 throw
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 99:3 castError
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 452:10 cast
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/classes.dart 635:14 as_C
dart-sdk/lib/_internal/js_dev_runtime/private/linked_hash_map.dart 171:26 putIfAbsent
packages/flutter_riverpod/src/consumer.dart 510:8 watch
packages/flutter_app/app/app.dart 38:24 build
...(省略后续栈信息)
解决步骤
1. 检查Provider定义
确保navigatorKeyProvider和scaffoldMessengerKeyProvider是正确的非空Provider类型,且未在Web环境下返回null:
// 正确定义示例 final navigatorKeyProvider = Provider<GlobalKey<NavigatorState>>((ref) { return GlobalKey<NavigatorState>(); }); final scaffoldMessengerKeyProvider = Provider<GlobalKey<ScaffoldMessengerState>>((ref) { return GlobalKey<ScaffoldMessengerState>(); });
避免在Provider内部做平台判断时遗漏Web端,比如不要出现以下错误写法:
// 错误示例:Web端返回null final navigatorKeyProvider = Provider<GlobalKey<NavigatorState>?>((ref) { if (!kIsWeb) return GlobalKey<NavigatorState>(); return null; });
2. 确认Provider已被正确注册
如果这两个Provider有对应的override实现,确保在ProviderScope的overrides中包含Web端的有效实现,或者直接使用默认实现,不要只针对移动端做override:
return ProviderScope( overrides: <Override>[ mainNavigationProvider.overrideWithProvider(mainNavigationProviderImpl), // 若需要override,添加这两个key的有效实现(Web端也适用) // navigatorKeyProvider.overrideWithValue(GlobalKey<NavigatorState>()), // scaffoldMessengerKeyProvider.overrideWithValue(GlobalKey<ScaffoldMessengerState>()), ], child: const MaterialWidget(), );
3. 替换ref.watch为ref.read(可选优化)
由于navigatorKey和scaffoldMessengerKey是全局静态key,不需要监听变化,使用ref.read更合适,避免不必要的监听逻辑:
navigatorKey: ref.read(navigatorKeyProvider), scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider),
4. 清理Web端缓存
如果之前有构建缓存残留,执行以下命令清理后重新构建:
flutter clean flutter pub get flutter run -d chrome
内容的提问来源于stack exchange,提问作者peer-f

