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