mix_generator 2.1.2 copy "mix_generator: ^2.1.2" to clipboard
mix_generator: ^2.1.2 copied to clipboard

A code generator for Mix, an expressive way to effortlessly build design systems in Flutter.

mix_generator #

Pub Version MIT Licence

Code generator for the Mix styling framework. Processes mix_annotations to generate boilerplate code for Spec, Styler, Mix, and widget-wrapper classes.

Installation #

flutter pub add mix
flutter pub add mix_annotations
flutter pub add --dev mix_generator
flutter pub add --dev build_runner

Or in pubspec.yaml:

dependencies:
  mix: ^2.0.0
  mix_annotations: ^2.0.0

dev_dependencies:
  build_runner: ^2.4.0
  mix_generator: ^2.0.0

What It Generates #

The generator emits several surfaces with deliberately different shapes, chosen to match the type they're paired with:

  • @MixableSpec emits a rich mixin (mixin _$<Name> implements Spec<T>, Diagnosticable) that fully completes the Spec contract on its own. Specs are immutable value types with no shared concrete behavior to inherit, so the generated mixin can be self-contained — user code only writes with _$<Name>.
  • @MixableSpec(target: Widget.new) also emits a full generated Styler class into the same .g.dart part file as the spec mixin. The generated class owns fields, constructors, factories, fluent methods, call(), merge, resolve, diagnostics, and props.
  • @MixableStyler emits a legacy slim mixin (mixin _$<Name>Mixin on Style<S>, Diagnosticable) that fills in per-field plumbing for handwritten styler classes.
  • @Mixable emits a slim mixin (mixin _$<Name>Mixin on Mix<T>[, DefaultValue<T>][, Diagnosticable]) for the same reason — Mix subclasses commonly compose intermediate base classes (e.g., class BoxConstraintsMix extends ConstraintsMix<BoxConstraints>) and the user keeps that inheritance chain.
  • @MixableModifier emits a self-contained modifier mixin (mixin _$<Name> implements WidgetModifier<T>, Diagnosticable) plus its corresponding <Name>Mix class.

If the asymmetry feels surprising, the rule is: rich/full shape for pure-data specs and their generated stylers; slim shape for types whose extends chain carries shared state or behavior.

The @MixWidget annotation generates a full StatelessWidget class (not a mixin) that wraps a top-level Style<S> factory — see the @MixWidget section below.

From @MixableSpec — Spec mixin #

Generates a self-contained _$<Name> mixin (implements Spec<Name>, Diagnosticable) for immutable Spec classes. User code declares the spec with a single with _$<Name> — Equatable-style equality and Diagnosticable's concrete surface are inlined by the generator:

  • Type get type — the concrete spec Type
  • copyWith() — create modified copies
  • lerp() — smooth interpolation between specs
  • props by default, ==, hashCode — deep equality via propsEquals / propsHash helpers
  • toString({DiagnosticLevel minLevel}), toStringShort(), toDiagnosticsNode() — Flutter inspector integration
  • debugFillProperties() — diagnostic output

No with Equatable and no with Diagnosticable on the user class — the mixin carries both.

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

part 'box_spec.g.dart';

@MixableSpec()
@immutable
final class BoxSpec with _$BoxSpec {
  @override
  final Color? color;
  @override
  final double? width;
  @override
  final double? height;
  @override
  final AlignmentGeometry? alignment;

  const BoxSpec({this.color, this.width, this.height, this.alignment});
}

When @MixableSpec generates a Styler, the host library must import the Mix runtime and any Flutter or local types referenced by the generated styler code, because the generated output is a Dart part and cannot declare its own imports.

Legacy declaration shape. Pre-2.0 generators emitted a slim _$<Name>SpecMethods mixin and required class <Name> extends Spec<<Name>> with Diagnosticable, _$<Name>SpecMethods. The generator now emits a @Deprecated typedef _$<Name>SpecMethods = _$<Name>; alongside every spec mixin so those legacy declarations keep compiling — you'll just see a deprecation warning. Migrate to class <Name> with _$<Name> to silence it; the alias is scheduled for removal in mix_generator 3.0. One observable change for legacy callers: toString() now routes through Diagnosticable.toDiagnosticsNode, replacing Equatable's Name(field: …) output with Flutter's diagnostic-node format.

From @MixableStyler — legacy Styler mixin #

Generates a _$<Name>Mixin for handwritten Styler classes with:

  • Setter methods for each $-prefixed field
  • merge() — combine styles
  • resolve() — resolve to the corresponding Spec
  • debugFillProperties() — diagnostics support
  • props — equality comparison
part 'box_style.g.dart';

@MixableStyler()
class BoxStyler extends Style<BoxSpec>
    with Diagnosticable, _$BoxStylerMixin {
  final Prop<Color>? $color;
  final Prop<double>? $width;
  final Prop<double>? $height;
  final Prop<AlignmentGeometry>? $alignment;

  // ...
}

From @Mixable — Mix mixin #

Generates a _$<Name>Mixin for compound property types with:

  • merge() — combine mix instances
  • resolve() — resolve to the target type
  • props — equality comparison
  • debugFillProperties() — diagnostics support
part 'box_constraints_mix.g.dart';

@Mixable()
final class BoxConstraintsMix extends ConstraintsMix<BoxConstraints>
    with DefaultValue<BoxConstraints>, Diagnosticable, _$BoxConstraintsMixMixin {
  final Prop<double>? $minWidth;
  final Prop<double>? $maxWidth;
  final Prop<double>? $minHeight;
  final Prop<double>? $maxHeight;

  // ...
}

From @MixableModifier — modifier mixin and ModifierMix #

Generates a self-contained _$<Name> mixin for an immutable WidgetModifier, plus a <Name>Mix class that can be used in styles. The mixin provides type, copyWith, optional lerp, value equality, diagnostics, and the build contract. The Mix class provides constructors, resolve, merge, diagnostics, and props.

part 'opacity_modifier.g.dart';

@MixableModifier()
final class OpacityModifier with _$OpacityModifier {
  @override
  final double opacity;

  const OpacityModifier([double? opacity]) : opacity = opacity ?? 1.0;

  @override
  Widget build(Widget child) => Opacity(opacity: opacity, child: child);
}

Pass lerp: false when the modifier implements custom interpolation. A field may use @MixableField(setterType: SomeMix) when its style-facing value should be a Mix type; the setter type must resolve to the field's runtime value type.

From @MixWidget — widget wrapper #

Generates a StatelessWidget wrapper around a top-level Style<S> variable or function. The wrapper's constructor mirrors the factory's parameters (for function-backed styles) plus the styler's call() parameters; build() invokes the factory and forwards the parameters through to the resulting widget.

part 'card.g.dart';

@mixWidget
final cardStyle = BoxStyler()
    .color(Colors.white)
    .borderRounded(8)
    .paddingAll(16);

// Generates:
//   class Card extends StatelessWidget {
//     const Card({super.key, this.child});
//     final Widget? child;
//     @override Widget build(BuildContext context) =>
//         cardStyle.call(key: this.key, child: this.child);
//   }

The widget name is derived from a lower-camel-case *Style identifier (cardStyleCard); pass @MixWidget(name: 'X') to override. Function-backed factories thread their args before the styler call(): @MixWidget Style<S> badge({Color? color}) => ... generates a Badge({this.color, this.child}) constructor.

When a function-backed factory declares a named, non-nullable enum parameter named variant, the generator also emits one named constructor per accessible enum value:

@MixWidget()
ButtonStyler buttonStyle({ButtonVariant variant = .solid}) => ...;

// Generates both the backwards-compatible `Button(variant: ...)` and:
//   const Button.solid({super.key, ...}) : variant = ButtonVariant.solid;
//   const Button.ghost({super.key, ...}) : variant = ButtonVariant.ghost;

Each named constructor omits the variant argument and preserves every other factory and styler call() parameter. Enum-value documentation and deprecation metadata are carried onto the matching constructor. Nullable, positional, and non-enum variant parameters retain the single-constructor behavior. Variant constructors are also skipped when an enum value conflicts with a generated widget type parameter.

See mix_annotations for the full annotation contract (parameter rules, Key? key forwarding, naming/visibility constraints).

Field-level control with @MixableField #

// Skip setter generation.
@MixableField(ignoreSetter: true)
final Prop<Matrix4>? $transform;

// Override the setter parameter type.
@MixableField(setterType: List<Shadow>)
final Prop<List<Shadow>>? $shadows;

Running the Generator #

dart run build_runner build --delete-conflicting-outputs

Or within the Mix monorepo:

melos run gen:build

Generated files use the .g.dart extension and should be committed to version control.

Debugging #

VS Code #

  1. Set breakpoints in the generator source files under lib/src/.
  2. Select the "Debug build_runner" launch configuration.
  3. Press F5.

Manual #

dart --enable-vm-service=8888 --pause-isolates-on-start run build_runner build --verbose

Then connect your debugger to localhost:8888.

Learn More #

3
likes
140
points
564
downloads

Documentation

API reference

Publisher

verified publisherleoafarias.com

Weekly Downloads

A code generator for Mix, an expressive way to effortlessly build design systems in Flutter.

Homepage
Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

analyzer, build, dart_style, mix_annotations, source_gen

More

Packages that depend on mix_generator