A Comparative Look at Mix
Mix rethinks the way we handle styling in Flutter by simplifying and streamlining the process.
See the same kind of behavior live: a styled box that reacts to hover and dark mode with Mix (left) and a static box for comparison (right).
This comparison aims to showcase how Mix enhances code readability, maintainability, and reduces boilerplate, especially when dealing with complex widget styles and interactions.
Code Comparison: With Mix vs. Without Mix
We’ll compare a common scenario: styling a custom widget, to illustrate the advantages of using Mix. In this example, we’ll be styling a custom widget with the following requirements:
- Flexible Overriding of Styles: This demonstrates the ability to override specific
TextStyleandBoxDecorationproperties, showcasing Mix’s flexibility and adaptability in customization. - Simplified Interaction-Based Styling: This highlights Mix’s capability to handle hover states effortlessly, allowing for dynamic styling changes in response to user interactions.
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(
.new()
.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),
),
);
}
}Take a look at how Mix lets you define styles and handle hover states in a really clean. The code is shorter, easier to follow, and just feels more natural to work with.
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 = 100.ms;
@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 ? textColor.lighten(20) : textColor.darken(20);
final onHoverBgColor =
isDark ? backgroundColor.lighten(20) : backgroundColor.darken(30);
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 is more verbose, especially in managing the hover state and styling. The separation between logic and presentation is less clear.