Flutter helps develop high-quality applications for various platforms, including iOS, Android, macOS, Windows, Linux, and the Web. One key feature of the Flutter framework is its extensive system of theming. Instead of configuring each widget separately, you can set default colors, fonts, and sizes in the ThemeData
class, which various widgets will use.
But usually, values provided by ThemeData
aren’t sufficient, so it means that classes like AppColors
or AppMargins
appear. Mentioned classes are good candidates for ThemeExtensions
.
What is ThemeExtension?
ThemeExtension is an interface that expands the Flutter theme system. When you extend ThemeExtension
class, it ensures you implement copyWith
and lerp
functions. With the help of InheritedWidget
(handled by Theme
), those two functions provide smooth transitions when changing themes (for example, from light to dark).
How to create ThemeExtension?
As an example of ThemeExtension, we will create a class that will keep brand data (only color in this example). This class has to extend ThemeExtension abstract class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import 'package:flutter/material.dart';
class Brand extends ThemeExtension<Brand> {
const Brand({
required this.color,
});
final Color? color;
@override
Brand copyWith({Color? color}) {
return Brand(
color: color ?? this.color,
);
}
@override
Brand lerp(
ThemeExtension<Brand>? other,
double t,
) {
if (other is! Brand) return this;
return Brand(
color: Color.lerp(color, other.color, t),
);
}
}
How to register ThemeExtension?
Next, you need to add extensions to your themes. If you use ready-to-use themes, you can use the copyWith
method (like in the example) or set extensions when the ThemeData object is created. Since extensions
field expects an iterable, you can add as many extensions as you want.
1
2
3
4
5
6
7
8
9
10
11
12
13
MaterialApp(
theme: ThemeData.light().copyWith(
extensions: [
const Brand(color: Colors.amber),
],
),
darkTheme: ThemeData.dark().copyWith(
extensions: [
const Brand(color: Colors.orange),
],
),
home: const HomePage(),
);
How to use ThemeExtension?
Now, when you have your ThemeExtension registered, you can start using it in code. You can get it through the extension function.
1
2
3
4
5
@override
Widget build(BuildContext context) {
final brand = Theme.of(context).extension<Brand>();
return Container(color: brand?.color);
}
Summary
At first, this may seem convoluted, and you may think using static fields in the abstract class is better. But going with ThemeExtensions has two advantages:
- you are coupling your theme settings with framework ones
- thanks to the
lerp
function, theme changes will also animate custom colors and sizes.
Complexity: Basic
Used Flutter version: 3.7.7