Themes in Flutter and how to use Material You dynamic colors
Contents
What is the theme and how to fit in the Material?
Theme can be understood as simple as a collection of properties, usually colors, text styles, shapes etc., that you can, and should, use across apps to style widgets.
Imagine a situation when a designer comes to you and says that every button now needs to be green instead of blue because the company went through a rebranding process. Let’s say the app has like ~50 buttons in different places. What do you do in this situation? If you didn’t use themes before, you’ll need to go one by one and adjust them manually. This is not an ideal situation. How can we do better? Ok, you’ve guessed it – when using the theme you can define a single property buttonBackgroundColor and refer to this property instead of color, which means you need to update only buttonBackgroundColor when needed.
Rebranding of the app is one possible situation when using theme comes in handy, but what is waaay more likely? Dark mode! When implementing dark mode, using themes is a must and it’s very simple once you understand the core concept.
Let’s address the elephant in the room – Material Design and its latest version, Material You or Material Design 3. It’s just one of the possible implementations of the theme concept and it’s used by material components provided by the Flutter team. But when you’re working with a designer, they will give you specific requirements on how the app needs to look, meaning theme. One of the biggest misconceptions around themes is that people tend to use material themes as the only one available solution, which is most probably not enough to describe the theme that your app needs. It doesn’t mean that you shouldn’t use material themes at all. You will probably use widgets like Scaffold or AlertDialog which use material themes, but try to not overly depend on it, define your own theme!
Material You and dynamic colors
Now that we understand what the theme is, we can tackle dynamic colors which is a new feature from Android 12. In principle, Android can give us a set of colors that match phone wallpaper so your app can blend into the user’s device as part of it rather than a separate entity. Pretty neat if you ask me. See what it may look like on the m3 side!
Ok, we know Android can give us some colors but how can we use them? Passing a list of colors is not that useful or easy to understand. The answer is a theme, m3 one theme to be exact.
Flutter 2.10.0 came with an updated version of ColorScheme class which now reflects m3 specifications with properties like tertiary or onTertiary, so now we use them properly.
Implementation!
A little demo of what you’ll be able to implement at the end of this article, have a look.
dynamic_colors_demo from Damian Moliński
As mentioned at the beginning, a theme is a simple set of properties, so let’s begin with defining some properties for our theme. We know that we want to use Android 12 dynamic colors, so let’s start here.
Remember that our goal is to implement an app theme that can be adjusted to whatever specification the designer gives us. So we can’t just use a material theme but we will still use built-in material components (Scaffold and others) which only know the material theme and depend on it. So how do we do that?
First, we can create an abstract class BaseColorScheme with everything we need to create a mapping to the material theme, and later we will extend BaseColorScheme with AppColorScheme which will hold all of the custom colors from our designer theme.
The material theme defines lots of properties so I’ll make it shorter here for the sake of readability. You can see full implementation in the repo linked and the end!
With AppColorScheme we can move one level higher and start working on AppThemeData. But before doing that, I think it is important to create one little naming distinction. I’ve been using the phrase “material theme” a lot in this article but how is “material theme” defined in Flutter? Well, ThemeData and its properties, like ColorScheme, which is unfortunate in my opinion but we can fix that very easily with simple typedef.
typedef MaterialThemeData = ThemeData; typedef MaterialColorScheme = ColorScheme; typedef MaterialTextTheme = TextTheme;
Personally, I found it really useful to have those names more specific.
With that being settled let’s see the implementation.
For the sake of simplicity, I’ve used MaterialColorScheme.fromSeed constructor, but you can easily imagine putting all of the specific colors in there. AppThemeFactory should be the only place where you’re allowed to access specific color definitions from like Color (0xFFFFFFFF).
Let’s add dynamic colors!
To do that, use the library from material.io team, dynamic_color. As of now in Flutter 2.10.0 and dynamic_color 1.1.2, you will need to override the material_color_utilities version so your pubspec.yaml should look like this.
Let’s look at DynamicColorPlugin and see how we have to work with it.
Ok, that’s what we’ve expected. We can get an instance of CorePalette. Wait a sec… but we’re working with ColorScheme in AppColorScheme, so we need to map CorePalette to ColorScheme with a simple extension on CorePalette.
Now we can put everything together back in AppThemeFactory. The only change is calling DynamicColorPlugin and using that ColorScheme if available.
How to access a theme?
The final piece of the puzzle is accessing the theme that we just created. The ideal candidate for such role is InheritedWidget or Provider. You’re probably already using Provider, so I’ll just go with it because of easier to read syntax.
Given that dark mode is a must nowadays, let’s implement that too, for a broader perspective.
AppTheme needs to be accessible everywhere, which means we have to put it above everything else in the widget tree, meaning in the MaterialApp builder method!
And of course, lightThemeData and darkThemeData are created with the factory like so.
Conclusion
Using themes makes your code a lot cleaner and implementing any changes much easier to execute. Additionally, if dark mode is a thing for you, then working with themes is a must-have.
Hopefully after reading this article you will have a better understanding of theming and make conscious decisions when to use built-in themes or create your own, if that’s what suits your project.
You can find the full app code here.