Can't rebuild MaterialApp using riverpod generator #3482
-
Hello, I am struggling with a seemingly simple functionality. I want to implement a themeMode switch to switch between light and dark mode. If I use the conventional ChangeNotifierProvider for this, everything works perfectly, but my question is how I could make this work with a GENERATED provider. Whenever I use a generated one, the UI does not update anymore. import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final appTheme = ref.watch(appThemeProvider);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Light/Dark Mode',
// Light Theme
theme: ThemeData(
primarySwatch: Colors.blue,
......
),
textTheme: const TextTheme(
headlineMedium: TextStyle(fontSize: 36.0, fontWeight: FontWeight.bold),
),
),
// Dark Theme
darkTheme: ThemeData(
primarySwatch: Colors.blue,
........
),
textTheme: const TextTheme(
headlineMedium: TextStyle(fontSize: 36.0, fontWeight: FontWeight.bold),
),
),
themeMode: appTheme.isDarkMode ? ThemeMode.dark : ThemeMode.light,
home: const MyHomePage(),
);
}
}
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final appTheme = ref.watch(appThemeProvider);
return Scaffold(
body: Center(
child: Switch(
value: appTheme.isDarkMode,
onChanged: (newValue) {
appTheme.toggleTheme();
},
),
),
);
}
}
class AppThemeState extends ChangeNotifier {
bool isDarkMode = false;
void toggleTheme() {
isDarkMode = !isDarkMode;
notifyListeners();
}
}
final appThemeProvider = ChangeNotifierProvider<AppThemeState>((ref) {
return AppThemeState();
}); |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 8 replies
-
If you want to mutate the state of a provider, you must put the mutating methods inside the provider and update the provider's state inside them. If you are using VS Code and Riverpod snippets, this is the one called riverpodClass. This will generate a provider called @riverpod
class AppTheme extends _$AppTheme {
@override
bool build() { // bool is the type of the value this provider provides (can be any type)
return false; // this is your initial state
}
void toggleTheme() {
state = !state; // here you set the state of the provider with whatever value you want
}
}
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isDarkMode = ref.watch(appThemeProvider);
return Scaffold(
body: Center(
child: Switch(
value: isDarkMode,
onChanged: (newValue) {
ref.read(appThemeProvider.notifier).toggleTheme();
},
),
),
);
}
}
BTW the naming of the provider suggests it provides a ThemeMode. With the implementation above, a more accurate class name would be IsDarkMode. |
Beta Was this translation helpful? Give feedback.
-
First define what type your provider provides: here it seems it's SettingsEntity. Then create a riverpodClass that builds an instance of this type. Then create the methods that mutate the state, i.e. the value that this provider provides. @riverpod
class SettingsController extends _$SettingsController {
@override
SettingsEntity build() {
final settingsRepository = ref.watch(settingsRepositoryProvider);
return GetSettings(settingsRepository).call();
}
void updateSettings(SettingsEntity settings) {
final settingsRepository = ref.read(settingsRepositoryProvider);
UpdateSettings(settingsRepository).call();
state = settings; // you can also replace this with ref.invalidateSelf()
}
} The suffix Controller, though usual for a provider that allows to mutate its state, is a bit misleading: the generated settingsControllerProvider does not provide a controller when you watch/read it, but the value it builds. To access the "controller" part of this provider you must use its notifier, i.e. ref.read(settingsControllerProvider.notifier). Note that you |
Beta Was this translation helpful? Give feedback.
-
If you place a breakpoint inside the build of the widget, is it hit when you toggle the switch? As for the onChanged being called twice, I don't know. It does not seem normal to me, but I don't think it is linked to riverpod: whether this callback is called is internal logic from the Switch. Also I don't understand why there is so much code in onChanged: I'm afraid you'll have to step debug. I do not see anything wrong in the code. |
Beta Was this translation helpful? Give feedback.
If you want to mutate the state of a provider, you must put the mutating methods inside the provider and update the provider's state inside them.
You can call these methods via the provider's notifier (which you must
read
).If you are using VS Code and Riverpod snippets, this is the one called riverpodClass. This will generate a provider called
appThemeProvider
(name of the class+Provider).