A Comparative Look at Mix
Mix rethinks the way we handle styling in Flutter by simplifying and streamlining the process. 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
TextStyle
andBoxDecoration
properties, 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(FontWeight.w600)
.color(Colors.white)
.animate(AnimationConfig.easeInOut(100.ms))
.onDark(TextStyler().color(Colors.black))
.onHovered(
TextStyler()
.animate(AnimationConfig.easeInOut(100.ms))
.onLight(TextStyler().color(Colors.white)),
);
}
BoxStyler get customBoxStyle {
return BoxStyler()
.height(120)
.width(120)
.paddingAll(20)
.elevation(ElevationShadow.nine)
.alignment(Alignment.center)
.borderRounded(10)
.color(Colors.blue)
.scale(1.0)
.animate(AnimationConfig.easeInOut(100.ms))
.onDark(BoxStyler().color(Colors.cyan))
.onHovered(
BoxStyler()
.alignment(Alignment.topLeft)
.elevation(ElevationShadow.two)
.paddingAll(10)
.scale(1.5)
.animate(AnimationConfig.easeInOut(100.ms))
.onLight(BoxStyler().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 = 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 ? 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.