My app recently received a dark mode, followed by the request for a switch to turn it on and off.
It was a question of finding the best example and taking care of all the edge cases, so let me share my result.
The problem
By default, the theme defined in the MyApp class is used. By setting themeMode to ThemeMode.system, one can use the mode selected by the device. But I might want to use the light mode despite my device having dark mode as default. Additionally, I don’t want to restart the app to see the change.
Another problem that popped up later is the case, that the user wants to save the mode switch and use the app in the selected mode on the next start.
I updated this post with my answer.
The provider solution
My main source to success was this YouTube Video by Johannes Milke and his git repository. I will include my relevant parts from the app. Note that I inserted the normal and dark theme directly into the app – although Johannes solution is much smoother.
Here is the relevant part from main.dart.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (context) => ThemeProvider(),
builder: (context, _){
final themeProvider = Provider.of<ThemeProvider>(context);
return new MaterialApp(
title: 'My light and dark app',
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Color.fromRGBO(40, 105, 40, 1),
accentColor: Colors.black,
fontFamily: 'Raleway',
),
darkTheme: ThemeData(
brightness: Brightness.dark,
primaryColor: Color.fromRGBO(100, 255, 218, 1),
accentColor: Colors.white,
fontFamily: 'Raleway',
),
themeMode: themeProvider.themeMode,
);
});
}
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
Preferences prefs;
@override
void initState() {
super.initState();
prefs = new Preferences();
await prefs.load();
Provider.of<ThemeProvider>(context, listen: false).initThemeProvider();
} on Exception catch (e) {
// TODO handle exception
}
}
}
And my theme_provider.dart. I choose to ask for the light instead the dark theme to be more positive… or for no particular reason. When the app is initialized, or the theme is toggled, the selected mode should be stored in the sharedPreferences.
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class ThemeProvider extends ChangeNotifier {
Preferences prefs = Preferences();
ThemeMode themeMode = ThemeMode.system;
void initThemeProvider() async {
themeMode = getThemeMode();
notifyListeners();
}
ThemeMode getThemeMode() {
if (prefs == null || prefs.isLightMode == null) return ThemeMode.system;
return prefs.isLightMode ? ThemeMode.light : ThemeMode.dark;
}
bool get isLightMode {
themeMode = getThemeMode();
return themeMode == ThemeMode.light;
}
void toggleTheme(bool isOn) {
prefs.setIsLightMode(isOn);
themeMode = getThemeMode();
notifyListeners();
}
}
My switch is inside a ListTile within the settings page.
final themeProvider = Provider.of<ThemeProvider>(context);
This has to be defined above.
Here is the tiles code:
new SwitchListTile(
value: themeProvider.isLightMode,
onChanged: (value) => Provider.of<ThemeProvider>(context, listen: false).toggleTheme(value),
title: themeProvider.isLightMode ?
Text(DemoLocalizations.of(context).trans('light_mode')) :
Text(DemoLocalizations.of(context).trans('dark_mode')),
),
The additional coding
I use a utils class to define a unified background image. To switch it according to the selected mode, all I need is to get the provider from the build context.
static getBackgroundDecoration(BuildContext context) {
final themeProvider = Provider.of<ThemeProvider>(context);
return BoxDecoration(
image: new DecorationImage(
image: themeProvider.isLightMode ?
new AssetImage("resources/images/pergament20.png") :
new AssetImage("resources/images/pergament20_dark.png"),
fit: BoxFit.fill,
),
);
}
The result
The mode switch is not immediate, but most of the time the user wont play around with dark and light mode to brag about a delay. It works like a charm, although I might have missed additional places where I have to switch the colors dynamically.
On start, the app will fall back to the systems theme mode and append the saved theme with the provider initialization.
Schreibe einen Kommentar