easy_theme_extension
A zero-boilerplate generator for Flutter ThemeExtension classes.
Define your theme as an annotated abstract class. The generator creates the full immutable implementation with constructor, fields, copyWith, lerp and more.
Installation
flutter pub add \
easy_theme_extension \
dev:easy_theme_extension_builder \
dev:build_runner
Usage
1. Define your theme contract
part 'my_colors.theme.g.dart';
@easyTheme
abstract class _MyColors {
Color get primary;
Color get secondary;
Color get textBody;
Color get textTitle;
Color get textLabel;
}
2. Run the generator
dart run build_runner build
my_colors.theme.g.dart
part of 'my_colors.dart';
@immutable
class MyColors extends ThemeExtension<MyColors> with Diagnosticable implements _MyColors {
const MyColors({
required this.primary,
required this.secondary,
required this.textBody,
required this.textTitle,
required this.textLabel,
});
@override
final Color primary;
@override
final Color secondary;
@override
final Color textBody;
@override
final Color textTitle;
@override
final Color textLabel;
@override
MyColors copyWith({
Color? primary,
Color? secondary,
Color? textBody,
Color? textTitle,
Color? textLabel,
}) => MyColors(
primary: primary ?? this.primary,
secondary: secondary ?? this.secondary,
textBody: textBody ?? this.textBody,
textTitle: textTitle ?? this.textTitle,
textLabel: textLabel ?? this.textLabel,
);
@override
MyColors lerp(MyColors? other, double t) {
if (other is! MyColors) return this;
return MyColors(
primary: Color.lerp(primary, other.primary, t)!,
secondary: Color.lerp(secondary, other.secondary, t)!,
textBody: Color.lerp(textBody, other.textBody, t)!,
textTitle: Color.lerp(textTitle, other.textTitle, t)!,
textLabel: Color.lerp(textLabel, other.textLabel, t)!,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other.runtimeType != runtimeType) return false;
return other is MyColors &&
other.primary == primary &&
other.secondary == secondary &&
other.textBody == textBody &&
other.textTitle == textTitle &&
other.textLabel == textLabel;
}
@override
int get hashCode => Object.hashAll([
primary,
secondary,
textBody,
textTitle,
textLabel,
]);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty<Color>('primary', primary))
..add(DiagnosticsProperty<Color>('secondary', secondary))
..add(DiagnosticsProperty<Color>('textBody', textBody))
..add(DiagnosticsProperty<Color>('textTitle', textTitle))
..add(DiagnosticsProperty<Color>('textLabel', textLabel));
}
}
extension MyColorsBuildContextExtension on BuildContext {
MyColors get myColors => Theme.of(this).extension<MyColors>()!;
}
3. Use the generated extension
return MaterialApp(
theme: ThemeData(
extensions: const [
MyColors(
primary: Colors.blue,
secondary: Colors.red,
textBody: Colors.black,
textTitle: Colors.black,
textLabel: Colors.grey,
),
],
),
);
// Context extension
context.myColors.secondary;
Annotation Options
@EasyTheme(contextExtensionName: 'color')
abstract class _MyColors { ... }
| Option | Type | Default | Description |
|---|---|---|---|
contextExtension |
bool? |
true |
Generates an extension on BuildContext for accessing the theme. |
contextExtensionName |
String? |
<className> |
Custom name for the generated BuildContext accessor. |
defaultStaticInstance |
bool? |
true |
Generates a static default instance ($default). |
defaultStaticInstanceAsConst |
bool? |
true |
Generates a static default instance ($default) as const. |
diagnosticable |
bool? |
true |
Mixes in Diagnosticable on the generated class. |
equals |
bool? |
true |
Generates == and hashCode implementations. |
Supported Types
See the example app for the full list of supported types and current limitations here.
Contributing
Contributions, issues, and suggestions are welcome. Open an issue or submit a pull request on GitHub
Package page on pub.dev
License
Released under the MIT License