Reputation: 627
I have tried to implement the dark and light theme in my flutter app. The code is below:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system,
home: SplashScreen(),
);
}
}
During runtime, I tried to change the theme from dark to light in the settings. But the theme change is not getting reflected in the app at that time. It is only updated after restarting the application. I have checked this on my Android phone. Is there anything I can do to update the theme during runtime?
Upvotes: 2
Views: 2293
Reputation: 3417
To make the logic you need, I might go using this method.
First of all - let's create a widget that will notify us about the system brightness change.
In order to change the system brightness - the user will need to close/put to background our app, do the changes in the settings and go back in to our app.
This is the place where we want to actually check if the brightness did change from the last time (when the app comes to foreground).
class BrightnessNotifier extends StatefulWidget {
final Widget child;
final VoidCallback? onBrightnessChanged;
const BrightnessNotifier({
Key? key,
required this.child,
this.onBrightnessChanged,
}) : super(key: key);
@override
_BrightnessNotifierState createState() => _BrightnessNotifierState();
}
class _BrightnessNotifierState extends State<BrightnessNotifier>
with WidgetsBindingObserver {
late Brightness _currentBrightness;
@override
void initState() {
_currentBrightness = SchedulerBinding.instance!.window.platformBrightness; // Save initial system brightness
WidgetsBinding.instance
?.addObserver(this); // Bind to app system state events
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance
?.removeObserver(this); // Don't forget to remove the observer
super.dispose();
}
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// App went back to foreground
final systemBrightness = SchedulerBinding.instance!.window.platformBrightness; // Check if current system brightness did change
if (_currentBrightness != systemBrightness) {
_currentBrightness = systemBrightness;
// Notify if it did
widget.onBrightnessChanged?.call();
}
}
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
After that we need to actually tell our app to re-render using the new brightness. In order to do that - we need to call build() method of our MyApp class (it will rebuild everything down the tree). To do this - we need to convert it in to a StatefulWidget and call it's setState() function to actually re-run the build() method.
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return BrightnessNotifier(
onBrightnessChanged: () {
setState(() {}); // Call this to re-build the widget
},
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.system,
home: SplashScreen(),
),
);
}
}
You can also put the same logic in to your MyApp widget but it would make it too bulky and hard to read.
Upvotes: 1