Mix
Overview

Comparison

Compare Mix and plain Flutter approaches for styling and interaction states.

Mix vs. Plain Flutter

This comparison shows the same interactive button built two ways: with Mix and without. The widget has the following requirements:

  • Custom text and box decoration styles
  • Hover state with animated color transitions
  • Press state with scale animation

Both approaches produce the same result:

Resolving preview metadata...

With Mix

Style definitions are declarative and separated from the widget tree. Hover, press, and animations are handled inline with the style:

class CustomMixWidget extends StatelessWidget {
  const CustomMixWidget({super.key});
  Color get accentColor => Colors.deepPurpleAccent;
  Color get surfaceColor => Colors.deepPurpleAccent.withValues(alpha: 0.1);

  TextStyler get customTextStyle =>
      TextStyler().fontSize(16).fontWeight(.w500).color(accentColor);

  BoxStyler get customBoxStyle => BoxStyler()
      .padding(.symmetric(horizontal: 12, vertical: 8))
      .borderRadius(.circular(10))
      .color(surfaceColor)
      .border(.color(surfaceColor))
      .animate(.easeInOut(100.ms))
      .scale(1)
      .onHovered(
        .color(accentColor.withValues(alpha: 0.0))
        .border(.color(accentColor)),
      )
      .onPressed(.scale(0.96));

  @override
  Widget build(BuildContext context) {
    return Pressable(
      onPress: () {},
      child: Box(
        style: customBoxStyle,
        child: StyledText('Click me', style: customTextStyle),
      ),
    );
  }
}

Without Mix

The same widget requires a StatefulWidget to track hover and press state, manual MouseRegion and GestureDetector handling, and nested animation widgets:

class CustomFlutterWidget extends StatefulWidget {
  const CustomFlutterWidget({super.key});

  @override
  State<CustomFlutterWidget> createState() => _CustomFlutterWidgetState();
}

class _CustomFlutterWidgetState extends State<CustomFlutterWidget> {
  bool _isHovered = false;
  bool _isPressed = false;

  Color get accentColor => Colors.deepPurpleAccent;
  Color get surfaceColor => Colors.deepPurpleAccent.withValues(alpha: 0.1);

  static const _duration = Duration(milliseconds: 100);
  static const _curve = Curves.easeInOut;

  @override
  Widget build(BuildContext context) {
    final bgColor = _isHovered
        ? accentColor.withValues(alpha: 0.0)
        : surfaceColor;
    final borderColor = _isHovered ? accentColor : surfaceColor;
    final scale = _isPressed ? 0.96 : 1.0;

    return MouseRegion(
      onEnter: (_) => setState(() => _isHovered = true),
      onExit: (_) => setState(() => _isHovered = false),
      child: GestureDetector(
        onTapDown: (_) => setState(() => _isPressed = true),
        onTapUp: (_) => setState(() => _isPressed = false),
        onTapCancel: () => setState(() => _isPressed = false),
        onTap: () {},
        child: AnimatedScale(
          scale: scale,
          duration: _duration,
          curve: _curve,
          child: AnimatedContainer(
            duration: _duration,
            curve: _curve,
            padding: .symmetric(horizontal: 12, vertical: 8),
            decoration: BoxDecoration(
              color: bgColor,
              borderRadius: .circular(10),
              border: Border.all(color: borderColor),
            ),
            child: Text(
              'Click me',
              style: TextStyle(
                fontSize: 16,
                fontWeight: .w500,
                color: accentColor,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Key Differences

With MixWithout Mix
Widget typeStatelessWidgetStatefulWidget
Hover stateonHovered variantManual MouseRegion + setState
Press stateonPressed variantManual GestureDetector + setState
Animations.animate() on the styleNested AnimatedScale + AnimatedContainer
Style locationSeparated from widget treeMixed into build() method

Next Steps

View page source on GitHub