skip to Main Content

I am receiving this error

Error: Could not find the correct Provider<ThemeService> above this Consumer<ThemeService> Widget


This happens because you used a `BuildContext` that does not include the provider
of your choice. 

There are a few common scenarios:

- You added a new provider in your `main.dart` and performed a hot-reload.
  To fix, perform a hot-restart.

- The provider you are trying to read is in a different route.

  Providers are "scoped". So if you insert of provider inside a route, then
  other routes will not be able to access that provider.

- You used a `BuildContext` that is an ancestor of the provider you are trying to read.

  Make sure that Consumer<ThemeService> is under your MultiProvider/Provider<ThemeService>.
  This usually happens when you are creating a provider and trying to read it immediately.

  For example, instead of:


  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // Will throw a ProviderNotFoundError, because `context` is associated
      // to the widget that is the parent of `Provider<Example>`
      child: Text(context.watch<Example>().toString()),
    );
  }
  

  consider using `builder` like so:

 
  Widget build(BuildContext context) {
    return Provider<Example>(
      create: (_) => Example(),
      // we use `builder` to obtain a new `BuildContext` that has access to the provider
      builder: (context, child) {
        // No longer throws
        return Text(context.watch<Example>().toString());
      }
    );
  }

theme.dart

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

extension ThemeModeExt on ThemeMode {
  int get mode {
    switch (this) {
    case ThemeMode.system:
    return 0;
    case ThemeMode.light:
    return 1;
    case ThemeMode.dark:
    return 2;
    }
  }
}

@immutable
abstract class ThemeService extends ChangeNotifier {
  static bool isThemeDark = false;
  Future<void> setThemeMode(ThemeMode mode);
  Future<void> toggleThemeMode();
  ThemeMode get themeMode;
  bool get isDarkThemeOn;
}

const ThemeMode defaultTheme = ThemeMode.system;

class ThemeServiceImpl extends ThemeService {
  final _ThemePrefs _prefs = _ThemePrefs();
  ThemeMode _mode = defaultTheme;
  @override
  ThemeMode get themeMode => _mode;
  @override
  bool get isDarkThemeOn => themeMode == ThemeMode.dark;
  ThemeServiceImpl(Brightness brightness) {
  _prefs.getThemeMode().then((mode) {
  _applyModeAndNotify(mode == ThemeMode.system ? _resolveTheme(brightness) : mode);
    });
  }

  ThemeMode _resolveTheme(Brightness brightness) =>
  brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark;

  Future<void> _applyModeAndNotify(ThemeMode value) async {
    if (_mode != value) {
    _mode = value;
    ThemeService.isThemeDark = isDarkThemeOn;
    notifyListeners();
    }
  }

  @override
  Future<void> setThemeMode(ThemeMode mode) =>
    _prefs.setThemeMode(mode).then((_) => _applyModeAndNotify(mode));

  @override
  Future<void> toggleThemeMode() =>
    setThemeMode(isDarkThemeOn ? ThemeMode.light : ThemeMode.dark);
}

class _ThemePrefs {
  static const keyThemeMode = "keyThemePrefsMode";

  Future<SharedPreferences> _prefs() => SharedPreferences.getInstance();

  Future<void> setThemeMode(ThemeMode theme) =>
    _prefs().then((prefs) => prefs.setInt(keyThemeMode, theme.mode));

  Future<ThemeMode> getThemeMode({ThemeMode fallback = defaultTheme}) =>
    _prefs().then((prefs) {
    final mode = prefs.getInt(keyThemeMode);
    if (mode == null) return fallback;
    return ThemeMode.values.firstWhere((it) => it.mode == mode, orElse: () => fallback);
    });
}

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:spending/router/router.dart';
import 'package:spending/services/theme.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({super.key});

  final _appRouter = AppRouter();

  @override
  Widget build(BuildContext context) {
    return Consumer<ThemeService>(
      builder: (contextTheme, theme, _) {
        final themeMode = theme.themeMode;
        if (themeMode != _currentTheme) {
          _currentTheme = themeMode;
          _rebuildAllChildren(contextTheme);
        }
        return MaterialApp.router(
          debugShowCheckedModeBanner: false,
          themeMode: themeMode,
          routerConfig: _appRouter.config(),
        );
      },
    );
  }
}

void _rebuildAllChildren(BuildContext context) {
  void rebuild(Element el) {
    el.markNeedsBuild();
    el.visitChildren(rebuild);

    (context as Element).visitChildren(rebuild);
  }
}

ThemeMode _currentTheme = ThemeMode.light;

2

Answers


  1. Chosen as BEST ANSWER

    I couldn't rebuild the application because I'm not using ThemeData but a custom class with colors. I modified my code a bit and this is what I came up with. Thanks more for your help.

    theme.dart

    const ThemeMode defaultTheme = ThemeMode.system;
    
    class ThemeService extends ChangeNotifier {
      static bool isThemeDark = false;
    
      final _ThemePrefs _prefs = _ThemePrefs();
    
      ThemeMode _mode = defaultTheme;
    
      ThemeMode get themeMode => _mode;
    
      bool get isDarkThemeOn => themeMode == ThemeMode.dark;
    
      ThemeService(Brightness brightness) {
        _prefs.getThemeMode().then((mode) {
          _applyModeAndNotify(
              mode == ThemeMode.system ? _resolveTheme(brightness) : mode);
        });
      }
    
      ThemeMode _resolveTheme(Brightness brightness) =>
          brightness == Brightness.light ? ThemeMode.light : ThemeMode.dark;
    
      Future<void> _applyModeAndNotify(ThemeMode value) async {
        if (_mode != value) {
          _mode = value;
          isThemeDark = isDarkThemeOn;
          notifyListeners();
        }
      }
    
      Future<void> setThemeMode(ThemeMode mode) =>
          _prefs.setThemeMode(mode).then((_) => _applyModeAndNotify(mode));
    
      Future<void> toggleThemeMode() async =>
          setThemeMode(isDarkThemeOn ? ThemeMode.light : ThemeMode.dark);
    }
    
    class _ThemePrefs {
      static const keyThemeMode = "keyThemePrefsMode";
    
      Future<SharedPreferences> _prefs() => SharedPreferences.getInstance();
    
      Future<void> setThemeMode(ThemeMode theme) =>
          _prefs().then((prefs) => prefs.setInt(keyThemeMode, theme.mode));
    
      Future<ThemeMode> getThemeMode({ThemeMode fallback = defaultTheme}) =>
          _prefs().then((prefs) {
            final mode = prefs.getInt(keyThemeMode);
            if (mode == null) return fallback;
            return ThemeMode.values
                .firstWhere((it) => it.mode == mode, orElse: () => fallback);
          });
    }
    

    main.dart

    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({super.key});
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      final _appRouter = AppRouter();
    
      @override
      Widget build(BuildContext buildContext) {
        return ChangeNotifierProvider<ThemeService>(create: (context) {
          return ThemeService(Brightness.light);
        }, builder: (context, child) {
          var service = context.watch<ThemeService>();
          final themeMode = service.themeMode;
          if (themeMode != _currentTheme) {
            _currentTheme = themeMode;
            _rebuildAllChildren(context);
          }
          return MaterialApp.router(
            debugShowCheckedModeBanner: false,
            themeMode: themeMode,
            routerConfig: _appRouter.config(),
          );
        });
      }
    }
    
    void _rebuildAllChildren(BuildContext context) {
      void rebuild(Element el) {
        el.markNeedsBuild();
        el.visitChildren(rebuild);
      }
      (context as Element).visitChildren(rebuild);
    }
    
    ThemeMode _currentTheme = ThemeMode.light;
    

  2. If you want to use your ThemeService, you will have to create it somewhere first. Then, you can access it (e.g. using your Consumer<> widget). As you are using the provider package, the following implementation with a would be straightforward:

    @override
    Widget build(BuildContext context) {
      return Provider<ThemeService>(
        create: (context) {
          // Create your instance of ThemeService here
          // For example by initiating it with Brightness.light
          return ThemeService(Brightness.light);
        },
        builder: (context, child) {
          // Here you can access your ThemeService
          // This part will rebuild when ThemeService object calls notifyListeners()
          var service = context.watch<ThemeService>();
    
          final themeMode = service.themeMode;
          return MaterialApp.router(...);
        }
      );
    }
    ```
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search