Theming in Flutter with Material 3

Krishnaji yedlapalli
8 min readNov 20, 2023

--

What is a Theme?

A theme is a collection of styles that’s applied to an entire app, Screen, or view hierarchy. Customizing your theme allows it to align with your unique personal design or your existing enterprise design.

Material 3

In Material 3 few new features were added, refer below link for more info

https://flutter.github.io/samples/web/material_3_demo/

The brand new feature in Material 3, allows you to set the color theme of your entire app from a single seed color. Set the color to the scheme seed parameter in your theme data constructor and, from that one entry, Flutter generates various color schemes for every widget in your app. This scheme works in both light and dark modes!

The default seed color is set to Purple and the font family is Roboto in Material 3

Please refer to the link below to view the final output of this blog on Theming https://flutter-end-to-end.web.app/#/home/localization

Let’s dive into practical ….!!!!

Getting started :

If you don’t have Flutter setup in your machine then follow this link to setup

https://docs.flutter.dev/get-started/install.

Create a new Flutter project using IDE (Android Studio/VS Code) or Command.

By default, every flutter project is set to Material theme with the seed color as Purple, and the font family as Roboto.

Creating Custom Theme :

In project open main.dart file from the lib folder, where we can find the MaterialApp widget, I’m continuing with the Material theme only if you want you can use Cupertino as well.

In the Material App widget we have two themes to configure one is for dark mode and another is for light mode

These two properties accept the ThemeData object and they contains the various properties to configure.

 @override
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
title: 'Flutter End to End',
theme: ...,
darkTheme: ...,
);

Let’s create a new dart file theme.dart in lib folder, in that file create a class CustomTheme and create two static methods lightThemeData and darkThemeData as shown below

class CustomTheme {

static ThemeData lightThemeData(BuildContext context) {
return ThemeData(
....
}

static ThemeData darkThemeData() {
return ThemeData(
...
);
}
}

Call these two methods in the Material App widget as shown below

MaterialApp.router(
debugShowCheckedModeBanner: false,
title: 'Flutter End to End',
theme: CustomTheme.lightThemeData(context),
darkTheme: CustomTheme.darkThemeData(),
);

The approach mentioned above will help streamline our code, and if you have any alternative suggestions, please feel free to share them.

To add Material 3 to the application, set the useMaterial3 property to true in ThemeData.

class CustomTheme {

static ThemeData lightThemeData(BuildContext context) {
return ThemeData(
useMaterial3: true
}

static ThemeData darkThemeData() {
return ThemeData(
useMaterial3: true
);
}
}

Let’s dive into the light theme and Dark theme….!!!

Light Theme Mode :

Here we will discuss about few properties that are primarily and commonly used in all applications.

ThemeData(
colorSchemeSeed : ....,
fontFamily: ...,
colorScheme : ...,
appBarTheme : ...,
hoverColor : ...,
cardTheme : ...,
textTheme : ...,
elevatedButtonTheme : ...,
useMaterial3: true
);

1. Color Scheme Seed :

Color scheme seed property accepts a single color, and based on this a set of tonal palettes are constructed, These tonal palettes are based on the Material 3 Color system.

Refer to the link for more info https://m3.material.io/styles/color/the-color-system/color-roles

As discussed above the default seed color is set to Purple and the font family is Roboto in Material 3

I’m applying a green color to the current application

ThemeData(
colorSchemeSeed: Colors.green,
....

if you want to customize the colors for primary, secondary, background, etc. you can use the color scheme property, which will covered below.

2. Color Scheme :

Color scheme property plays a major role, we can customize the color properties of components, not like color scheme seed.

Here we can apply colors to the application in two ways, by setting the Color scheme constructor directly and another one named constructor ColorScheme.fromSeed

ColorScheme class instance accepts a couple of mandatory fields but using fromSeed name constructor we can use generated tonal palettes and also we can pass customized colors to define secondary and tertiary colors to create a completely unique color palette. Refer to the below link for customize the colors

Must Visit link: https://m3.material.io/theme-builder#/custom

It exports the Dart files that you can use immediately in your Flutter app.

return ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.green,
background: Colors.white,
error: Colors.red,
onTertiary: Colors.orange
)

The main accent color groups in the scheme are

  1. Primary
  2. Secondary
  3. Tertiary
  4. Neutral

Primary: Primary colors are used for key components across the UI, such as the FAB, prominent buttons, and active states.

Secondary: Secondary colors are used for less prominent components in the UI, such as filter chips

Tertiary: Tertiary colors are used for contrasting accents that can be used to balance primary and secondary colors or bring heightened attention to an element, such as an input field

Neutral: Used for background and surfaces

3. App Bar Theme :

We can customize the app bar theme all over the application using app bar theme property as shown below

appBarTheme: const AppBarTheme(color: Colors.green, shadowColor: Colors.red,
elevation: 5, foregroundColor: Colors.white),

4. Font Family :

In three ways we can add a font family to the application

  1. Import the font files from the Google Fonts portal or from any font family website. Create a folder, name it as fonts at the root of the project and import it in the pubspec yaml file as shown below
fonts:
- family: Schyler
fonts:
- asset: fonts/Schyler-Regular.ttf
- asset: fonts/Schyler-Italic.ttf
style: italic

Declare Schyler in the theme, now font family will applied to the whole project. Refer below link for a detailed explanation https://docs.flutter.dev/cookbook/design/fonts

 return ThemeData(
fontFamily: 'Schyler',

2. Create your own package and import it into multiple projects, refer to this link https://docs.flutter.dev/cookbook/design/package-fonts

3. Using the google_fonts package, you can use almost 1000 open-source font families.

google_fonts: ^6.1.0
ThemeData(
fontFamily: GoogleFonts.openSans().fontFamily,

5. Text Theme :

By default Flutter Material Design provides various typographical styles, we can directly use them in our code as shown below

Text('Title', style: Theme.of(context).textTheme.titleMedium)

You can refer to this link for more details https://api.flutter.dev/flutter/material/TextTheme-class.html

We can customize the Text theme by applying different colors, font families, styles, font sizes, etc.

ThemeData(
textTheme: TextTheme(
displayLarge: const TextStyle(
fontSize: 72,
fontWeight: FontWeight.bold,
),
titleLarge: GoogleFonts.nunito(
fontWeight: FontWeight.bold,
color: Colors.white,
),
titleMedium: GoogleFonts.kanit(
fontWeight: FontWeight.bold,
color: Colors.black,
),
titleSmall: GoogleFonts.kanit(
fontWeight: FontWeight.bold,
color: Colors.black,
),
bodyMedium: GoogleFonts.dmSans(
color: Colors.black,
),
),
)

See in the above code, I modified a few property styles of the text theme, for text I added different font families, I’m using the Google fonts package to achieve this you can also import custom font families as explained in the font family section.

Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dumm", 
style: Theme.of(context).textTheme.bodyMedium)

Note: Modifying the Text theme properties will affect the predefined widgets that are using these text themes, for example, in the App bar we have a title property, it will use the titleLarge text theme to show, but in the text theme if we modified it means affect will be there, as shown below, be conscious with that.

App Bar before modification :

App bar After Modification :

titleLarge: GoogleFonts.nunito(
fontWeight: FontWeight.bold,
fontSize: 40
),

6. Elevated Button Theme:

Flutter offers various button types, with the Elevated button being the most commonly chosen option, making it my selection.

Flutter supports multi-platform, We should remember that the way things look and behave should be different depending on the platform. For instance, on a web/desktop application, when you move your mouse over a button, some hover affects will be there, but this isn’t needed on a phone. Let’s talk about how we can manage these Platform-specific differences.

ThemeData(
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
elevation: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return 5.0;
} else {
return 3.0;
}
}),
backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return Colors.white;
} else {
return Colors.green;
}
}),
shadowColor: MaterialStateProperty.all<Color>(Colors.lightGreenAccent),
textStyle: MaterialStateProperty.all(GoogleFonts.prompt(fontWeight: FontWeight.w600)),
foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return Colors.green;
} else {
return Colors.white;
}
}))),
)

Using Material state property we can listen to the state of a button. In code, we can find two methods resolveWith and all in the MaterialStateProperty class

all : It indicates for all states, the same type of color will applied, for example, shadowColor is Colors.lightGreenAccent, it is the same for all states if it is hovered or disabled…

shadowColor: MaterialStateProperty.all<Color>(Colors.lightGreenAccent),

resolveWith : Based on the Material state we can implement our conditions, for example, in above code for backgroundColor there is a condition if the states containing hovered then it will set green color, and in the normal case it will set white color.

backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered)) {
return Colors.white;
} else {
return Colors.green;
}
}),

7. Hover color:

The hover color property accepts color and this will be used in Web/Desktop platforms, When any click event widget is hovered, this color will be applied on top of the widget as shown in the below gif.

hoverColor: Colors.green.shade200,

Dark Theme Mode :

In dark theme mode, everything will reset to the default Purple color and Roboto font family, in the above light theme mode we applied different font families to the text theme, all those won’t apply in the dark theme, we need to apply them again

Managing Theme Modes :

In the above gif, you can find a button on the top right-side corner, on tap of that, the theme mode changes to light to dark and dark to light. I did this using the Provider state management tool, by changing the themeMode property in the Material App

MaterialApp.router(
debugShowCheckedModeBanner: false,
title: 'Flutter End to End',
theme: CustomTheme.lightThemeData(context),
darkTheme: CustomTheme.darkThemeData(),
themeMode: context.watch<CommonProvider>().themeModeType,
}

In Theme, we have a few more concepts to cover like Theme extensions, Overriding the Theme, etc. Will cover these concepts in different blog

You can find the code in the below git repo

Git Reference: https://github.com/krishnaji-yedlapalli/flutter_end_to_end

Web Page Reference: https://flutter-end-to-end.web.app/

I hope this article was helpful to you in creating a custom theme

Thanks for Reading 😊 !!!!!

Reference links for Material 2 and Material 3:

--

--