Overview of Flutter Theme

Momshad DinuryMomshad Dinury
3 min read

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:

As of the Flutter 3.16 release, Material 3 is enabled by default. For now, you can opt out of Material 3 by setting the useMaterial3 property to false. But be aware that the useMaterial3 property and support for Material 2 will eventually be deprecated according to Flutter's deprecation policy. Read More...

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:

  1. Styles applied to the specific widget

  2. Themes that override the immediate parent theme

  3. 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.

0
Subscribe to my newsletter

Read articles from Momshad Dinury directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Momshad Dinury
Momshad Dinury