widget_theme 0.1.4 copy "widget_theme: ^0.1.4" to clipboard
widget_theme: ^0.1.4 copied to clipboard

Code generation utilities for creating widget-specific theme models from Flutter widget properties, enabling scalable and consistent theming.

widget_theme #

build Pub Version Pub Points codecov GitHub License melos

Code generation for widget-level theming in Flutter.

widget_theme generates strongly-typed theme models directly from your widget properties, enabling consistent, scalable, and centralized theming using Flutter’s ThemeData and ThemeExtension. This package is particularly useful for widget libraries, design systems, and reusable UI components that require centralized theming.

MyWidget MyWidgetTheme
MyWidget MyWidgetTheme

Features #

  • Generate theme classes from widget properties
  • Built on top of ThemeExtension
  • Automatic copyWith, lerp, equality (== and hashCode), and diagnostics (Diagnosticable)
  • Context-based access (MyWidgetTheme.of(context)) and extensions (context.myWidgetTheme)
  • Supports widget-level overrides
  • Fine-grained control over generated code
  • Zero manual boilerplate

Installation #

Add dependencies to your pubspec.yaml:

dependencies:
  widget_theme_annotation: ^latest

dev_dependencies:
  build_runner: ^latest
  widget_theme: ^latest

Or run the following command:

flutter pub add \
  widget_theme_annotation \
  dev:widget_theme \
  dev:build_runner

Usage #

1. Annotate your widget #

@widgetTheme
class MyWidget extends StatelessWidget {
  const MyWidget({
    this.padding,
    this.color,
    super.key,
  });

  final Color? color;
  final EdgeInsets? padding;

  @override
  Widget build(BuildContext context) {
    // Merge widget properties with the theme from the context
    final theme = context.myWidgetTheme._mergeWidget(this);

    return Padding(
      padding: theme.padding ?? const EdgeInsets.all(8),
      child: Text(
        'Hello world!',
        style: TextStyle(color: theme.color),
      ),
    );
  }
}

2. Run code generation #

dart run build_runner build

3. Use generated theme #

MaterialApp(
  theme: ThemeData(
    extensions: const [
      MyWidgetTheme(color: Colors.red),
    ],
  ),
  home: const Scaffold(
    body: Center(child: MyWidget()),
  ),
);

How It Works #

widget_theme reads your widget's fields and generates a corresponding ThemeExtension. When both a widget property and a theme property are provided, the widget property takes precedence.

By default, the generator includes fields that meet all of the following criteria:

  1. final
  2. No initializer (e.g., final Color? color; instead of final Color? color = Colors.red;)
  3. Nullable type (e.g., Color? instead of Color)
  4. Not annotated with @themeExclude
  5. The field type is either a standard Flutter framework type (like Color, TextStyle, EdgeInsets, etc.) OR a WidgetStateProperty, OR the field is annotated with @themeInclude.

Including Custom Types #

If you have a custom type or a type that is not automatically recognized, you can force the generator to include it using the @ThemeInclude annotation. By default, for custom types without a provided lerp function, the generated lerp method will simply snap between the values at t < 0.5 rather than smoothly interpolating.

You can provide a custom lerp function directly to the @ThemeInclude annotation to enable smooth interpolation for your custom types:

class CustomData {
  // ...

  static CustomData? lerp(CustomData? a, CustomData? b, double t) {
    // Custom interpolation logic
    return CustomData();
  }
}

@widgetTheme
class MyWidget extends StatelessWidget {
  const MyWidget({
    this.customData,
    this.otherData,
    super.key,
  });

  // Snaps at t < 0.5
  @themeInclude
  final CustomData? customData;

  // Uses the static lerp function
  @ThemeInclude(lerp: CustomData.lerp)
  final CustomData? otherData;
  // ...
}

Excluding Properties #

If you want to exclude a field that would otherwise be included automatically, use @themeExclude.

@widgetTheme
class MyWidget extends StatelessWidget {
  const MyWidget({
    this.color,
    super.key,
  });

  @themeExclude
  final Color? color; // This will not be part of the generated theme
  // ...
}

Generated APIs #

For a widget MyWidget, the generator produces MyWidgetTheme extends ThemeExtension<MyWidgetTheme> with:

  • copyWith and lerp implementation
  • operator == and hashCode
  • debugFillProperties for diagnostics
  • MyWidgetTheme.of(context) / MyWidgetTheme.maybeOf(context) helpers
  • _mergeWidget(MyWidget) helper to prioritize widget properties over theme properties
  • Scoped override API: MyWidgetTheme.overrideWith(...)
  • BuildContext extension: context.myWidgetTheme
  • ThemeData extension: theme.myWidgetTheme

Configuration #

You can customize the generated code by passing arguments to the @WidgetTheme annotation or configuring build.yaml.

@WidgetTheme(
  name: 'CustomWidgetThemeName',
  staticAccessor: true,          // Generate `of` and `maybeOf`
  mergeWidgetHelper: true,       // Generate `_mergeWidget`
  overrideWithHelper: true,      // Generate `overrideWith`
  diagnosticable: true,          // Mixin `Diagnosticable`
  equals: true,                  // Generate `==` and `hashCode`
  buildContextExtension: true,   // Generate `context.myWidgetTheme`
  themeDataExtension: true,      // Generate `theme.myWidgetTheme`
  docs: true,                    // Generate documentation
)
class MyWidget extends StatelessWidget {
  // ...
}

You can also set these options globally in your project's build.yaml:

targets:
  $default:
    builders:
      widget_theme:
        options:
          staticAccessor: true
          mergeWidgetHelper: true
          overrideWithHelper: true
          diagnosticable: true
          equals: true
          buildContextExtension: true
          themeDataExtension: true
          docs: true

Overriding Theme in Subtree #

You can easily override the theme for a specific widget subtree without having to manually wrap it in a Theme widget:

MyWidgetTheme.overrideWith(
  data: const MyWidgetTheme(color: Colors.blue),
  child: const MyWidget(),
)

When to Use #

Use widget_theme when:

  • You want reusable, themeable widgets
  • You need consistent styling across the app
  • You want to avoid manual boilerplate for ThemeExtension
  • You are building design systems or UI libraries


License #

MIT License

0
likes
150
points
16
downloads

Documentation

API reference

Publisher

verified publisheralbinpk.dev

Weekly Downloads

Code generation utilities for creating widget-specific theme models from Flutter widget properties, enabling scalable and consistent theming.

Repository (GitHub)
View/report issues

Topics

#codegen #theming #flutter #ui #developer-tools

License

MIT (license)

Dependencies

analyzer, build, code_builder, dart_style, source_gen, widget_theme_annotation

More

Packages that depend on widget_theme