Skip to Content
Mix 2.0 is in development! You can access the Mix 1.0 docs here.
DocsOverviewComparative Overview

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)) .borderRounded(10) .color(surfaceColor) .borderAll(color: surfaceColor) .animate(.easeInOut(100.ms)) .scale(1) .onHovered( .color( accentColor.withValues(alpha: 0.0), ).borderAll(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: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(10), border: Border.all(color: borderColor), ), child: Text( 'Click me', style: TextStyle( fontSize: 16, fontWeight: 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