Flutter使用hooks_riverpod切换主题时出现No ProviderScope found错误
解决Riverpod中"Bad state: No ProviderScope found"错误并实现明暗主题切换
问题背景
原本应用仅支持深色模式,尝试通过hooks_riverpod: ^2.5.1实现明暗主题切换时,抛出StateError (Bad state: No ProviderScope found)错误,根源是framework.dart中ProviderContainer.containerOf方法的scope参数为null。
原代码示例
main.dart
import 'dart:math'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:video_app/app_theme.dart'; import 'package:video_app/theme_provider.dart'; import 'JoinScreen.dart'; import 'signallingService.dart'; void main() { // start videoCall app runApp(VideoCallApp()); } class VideoCallApp extends HookConsumerWidget { VideoCallApp({super.key}); // signalling server url final String websocketUrl2 = "http://10.0.2.2:8000"; //for emulator final String websocketUrl = "http://localhost:8000"; final String websocketUrl3 = "http://192.5.10.8:8000"; final String selfCallerID = Random().nextInt(999999).toString().padLeft(6, '0'); @override Widget build(BuildContext context, WidgetRef ref) { final appThemeState = ref.watch(appThemeStateNotifier); SignallingService.instance.init( websocketUrl: websocketUrl, websocketUrl2: websocketUrl2, websocketUrl3: websocketUrl3, selfCallerID: selfCallerID, ); return MaterialApp( darkTheme: AppTheme.darkTheme, theme: AppTheme.lightTheme, themeMode: appThemeState.isDarkModeEnable ? ThemeMode.dark : ThemeMode.light, home: JoinScreen(selfCallerId: selfCallerID), ); } }
theme_provider.dart
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:flutter/material.dart'; final appThemeStateNotifier = ChangeNotifierProvider((ref) => AppThemeState()); class AppThemeState extends ChangeNotifier { var isDarkModeEnable = true; void setLightTheme() { isDarkModeEnable = true; notifyListeners(); } void setDarkTheme() { isDarkModeEnable = false; notifyListeners(); } }
问题分析
- 核心错误原因:Riverpod要求所有使用Provider的Widget必须包裹在
ProviderScope内部,它是管理Provider状态容器的核心作用域。原代码直接将VideoCallApp传入runApp,缺少ProviderScope包裹,导致HookConsumerWidget无法找到Provider上下文,触发错误。 - 主题逻辑bug:
AppThemeState中的setLightTheme和setDarkTheme赋值逻辑完全颠倒,会导致主题切换行为和预期相反。
修复方案
1. 给应用添加ProviderScope包裹
修改main.dart的main方法,用ProviderScope包裹整个应用:
void main() { // 用ProviderScope包裹应用,提供Riverpod状态管理的上下文 runApp(const ProviderScope(child: VideoCallApp())); }
2. 修正主题切换逻辑
修改theme_provider.dart中的方法赋值,让方法名与实际行为一致:
class AppThemeState extends ChangeNotifier { var isDarkModeEnable = true; void setLightTheme() { isDarkModeEnable = false; // 启用浅色主题 notifyListeners(); } void setDarkTheme() { isDarkModeEnable = true; // 启用深色主题 notifyListeners(); } }
3. 额外优化建议
将SignallingService.instance.init从build方法中移出,因为build会多次执行,可能导致重复初始化,建议放在main函数或专门的初始化逻辑中:
void main() { // 提前初始化信令服务 final selfCallerID = Random().nextInt(999999).toString().padLeft(6, '0'); SignallingService.instance.init( websocketUrl: "http://localhost:8000", websocketUrl2: "http://10.0.2.2:8000", websocketUrl3: "http://192.5.10.8:8000", selfCallerID: selfCallerID, ); runApp(ProviderScope(child: VideoCallApp(selfCallerID: selfCallerID))); } // 调整VideoCallApp接收selfCallerID参数 class VideoCallApp extends HookConsumerWidget { const VideoCallApp({super.key, required this.selfCallerID}); final String selfCallerID; // ... 其余代码不变 }
内容的提问来源于stack exchange,提问作者Naif Alluhaidan Engr
相关产品推荐
相关产品推荐

