Building theme switcher using Provider and Shared Preferences
In this tutorial, we’ll learn how to create dark and light theme switcher in our Flutter application using Provider and we’ll persist the chosen theme using Shared Preferences so that when the users open the app for the next time, that theme that they chose would be permanent.
Contents
- Before we kick off
- Introduction
- Our example
- Shared Preferences
- Provider
- Switch theme using provider
- Persist the chosen theme using Shared Preferences
Before we kick off
Before we start we make sure:
- You have a text editor ( VsCode).
- You have Flutter running on your computer.
- You have basic knowledge of Flutter and Dart language.
Introduction
This article shows dynamically switching between themes in flutter during runtime using Provider as well as making the chosen theme permanent between app sessions using shared preferences.
Flutter Theme Switcher
For illustration, consider the following simple app.
Shared Preferences in Flutter
Wraps platform-specific persistent storage for simple data (NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). In case you want to go deeper, click here to get it done, using the official documentation.
Provider
You can use it to provide a value (usually a data model object) to anywhere in the widget tree. In case you want to go more deep click here to get it done, using the official documentation.
Building theme switcher using provider
Create a Flutter project named theme_swicher
and open it to your text editor.
flutter create theme_swicher
cd theme_swicher
On lib path create two folders:
Now on services path create theme_provider.dart
and on view path create settings.dart.
Let’s import some packages. Open pubspec.yaml
and add these dependecies:
dependencies:
provider: ^4.3.1
shared_preferences: ^0.5.8
It should looks like this:
You can find all the packages here. Save it or run this command:
$ flutter pub get
Now that we have set up the project we are ready to move forward.
Let’s create our themes. Open theme_provider.dart
:
theme_provider.dart
import 'package:flutter/material.dart';
ThemeData light = ThemeData(
brightness: Brightness.light,
primarySwatch: Colors.indigo,
accentColor: Colors.pink,
scaffoldBackgroundColor: Color(0xfff1f1f1)
);
ThemeData dark = ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.indigo,
accentColor: Colors.pink,
);
Let’s create a simple UI with a switch button so that we can choose the light or dark mode.
Open your settings.dart
file and past this:
import 'package:flutter/material.dart';
class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Theme Switcher"),
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SwitchListTile(
title: Text("Dark Mode"),
onChanged:(value){
} ,
value:false ,
),
Card(
child: ListTile(
title: Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit."),
),
),
]
),
)
);
}
}
On main.dart
import 'package:flutter/material.dart';
import 'package:theme_switcher/view/settings.dart';
import 'package:theme_switcher/services/theme_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Theme Provider',
theme: light,
home: SettingsPage(),
);
}
}
Run the app.
$ flutter run
So our job is to make this switch button work so that when users turns on the dark mode, the dark mode theme is applied and when they run off the dark mode the light theme is applied.
On theme_provider.dart
we’ll create a class that extends ChangeNotifier
that will allow us to change the dark/light theme.
Add this class on theme_provider.dart
class ThemeNotifier extends ChangeNotifier {
final String key = "theme";
SharedPreferences _pref;
bool _darkTheme;
bool get darkTheme => _darkTheme;
ThemeNotifier() {
_darkTheme = true;
}
toggleTheme(){
_darkTheme = !_darkTheme;
notifyListeners();
}
}
Above we have
-
key
to save in the Shared Preferences (we’ll see in the next section). _darkTheme
: this variable says whether the light or dark theme misses.get darkTheme
to get the value on_darkTheme
.- The
construct
that makes the dark theme the default. toggleTheme()
function that toggles the theme.- Into toggleTheme() we call
notifyListeners()
method to tell the “listeners” to rebuild and get the new value of_darkTheme
.
On main.dart
wrap the MaterialApp with ChangeNotifierProvider
and add a Consumer. You can find here the official documentation of the Provider.
It should look like this
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:theme_switcher/view/settings.dart';
import 'package:theme_switcher/services/theme_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ThemeNotifier(),
child: Consumer<ThemeNotifier>(
builder: (context, ThemeNotifier notifier, child) {
return MaterialApp(
title: "Flutter Provider",
theme: notifier.darkTheme ? dark : light,
home: SettingsPage(),
);
},
),
);
}
}
Before we run the app. Let’s update our switch button. We need to wrap the SwitchListTile
with a Consumer and the value from ThemeProvider
.
Consumer<ThemeNotifier>(
builder:(context, notifier, child) =>
SwitchListTile(
title: Text("Dark Mode"),
onChanged:(value){
notifier.toggleTheme();
} ,
value: notifier.darkTheme ,
),
),
Run it.
Congratulations!!!! The theme is changing right now.
But when we Hot Restart the App the theme doesn’t persist. We can solve this with Shared Preferences.
Persist the chosen theme using Shared Preferences
To persist the theme whenever the users close the app we need to create two functions on theme_provider.dart
. One to load the theme from Shared Preferences and the next one to save the theme to Shared Preferences.
Open theme_provider.dart
// _initPref() is to iniliaze the _pref variable
_initPrefs() async {
if(_pref == null)
_pref = await SharedPreferences.getInstance();
}
_loadFromPrefs() async {
await _initPrefs();
_darkTheme = _pref.getBool(key) ?? true;
notifyListeners();
}
_saveToPrefs() async {
await _initPrefs();
_pref.setBool(key, _darkTheme);
}
Whenever we instance the ThemeNotifier we would like to load the theme from Shared Preferences.
Update the constructor
ThemeNotifier() {
_darkTheme = true;
_loadFromPrefs(); // add this line
}
And whenever we toggle the theme we would like to save it to Shared Preferences so that it is persist.
Update the toggleTheme()
toggleTheme(){
_darkTheme = !_darkTheme;
_saveToPrefs(); // add this line
notifyListeners();
}
Before you run the app you have to rebuilt it. If you are on debug mode, stop the app and run it again.
Conclusion
Now you are able to access a value anywhere on your Widget tree using Provider – Change Notifier and With Shared Preferences you can read and write key-value pairs in a couple of lines easily. But always remember Shared Preferences is not a solution for storing critical data.
You can find the source code here.