You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
  }
}

问题分析

  1. 核心错误原因:Riverpod要求所有使用Provider的Widget必须包裹在ProviderScope内部,它是管理Provider状态容器的核心作用域。原代码直接将VideoCallApp传入runApp,缺少ProviderScope包裹,导致HookConsumerWidget无法找到Provider上下文,触发错误。
  2. 主题逻辑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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:27:04