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: 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 Mix | Without Mix | |
|---|---|---|
| Widget type | StatelessWidget | StatefulWidget |
| Hover state | onHovered variant | Manual MouseRegion + setState |
| Press state | onPressed variant | Manual GestureDetector + setState |
| Animations | .animate() on the style | Nested AnimatedScale + AnimatedContainer |
| Style location | Separated from widget tree | Mixed into build() method |
Next Steps
- Getting Started — install Mix and build your first widget
- Dynamic Styling — deep dive into variants like
onHoveredandonPressed
Last updated on