Mateen Kiani
Published on Fri Jul 14 2023·6 min read
In this article I will explain how you can implement dark theme for your app in a most simple and easy way. Using this method you can implement as many themes as you want and specify your own color schemes as needed. So without further do let’s start!
There are two packages that you need for this article provider and shared_preferences. Well why do we need them?
Provider is a very good solution for state management and as we need to change the state of our app by changing the theme therefore we needed some kind of a state management solution and provider came to rescue for that. The other package that we will use is shared_preferences. This package is used to store any information on file system and the reason why we are using it is because we need to permanently store the theme that user wants so he does not have to change it every time.
First of all add provider and shared_preferences as dependencies in your pubspec.yaml file.
dependencies:shared_preferences: ^0.5.10provider: ^4.3.2+2
Now in your lib folder create a storagemanager.dart file with the following code. This file will be used to save your theme choice permanently on file system.
import 'package:shared_preferences/shared_preferences.dart';class StorageManager {static void saveData(String key, dynamic value) async {final prefs = await SharedPreferences.getInstance();if (value is int) {prefs.setInt(key, value);} else if (value is String) {prefs.setString(key, value);} else if (value is bool) {prefs.setBool(key, value);} else {print("Invalid Type");}}static Future<dynamic> readData(String key) async {final prefs = await SharedPreferences.getInstance();dynamic obj = prefs.get(key);return obj;}static Future<bool> deleteData(String key) async {final prefs = await SharedPreferences.getInstance();return prefs.remove(key);}}
In this code snippet we have first imported our shared preferences package.Then we have created our StorageManger class that has three static methods to save, read and delete data from file system. Our save data function takes a key and a value to be written to the files system. As we procees you will see how we are calling these methods from our theme manager file. One more thing that you may have noticed is that these methods are async and the reason for that is because reading or deleting from file system can take some time so by using async functions we do not block our main thread.
Now have a look at thememanager.dart file
import 'package:flutter/material.dart';import '../services/storage_manager.dart';class ThemeNotifier with ChangeNotifier {final darkTheme = ThemeData(primarySwatch: Colors.grey,primaryColor: Colors.black,brightness: Brightness.dark,backgroundColor: const Color(0xFF212121),accentColor: Colors.white,accentIconTheme: IconThemeData(color: Colors.black),dividerColor: Colors.black12,);final lightTheme = ThemeData(primarySwatch: Colors.grey,primaryColor: Colors.white,brightness: Brightness.light,backgroundColor: const Color(0xFFE5E5E5),accentColor: Colors.black,accentIconTheme: IconThemeData(color: Colors.white),dividerColor: Colors.white54,);ThemeData _themeData;ThemeData getTheme() => _themeData;ThemeNotifier() {StorageManager.readData('themeMode').then((value) {print('value read from storage: ' + value.toString());var themeMode = value ?? 'light';if (themeMode == 'light') {_themeData = lightTheme;} else {print('setting dark theme');_themeData = darkTheme;}notifyListeners();});}void setDarkMode() async {_themeData = darkTheme;StorageManager.saveData('themeMode', 'dark');notifyListeners();}void setLightMode() async {_themeData = lightTheme;StorageManager.saveData('themeMode', 'light');notifyListeners();}}
In this file first we have imported our storagemanager that we created previously and then material.dart for defining our theme.
After that we have created our ThemeNotifier class with ChangeNotifier mixin.
ChangeNotifier is a simple class included in the Flutter SDK which provides change notification to its listeners. In other words, if something is a ChangeNotifier, you can subscribe to its changes. You can know more about mixins here.
Next we have defined our light and dark theme variables with theme data, you can specify your own colors as you want. Also you can create multiple theme objects with different color schemes to provide more customization. In ThemeNotifier Constructor we first check if there is some theme stored in the file system, if so we initialize our theme notifier with that theme else _themedata variable is initialized with light theme as default. We also have setLightTheme and setDarkTheme methods that can be called from our widget tree to switch to other theme.
We call notifyListener to notify the clients that the _themedata object has changed.
Finally here is our main.dart file.
import 'package:flutter/material.dart';import './src/theming/theme_manager.dart';import 'package:provider/provider.dart';void main() {return runApp(ChangeNotifierProvider<ThemeNotifier>(create: (_) => new ThemeNotifier(),child: MyApp(),));}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return Consumer<ThemeNotifier>(builder: (context, theme, _) => MaterialApp(theme: theme.getTheme(),home: Scaffold(appBar: AppBar(title: Text('Hybrid Theme'),),body: Row(children: [Container(child: FlatButton(onPressed: () => {print('Set Light Theme'),theme.setLightMode(),},child: Text('Set Light Theme'),),),Container(child: FlatButton(onPressed: () => {print('Set Dark theme'),theme.setDarkMode(),},child: Text('Set Dark theme'),),),],),),),);}}
In our runApp method we have wrapped our MyApp which is basically the root widget of our app with our ThemeNotifier. By doing so we can access our theme from anywhere inside widget tree and call our setLightTheme or setDarkTheme functions to quickly switch theme.
In our MyApp widget we have used Consumer with type ThemeNotifier to get our themedata inside theme variable and to provide it as a theme in our MaterialApp.
If you don’t want to understand whatever is happening simply download the two thememanager and storagemanager files and in your main.dart file wrap your app with ThemeNotifier provider that’s it you have just implemented dark and light theme for your app!
Here is the link to the github project. Don’t forget to star it 🙂
Follow us on twitter, facebook, github
If you have any questions you can mention in the comments below!