Switching Themes in Flutter (☀️/🌙).

Flutter Queen✨
6 min readMar 23, 2023

--

Switching app theme with provider and saved with Shared Preferences.

Install Packages:

provider: ^6.0.5
shared_preferences: ^2.0.20

DarkThemePrefs Class:

Define a class DarkThemePrefs that has methods for setting and getting a boolean value related to the dark theme status of an application, using the shared_preferences package.

The setDarkTheme method takes a boolean value as a parameter and saves it to the shared preferences with the key THEME_STATUS. It uses the SharedPreferences.getInstance() method to get an instance of the shared preferences object and then calls the setBool method on that object to save the boolean value with the given key.

The getTheme method retrieves the previously saved boolean value related to the dark theme status from the shared preferences using the same key THEME_STATUS. It returns a Future<bool> type as it needs to await the asynchronous call to SharedPreferences.getInstance() method. It uses the getBool method on the shared preferences object to retrieve the boolean value. If there is no saved value, it returns false by default using the null-aware operator ??.

Overall, this code allows for the persistent storage and retrieval of a boolean value related to the dark theme status of an application using the shared_preferences package.

Save and fetch from the phone memory to show the last applied theme.

import 'package:shared_preferences/shared_preferences.dart';

class DarkThemePrefs {
//?defining theme status key
static const themeStatus = 'THEME_STATUS';

//***************SETTING DARK THEME METHOD TO SAVE***************//
setDarkTheme(bool value) async {
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.setBool(themeStatus, value);
}

//***************GETTING DARK THEME METHOD WHICH IS BEING SAVED ***************//

Future<bool> getTheme() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getBool(themeStatus) ?? false;
}
}

DarkThemeProvider Class:

Now define a class DarkThemeProvider that extends the ChangeNotifier class, indicating that it can be used to notify the UI when the dark theme status is changed. The class has a private boolean variable _darkTheme that represents the current status of the dark theme. It also has a public getter method getDarkTheme to retrieve the value of the private _darkTheme variable.

The setDarkTheme method sets the value of _darkTheme to the passed value boolean parameter and then saves this value to shared preferences by calling the setDarkTheme method of the DarkThemePrefs object, which we saw in the previous example. Finally, it also calls the notifyListeners() method to notify the UI about the change in the dark theme status.

The DarkThemeProvider class works in conjunction with the DarkThemePrefs class, where the DarkThemePrefs class handles the actual storage and retrieval of the boolean value related to the dark theme status in the shared preferences, while the DarkThemeProvider class allows for the manipulation of this value and notifies the UI when there is a change.

Overall, this code provides a convenient way to manage and update the dark theme status of an application, using shared preferences and the ChangeNotifier class.

import 'package:flutter/material.dart';
import 'package:groceteria_app/Services/ThemeService/dark_theme_prefs.dart';

class DarkThemeProvider with ChangeNotifier {
//? Object of Dark Theme Prefs Class in order to save the theme changes to shared preferences
DarkThemePrefs darkThemePrefs = DarkThemePrefs();

bool _darkTheme = false; //private
bool get getDarkTheme => _darkTheme; //getting private var

//***************SETTING DARK THEME METHOD***************//
set setDarkTheme(bool value) {
_darkTheme = value; //save this in the shared preferences
darkThemePrefs.setDarkTheme(value);
notifyListeners();
}
}

getCurrentTheme Method:

Initializes an object themeChangeProvider of the DarkThemeProvider class. It also defines a method getCurrentTheme, which is an asynchronous method that fetches the current theme from shared preferences using the darkThemePrefs.getTheme() method.

Inside the method, await themeChangeProvider.darkThemePrefs.getTheme() is used to retrieve the previously saved boolean value related to the dark theme status from the shared preferences using the getTheme() method of the DarkThemePrefs object inside the DarkThemeProvider.

Once the value is retrieved from the shared preferences, it is passed to the setDarkTheme method of the DarkThemeProvider object using the assignment operator =. The setDarkTheme method sets the retrieved value as the current dark theme status and saves it to the shared preferences using the setDarkTheme method of the DarkThemePrefs object. Finally, it notifies the UI about the change in the dark theme status by calling the notifyListeners() method.

Overall, this code allows for the retrieval and initialization of the dark theme status of an application by fetching it from the shared preferences using the DarkThemePrefs and DarkThemeProvider classes.

//* Fetching theme from shared preference so if the user choose the value so the chosen value will be fetched and applied

//Object oof Dark Theme Provider
DarkThemeProvider themeChangeProvider = DarkThemeProvider();

//Fetching theme from Shared Prefrence.
void getCurrentTheme() async {
themeChangeProvider.setDarkTheme =
(await themeChangeProvider.darkThemePrefs.getTheme());
}

Calling getCurrentTheme Method:

Initialize the state of a widget and set the current dark theme status by calling the getCurrentTheme() method.

@override
void initState() {
super.initState();
getCurrentTheme();
}

MultiProvider:

The MultiProvider widget is a widget provided by the provider package in Flutter, which allows for the creation of multiple Provider widgets and combines them into a single widget tree.

The ChangeNotifierProvider widget is a type of provider that creates and provides an instance of a ChangeNotifier class, which can then be accessed by child widgets.

It creates and returns a MultiProvider widget that provides access to the themeChangeProvider object of the DarkThemeProvider class using the ChangeNotifierProvider widget. This allows the child widgets to access and manipulate the dark theme status using the DarkThemeProvider object, as explained in the previous examples.

@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) {
return themeChangeProvider;
})
],

Consumer Widget:

Consumer widget, which allows for reactive programming in Flutter. The Consumer widget listens to changes in the DarkThemeProvider object and rebuilds the child widget whenever there are changes.

The builder function is a callback function that takes three arguments: context, themeProvider, and child. The context argument is a build context object, which is used to obtain the theme data. The themeProvider argument is the DarkThemeProvider object, which is provided by the ChangeNotifierProvider widget.

A MaterialApp widget that displays the home screen of the app using the theme data obtained from the DarkThemeProvider object using the getDarkTheme getter method. The Consumer widget listens to changes in the DarkThemeProvider object and rebuilds the child widget whenever there are changes, ensuring that the app is always up-to-date with the latest dark theme status.

child:Consumer<DarkThemeProvider>(builder: (context, themeProvider, child) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: Styles.themeData(themeProvider.getDarkTheme, context),
home: const HomeScreen(),
);
}),

Complete MyApp Code:

import 'package:flutter/material.dart';
import 'package:groceteria_app/Providers/ThemeProviders/dark_theme_provider.dart';
import 'package:provider/provider.dart';

import 'Features/Home/home_screen.dart';
import 'Themes/theme.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
//Object oof Dark Theme Provider
DarkThemeProvider themeChangeProvider = DarkThemeProvider();

//Getting theme from class DrakThemeProvider.
void getCurrentTheme() async {
themeChangeProvider.setDarkTheme =
(await themeChangeProvider.darkThemePrefs.getTheme());
}

@override
void initState() {
super.initState();
getCurrentTheme();
}

@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) {
return themeChangeProvider;
})
],
child:
Consumer<DarkThemeProvider>(builder: (context, themeProvider, child) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: Styles.themeData(themeProvider.getDarkTheme, context),
home: const HomeScreen(),
);
}),
);
}
}

In Home Screen:

Retrieve the current state of the dark theme status from the DarkThemeProvider object using the Provider.of method and the current BuildContext object. This allows for reactive programming in Flutter, where the UI is updated automatically whenever the state of the dark theme status changes.

final themeState = Provider.of<DarkThemeProvider>(context);

SwitchListTile Widget:

creates a SwitchListTile widget.

The secondary parameter is used to display an icon beside the title. If the getDarkTheme method of the themeState object is true, the Icons.dark_mode_rounded icon is displayed, otherwise the Icons.light_mode icon is displayed.

The value parameter is set to the current state of the dark theme status, which is obtained by calling the getDarkTheme method of the themeState object.

The setDarkTheme method of the themeState object is then called with the new value of the switch, which updates the dark theme status of the app.

return Scaffold(
body: Center(
child: SwitchListTile(
activeColor: Colors.yellow,
title: const Text("Theme"),
secondary: Icon(themeState.getDarkTheme
? Icons.dark_mode_rounded
: Icons.light_mode),
onChanged: (bool value) {
setState(() {
themeState.setDarkTheme = value;
});
},
value: themeState.getDarkTheme,
),
),
);

Complete Home SCreen Code:

import 'package:flutter/material.dart';
import 'package:groceteria_app/Providers/ThemeProviders/dark_theme_provider.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});

@override
State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
final themeState = Provider.of<DarkThemeProvider>(context);
return Scaffold(
body: Center(
child: SwitchListTile(
activeColor: Colors.yellow,
title: const Text("Theme"),
secondary: Icon(themeState.getDarkTheme
? Icons.dark_mode_rounded
: Icons.light_mode),
onChanged: (bool value) {
themeState.setDarkTheme = value;
},
value: themeState.getDarkTheme,
),
),
);
}
}

I hope this article was helpful to you. Thank you for taking the time to read it. Your feedback and suggestions are always welcome.

Support Me:

--

--

Flutter Queen✨
Flutter Queen✨

Written by Flutter Queen✨

Flutter Enthusiast | Software Engineer to be

No responses yet