Unable to access the state of ValueNotifier inside the MaterialApp
I've been experimenting with ValueNotifier
in Dart and Flutter and came across an interesting issue.
I defined two custom themes and tried to access them from two different parts of the app using ValueListenableBuilder
I am able to correctly get the theme data within the Container
inside the App
component. However, it doesn't work with the MaterialApp
. Please shed some light on why this is happening.
PS: I know there are better ways to manage the theme, I am just experimenting.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final _themeManager = ThemeManager();
return ValueListenableBuilder<ThemeData>(
valueListenable: _themeManager.theme,
builder: (context, theme, child) {
return MaterialApp(
title: 'Flutter Demo',
// theme not updating ❌
theme: theme,
home: const App(),
);
});
}
}
class Theme {
static ThemeData primaryTheme =
ThemeData(primarySwatch: Colors.amber, primaryColor: Colors.amber);
static ThemeData secondaryTheme =
ThemeData(primarySwatch: Colors.blue, primaryColor: Colors.blue);
}
class ThemeManager {
final ValueNotifier<ThemeData> _theme =
ValueNotifier<ThemeData>(Theme.primaryTheme);
ValueNotifier<ThemeData> get theme => _theme;
void changeTheme(ThemeData theme) {
_theme.value = theme;
}
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final _themeManager = ThemeManager();
return Scaffold(
appBar: AppBar(
title: const Text('App'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ValueListenableBuilder<ThemeData>(
valueListenable: _themeManager.theme,
builder: (context, theme, child) {
return Container(
height: 100,
width: 100,
// theme updating correctly ✔️
color: theme.primaryColor,
);
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
_themeManager.theme.value =
_themeManager.theme.value == Theme.primaryTheme
? Theme.secondaryTheme
: Theme.primaryTheme;
},
child: const Text('Toggle Theme')),
],
),
),
);
}
}
You are working with two different instances of ThemeManager
. One in MyApp
other in the build method of the App
Widget. On tapping the button, you are only updating the second instance, that's why only the container is being updated. Try to use a single instance of ThemeManager
or define ThemeManager as a Singleton.
Update ThemeManager as
class ThemeManager {
static ThemeManager? _instance;
ThemeManager._();
static ThemeManager get instance => _instance ??= ThemeManager._();
final ValueNotifier<ThemeData> _theme =
ValueNotifier<ThemeData>(Theme.primaryTheme);
ValueNotifier<ThemeData> get theme => _theme;
void changeTheme(ThemeData theme) {
_theme.value = theme;
}
}
and get the instance of ThemeManager as
final _themeManager = ThemeManager.instance;
Here is the updated code on dartpad.