Overview of Flutter Theme

Given, that Flutter is a brainchild of Google, Material Design is the defacto design system of Flutter. If you are using MaterialApp
then you are already using the Material Design System. Though there is a CoupertinoApp
that is very rarely used, we will explore the use-case of this one as well later.
And, as for theming, Flutter theme covers the colors and text styles among other things. The main goal of this doc is to explore what is possible with default flutter theme API and how can we achieve custom design system of any project in the best possible way.
Please take note of the below warning about why we won’t cover Material 2:
And, if your project is already using Material 2 then first checkout the migration guide to move to Material 3.
Theme Application Order
Flutter applies themes in the following order:
Styles applied to the specific widget
Themes that override the immediate parent theme
Main theme for the entire app
Let’s go through an example to understand this order better.
Example Code
The following Flutter code snippet demonstrates how themes are applied at different levels:
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
),
home: const MyHomePage(title: 'Theming'),
);
}
}
1. Main theme for the entire app
In the MyApp
class, we define the main theme using ThemeData
. This sets the default color scheme for the entire application. In this example, the seed color is green, meaning all themeable widgets will follow this color scheme unless overridden. Check the example below:
class MyHomePage extends StatelessWidget {
...
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
spacing: 10,
children: [
FilledButton(
onPressed: () {},
child: Text("App Theme - Green"),
),
ParentThemeExampleButton(),
SpecificWidgetThemeExampleButton(),
],
),
);
}
}Open App Theme.png
2. Themes that override the immediate parent theme
Inside MyHomePage
, we override the default theme in a specific section using the Theme
widget:
class ParentThemeExampleButton extends StatelessWidget {
const ParentThemeExampleButton({super.key});
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
),
child: FilledButton(
onPressed: () {},
child: Text("Parent Theme - Red"),
),
);
}
}
In this case, the ParentThemeExampleButton
has its own ThemeData
, which sets the color scheme to red. Any child widget within this widget will inherit this red theme unless explicitly styled otherwise.
3. Styles applied to the specific widget
Lastly, a specific widget can define its own style, completely overriding both the immediate parent theme and the app-wide theme.
class SpecificWidgetThemeExampleButton extends StatelessWidget {
const SpecificWidgetThemeExampleButton({super.key});
@override
Widget build(BuildContext context) {
return FilledButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(Colors.blue),
),
onPressed: () {},
child: Text("Specific Widget Theme - Blue"),
);
}
}
Open Specific Theme.png
Here, the button explicitly sets its background color to blue using ButtonStyle
. This takes priority over both the parent theme (red) and the app-wide theme (green).
Summary
App-wide theme (set in
MaterialApp
) applies by default.Immediate parent theme can override the app-wide theme for a section of the UI.
Specific widget styling takes the highest priority, overriding both the parent and global themes.
By understanding this order, developers can effectively manage the styling of their Flutter applications, ensuring consistency while allowing flexibility when needed.
Subscribe to my newsletter
Read articles from Momshad Dinury directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
