Comparison
A Comparative Look at Mix
Mix simplifies Flutter styling by reducing boilerplate and separating style from logic. This comparison shows how Mix handles complex widget styles and interactions versus plain Flutter.
Code Comparison: With Mix vs. Without Mix
This comparison styles a custom widget with the following requirements:
- Flexible style overriding — Override specific
TextStyleandBoxDecorationproperties independently - Interaction-based styling — Apply different styles on hover, press, and dark mode without manual state management
With Mix
class CustomMixWidget extends StatelessWidget {
const CustomMixWidget({super.key});
TextStyler get customTextStyle {
return TextStyler()
.fontSize(16)
.fontWeight(.w600)
.color(Colors.white)
.animate(.easeInOut(100.ms))
.onDark(.color(Colors.black))
.onHovered(
.animate(.easeInOut(100.ms))
.onLight(.color(Colors.white)),
);
}
BoxStyler get customBoxStyle {
return BoxStyler()
.height(120)
.width(120)
.paddingAll(20)
.elevation(.nine)
.alignment(.center)
.borderRounded(10)
.color(Colors.blue)
.scale(1.0)
.animate(.easeInOut(100.ms))
.onDark(.color(Colors.cyan))
.onHovered(
.alignment(.topLeft)
.elevation(.two)
.paddingAll(10)
.scale(1.5)
.animate(.easeInOut(100.ms))
.onLight(.color(Colors.blue.shade300)),
);
}
@override
Widget build(BuildContext context) {
return Pressable(
onPress: () {},
child: Box(
style: customBoxStyle,
child: StyledText('Custom Widget', style: customTextStyle),
),
);
}
}Mix separates style definitions from interaction logic. The code is shorter, has no manual state tracking, and style changes are declarative.
Without Mix
class CustomWidget extends StatefulWidget {
const CustomWidget({
Key? key,
}) : super(key: key);
@override
_CustomWidgetState createState() => _CustomWidgetState();
}
class _CustomWidgetState extends State<CustomWidget> {
bool _isHover = false;
final _curve = Curves.linear;
final _duration = const Duration(milliseconds: 100);
@override
Widget build(BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final backgroundColor = isDark ? Colors.cyan : Colors.blue;
final textColor = isDark ? Colors.black : Colors.white;
final borderRadius = BorderRadius.circular(10);
final onHoverTextColor =
isDark
? Color.lerp(textColor, Colors.white, 0.2)!
: Color.lerp(textColor, Colors.black, 0.2)!;
final onHoverBgColor =
isDark
? Color.lerp(backgroundColor, Colors.white, 0.2)!
: Color.lerp(backgroundColor, Colors.black, 0.3)!;
return MouseRegion(
onEnter: (event) {
setState(() => _isHover = true);
},
onExit: (event) {
setState(() => _isHover = false);
},
child: Material(
elevation: _isHover ? 2 : 9,
borderRadius: borderRadius,
child: AnimatedScale(
scale: _isHover ? 1.5 : 1,
curve: _curve,
duration: _duration,
child: AnimatedContainer(
curve: _curve,
duration: _duration,
height: 120,
width: 120,
padding:
_isHover ? const EdgeInsets.all(10) : const EdgeInsets.all(20),
decoration: BoxDecoration(
color: _isHover ? onHoverBgColor : backgroundColor,
borderRadius: borderRadius,
),
child: AnimatedAlign(
alignment: _isHover ? Alignment.topLeft : Alignment.center,
curve: _curve,
duration: _duration,
child: Text(
'Custom Widget',
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(color: _isHover ? onHoverTextColor : textColor),
),
),
),
),
),
);
}
}Without Mix, the code requires manual setState, MouseRegion, and multiple Animated* widgets. Style logic is interleaved with widget construction, making it harder to maintain.