widget_theme 0.1.4
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 #
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 |
|---|---|
![]() |
![]() |
Features #
- Generate theme classes from widget properties
- Built on top of
ThemeExtension - Automatic
copyWith,lerp, equality (==andhashCode), 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:
final- No initializer (e.g.,
final Color? color;instead offinal Color? color = Colors.red;) - Nullable type (e.g.,
Color?instead ofColor) - Not annotated with
@themeExclude - The field type is either a standard Flutter framework type (like
Color,TextStyle,EdgeInsets, etc.) OR aWidgetStateProperty, 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:
copyWithandlerpimplementationoperator ==andhashCodedebugFillPropertiesfor diagnosticsMyWidgetTheme.of(context)/MyWidgetTheme.maybeOf(context)helpers_mergeWidget(MyWidget)helper to prioritize widget properties over theme properties- Scoped override API:
MyWidgetTheme.overrideWith(...) BuildContextextension:context.myWidgetThemeThemeDataextension: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
Related Packages #
- widget_theme_annotation – Provides annotations used for code generation
License #
MIT License

