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
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
main.dart
If you want to use your
ThemeService
, you will have to create it somewhere first. Then, you can access it (e.g. using yourConsumer<>
widget). As you are using theprovider
package, the following implementation with a would be straightforward: