Use case
@MixWidget currently derives named constructors only when a selected factory parameter is a named, non-nullable enum parameter literally named variant. It also always preserves the unnamed constructor and exposes variant as a public generated field.
For example:
@MixWidget(
name: 'FortalButton',
target: RemixButton.new,
factoryParameters: .only({'variant', 'size'}),
)
ButtonStyler fortalButtonStyler({
FortalButtonVariant variant = .solid,
FortalButtonSize size = .size2,
bool highContrast = false,
});
This produces both FortalButton(variant: ...) and constructors such as FortalButton.solid(...).
That API does not fit design-system recipes where:
- visual variants are Mix
NamedVariants;
- variants should be selected only through named widget constructors;
- ordinary recipe controls such as
size should remain generated parameters;
- the low-level target widget should receive only its own parameters and the computed style.
The desired public API is:
FortalButton.solid(size: .size2, label: 'Save');
FortalButton.soft(size: .size3, label: 'Save');
There should be no unnamed FortalButton(...), public variant: argument, or public variant field.
This is needed by Remix PR #84, which uses the direct-target support introduced in Mix PR #997.
Proposal
Support an opt-in convention that discovers an enum-backed NamedVariant domain independently from factory parameters.
Proposed usage sketch:
enum FortalButtonVariant with EnumVariant {
solid,
soft,
surface,
outline,
ghost,
}
@MixWidget(target: RemixButton.new)
ButtonStyler fortalButtonStyler({
FortalButtonSize size = .size2,
}) {
return _fortalButtonBaseStyle(size).variantsFromEnum(
FortalButtonVariant.values,
(variant) => switch (variant) {
.solid => _fortalButtonSolidStyle(size),
.soft => _fortalButtonSoftStyle(size),
.surface => _fortalButtonSurfaceStyle(size),
.outline => _fortalButtonOutlineStyle(size),
.ghost => _fortalButtonGhostStyle(size),
},
);
}
variantsFromEnum is an API sketch for registering every enum value as a VariantStyle; an equivalent exhaustive API would also satisfy the use case.
Discovery would be deterministic:
- Infer
FortalButton from the annotated fortalButtonStyler declaration by stripping the conventional Styler suffix and converting the remaining identifier to PascalCase. No name: override is required.
- Look in the same library for an enum named
FortalButtonVariant.
- Require that enum to implement
EnumVariant.
- Generate one named constructor per accessible enum value.
- Generate no unnamed constructor for that widget.
- Store the selected enum value privately and apply it to the returned styler before passing the style to
target.
Conceptual generated output:
class FortalButton extends StatelessWidget {
const FortalButton.solid({
super.key,
this.size = .size2,
required this.label,
}) : _variant = FortalButtonVariant.solid;
const FortalButton.soft({
super.key,
this.size = .size2,
required this.label,
}) : _variant = FortalButtonVariant.soft;
final FortalButtonVariant _variant;
final FortalButtonSize size;
final String label;
@override
Widget build(BuildContext context) {
return RemixButton(
style: fortalButtonStyler(size: size).applyVariant(_variant),
label: label,
);
}
}
The constructor-discovery scope should be deliberately narrow:
- enum values implementing
EnumVariant: generate constructors;
- arbitrary
NamedVariant('name') instances: remain manually applicable and do not generate constructors;
ContextVariant, widget-state variants, and builder variants: never generate constructors.
The existing factory-parameter behavior should remain unchanged when no matching EnumVariant enum is present.
Missing or invalid mappings should fail clearly rather than silently applying the base style. An exhaustive enum registration helper plus a strict singular applyVariant operation would provide compile-time switch coverage and runtime validation.
Acceptance criteria
@MixWidget(target: RemixButton.new) on fortalButtonStyler deterministically generates FortalButton; callers do not have to provide name:.
- A target-backed function with only a
size factory parameter can generate named-only constructors from a same-library <WidgetName>Variant with EnumVariant enum.
- The generated wrapper has no unnamed constructor, public
variant: parameter, or public variant field.
- Every named constructor forwards the other factory and target parameters, including defaults and generic types.
- The selected enum value is applied as a Mix
NamedVariant before the style reaches the target widget.
- Arbitrary named variants and all context/state variants do not generate constructors.
- Missing variant-style mappings produce a clear failure.
- Existing
@MixWidget generation remains unchanged when the convention does not apply.
- Generator unit/integration tests and annotation/generator documentation cover the new behavior.
Current implementation references:
Use case
@MixWidgetcurrently derives named constructors only when a selected factory parameter is a named, non-nullable enum parameter literally namedvariant. It also always preserves the unnamed constructor and exposesvariantas a public generated field.For example:
This produces both
FortalButton(variant: ...)and constructors such asFortalButton.solid(...).That API does not fit design-system recipes where:
NamedVariants;sizeshould remain generated parameters;The desired public API is:
There should be no unnamed
FortalButton(...), publicvariant:argument, or publicvariantfield.This is needed by Remix PR #84, which uses the direct-target support introduced in Mix PR #997.
Proposal
Support an opt-in convention that discovers an enum-backed
NamedVariantdomain independently from factory parameters.Proposed usage sketch:
variantsFromEnumis an API sketch for registering every enum value as aVariantStyle; an equivalent exhaustive API would also satisfy the use case.Discovery would be deterministic:
FortalButtonfrom the annotatedfortalButtonStylerdeclaration by stripping the conventionalStylersuffix and converting the remaining identifier to PascalCase. Noname:override is required.FortalButtonVariant.EnumVariant.target.Conceptual generated output:
The constructor-discovery scope should be deliberately narrow:
EnumVariant: generate constructors;NamedVariant('name')instances: remain manually applicable and do not generate constructors;ContextVariant, widget-state variants, and builder variants: never generate constructors.The existing factory-parameter behavior should remain unchanged when no matching
EnumVariantenum is present.Missing or invalid mappings should fail clearly rather than silently applying the base style. An exhaustive enum registration helper plus a strict singular
applyVariantoperation would provide compile-time switch coverage and runtime validation.Acceptance criteria
@MixWidget(target: RemixButton.new)onfortalButtonStylerdeterministically generatesFortalButton; callers do not have to providename:.sizefactory parameter can generate named-only constructors from a same-library<WidgetName>Variant with EnumVariantenum.variant:parameter, or public variant field.NamedVariantbefore the style reaches the target widget.@MixWidgetgeneration remains unchanged when the convention does not apply.Current implementation references:
MixWidgetGeneratorvariant discoveryMixWidgetBuilderconstructor emission