theme_extensions_gen 0.1.0 copy "theme_extensions_gen: ^0.1.0" to clipboard
theme_extensions_gen: ^0.1.0 copied to clipboard

Theme Extensions Generator

theme_extensions_gen #

Code generation for strongly-typed, modular Flutter ThemeExtensions.
Define your visual theme structure with annotations β€” generate boilerplate-free implementations, context extensions, and theme lists.


πŸ”₯ Features #

  • βœ… Declarative @ThemeExtensionTemplate() interface for theme structure
  • βœ… @ThemeExtensionImpl() for centralized theme value lists
  • βœ… Auto-generated:
    • copyWith, lerp, equality, and mixins
    • BuildContext extensions
    • Merged ThemeExtension lists for ThemeData
    • debugFillProperties() if Diagnosticable is used
  • βœ… Group-based output (light, dark, etc.)
  • βœ… Fully configurable via build.yaml

πŸ“Έ Before / After #

Hand-written ThemeExtension With @ThemeExtensionTemplate
Manual Generated

🧩 Annotations #

@ThemeExtensionTemplate() #

Marks an abstract interface as a ThemeExtension template. The generator creates:

  • A concrete class with a factory constructor
  • copyWith, lerp, ==, hashCode
  • debugFillProperties() if Diagnosticable is used in the mixins
  • BuildContext extensions with accessors like context.brandedButtonTheme

Example:

@ThemeExtensionTemplate()
abstract interface class BrandedButtonTheme
    extends ThemeExtension<BrandedButtonTheme>
    with _$BrandedButtonThemeMixin, Diagnosticable {
  const factory BrandedButtonTheme({
    required Decoration decoration,
    required TextStyle textStyle,
    required EdgeInsets padding,
  }) = _$BrandedButtonTheme;
}

@ThemeExtensionImpl({ String? group }) #

Marks a List<ThemeExtension> as a named implementation group to be collected into a generated file.

  • Lists without group are merged into a default file.
  • Lists with group are merged into a separate file (e.g. for dark, light, etc.).
@ThemeExtensionImpl()
List<ThemeExtension> get someFeatureThemeExtensions => const [
  BrandedCardTheme(/* ... */),
  BrandedButtonTheme(/* ... */),
];

@ThemeExtensionImpl(group: 'dark')
List<ThemeExtension> get someFeatureThemeExtensions => const [
  BrandedCardTheme(/* ... */),
  BrandedButtonTheme(/* ... */),
];

βš™οΈ Configuration (build.yaml) #

targets:
  $default:
    builders:
      themeExtensionsImplCombiner:
        options:
          default_output:
            path: "lib/generated/theme_extensions/theme_extensions.dart"
            list_name: "themeExtensions"
          groups:
            dark:
              path: "lib/generated/theme_extensions/theme_extensions_dark.dart"
              list_name: "themeExtensionsDark"

      contextExtensionsGenerator:
        options:
          output_path: "lib/generated/theme_extensions/context_extensions.dart"

To disable contextExtensionsGenerator, use:

builders:
  contextExtensionsGenerator:
    enabled: false

πŸš€ Example Usage #

final theme = Theme.of(context).extension<BrandedCardTheme>();
// or
final theme = context.brandedCardTheme;

Works with all generated extensions automatically.


πŸ“ Example Project #

The /example directory demonstrates:

  • Shared ThemeExtension templates
  • Light and dark theme implementations
  • Runtime theme switching
  • Organized resource files (app_colors.dart, app_text_styles.dart, etc.)

πŸ™ Acknowledgements #

Special thanks to
Vladyslav Ulianytskyi
for inspiration, technical discussions, and early feedback.


Happy theming! 🎨