Create a Theme and Primary Color Switcher For Your Flutter App Using Provider

Roaa Khaddam
Flutter Community
Published in
6 min readDec 20, 2021

Learn how to create a functional theme switcher with the ability to change the primary color of your app using Provider. You will also learn how to change the primary color swatch and get MaterialColor from Color.

Final Outcome, with some design differences 👀

TL, DR;

You can view and edit the code in this tutorial in this DartPad or view it in this Gist.

Update

I live coded a theme and primary color switcher similar to this one In a Flutter Festival session. In addition to the basic theme color switching, I used local storage with the Hive package to persist user selection.

Here’s the festival video, my session starts at 4:24:35

You can view the code from the session in this repo.

Theme Switcher

We’ll start by setting up our ChangeNotifierProvider ThemeProvider by giving it the selectedThemeMode variable and a function that updates it and notifies the listeners

class ThemeProvider with ChangeNotifier {
ThemeMode selectedThemeMode = ThemeMode.system;

setSelectedThemeMode(ThemeMode _themeMode) {
selectedThemeMode = _themeMode;
notifyListeners();
}
}

In our main.dart file, we’ll wrap our MaterialApp with a Consumer widget that listens to (consumes) the ThemeProvider and we’ll wrap that with a MultiProvider widget and add our ThemeProvider to its providers array.

class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => ThemeProvider(),
),
],
child: Consumer<ThemeProvider>(
child: HomePage(),
builder: (c, themeProvider, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
themeMode: themeProvider.selectedThemeMode,
home: child,
);
},
),
);
}
}

Notice here that we used the child in our Consumer builder instead of directly including the HomePage() inside the builder function. This is because Flutter itself handles theme changes and applies them and we don’t need to cause unnecessary rebuilds to see those changes applied, we only need to provide it with the updated value (themeProvider.selectedThemeMode)

Now let’s build the widget that will allow the user to actually change the selectedThemeMode. Let’s think first, what do we need? Well we need to show the user the 3 options that they can switch between which are available from Flutter, and they are the three values of the enum ThemeMode:

So do to build our UI, we can loop over a 3-item array containing the theme’s mode, title, and maybe icon? Instead of using an ugly looking key/value map for the items of that array, We’ll create a model class to define what our AppTheme needs

class AppTheme {
ThemeMode mode;
String title;
IconData icon;

AppTheme({
required this.mode,
required this.title,
required this.icon,
});
}

Now we can create our appThemes array

List<AppTheme> appThemes = [
AppTheme(
mode: ThemeMode.light,
title: 'Light',
icon: Icons.brightness_5_rounded,
),
AppTheme(
mode: ThemeMode.dark,
title: 'Dark',
icon: Icons.brightness_2_rounded,
),
AppTheme(
mode: ThemeMode.system,
title: 'Auto',
icon: Icons.brightness_4_rounded,
),
];

And this is how we use what we did so far in our ThemeSwitcher widget

Here’s what we did:

  1. lines 10 & 11: We used the appThemes array to generate the grid items.
  2. lines 36 & 38: Displayed each theme’s title and icon.
  3. lines 14 & 21: got the selectedThemeMode from the provider to highlight the selected theme widget and to prevent it from being clickable (line 16)
  4. line 16: Gave each themes’ themeMode to the GestureDetector’s onTap function’s setSelectedThemeMode from the provider

This should be your outcome so far:

Theme Switcher Preview

Now we’re done with our theme switcher and we can start with our cool primary color switcher!

Source: giphy.com

Primary Color Switcher

Okay this is the fun and tricky part! You might think that changing the primaryColor value in your ThemeData is enough! And you’re right! It’s still good. But you can only see it applied when you tell Flutter to use it by assigning a Theme.of(context).primaryColor to a widget’s color parameter…

From MaterialColor to Color

But what about all the other places that use the pretty blue color of Flutter defined by the default primarySwatch property value of the ThemeData? Well you can’t override it by using your primaryColor as it’s of type Color and the primarySwatch only accepts a MaterialColor. So in this tutorial we’ll create a cool function that will get a MaterialColor from a Color and solve this problem for us.

First we’ll create an AppColors helper class that handles our colors functionality.

  1. Line 2: Created the primary color options array
  2. Line 10: A cool function that can get a shade of a color (darker or lighter) inspired by this StackOverflow answer.
  3. Line 19: This is our cool function that gets a MaterialColor from a regular Color, to understand the function, you need to understand what a MaterialColor is, which is, simply put, a color with a primary value (first param; int), and 10 shades of that color (second param; Map<int, Color>) with the int map key as the shade index, which has the following options:

50, 100, 200, 300, 400, 500, 600, 700, 800, 900

The important thing here is that the shade with index 500 is the primary color of that material color. So we needed to find a way to get darker and lighter shades of a certain color to create all the shades.

Here’s a preview of what that function can do for each primary color option:

You can play around with the code that produced this image in this DartPad

Enough about this. Let’s actually create our primary color switcher!

Updating our ThemeProvider

Let’s first give our ThemeProvider the selectedPrimaryColor field (with a default value of the first color in our primaryColors array) and its setSelectedPrimaryColor setter/listeners notifier function

Color selectedPrimaryColor = AppColors.primaryColors[0];

setSelectedPrimaryColor(Color _color) {
selectedPrimaryColor = _color;
notifyListeners();
}

Now let’s listen to that value in our consumer

Here we were able to assign the MaterialColor generated from our primary color to the primarySwatch param of our theme and darkTheme MaterialApp parameters (lines 18 & 23)

Primary Color Switcher UI

Similar to what we did with our ThemeSwitcher, we loop over out AppColors.primaryColors array and build our widgets with a GestureDetector to make them selectable. You can see the full UI code in this gist.

This should be the final result after completing all the above:

Final Result

Notice how the background color of the AppBar changed without us having to set it to Theme.of(context).primaryColor in the ThemeData? What’s more? The color of the AppBar title automatically changes between white and black depending on the brightness of the primary color! Isn’t that awesome!

Source: giphy.com

You can view the all the code in this tutorial in this DartPad or Gist. BTW, I had to make some changes to the app in the preview image due to not being able to use an external image in DartPad 🤷🏽‍♀️ But you can download the images I used: light, dark, system.

Summary

Here’s what you should have gotten out of this tutorial (hopefully!)

  1. Ability to allow your app users to switch between light, dark and system themes
  2. Ability to allow your users to change the primary color of the app
  3. Using Provider/Consumer combo to make the above work.
  4. Know how to get a Color type from a MaterialColor type
  5. Some Gifs about Joey from friends 👀

Hit me up if you face any problems or have any questions. See you in the next one!

https://twitter.com/FlutterComm

--

--

Roaa Khaddam
Flutter Community

Software Engineer | 💙 Flutter / Dart Google Developer Expert