Styling with Mix
In Flutter, styling lives inside your widget tree — a Container owns its color, padding, and border. This makes styles hard to reuse, share, or override without duplicating widget code. Mix solves this by pulling style declarations out of the tree into composable, chainable objects called stylers.
Defining and Applying Styles
A styler is a declarative builder that describes how a widget should look. Each Mix widget has a matching styler: BoxStyler for Box, TextStyler for StyledText, IconStyler for StyledIcon, and so on.
Define a style with a fluent chain, then pass it to the widget via the style parameter:
final boxStyle = BoxStyler()
.width(240)
.height(100)
.color(Colors.blue)
.borderRounded(12);
Box(style: boxStyle, child: Text('Hello'));The same pattern works across all Mix widgets:
// Text
final textStyle = TextStyler().fontSize(24).fontWeight(.bold).color(Colors.blue);
StyledText('Hello Mix', style: textStyle);
// Icon
final iconStyle = IconStyler().size(32).color(Colors.blue);
StyledIcon(icon: Icons.favorite, style: iconStyle);
// FlexBox (row/column layout + box decoration)
final rowStyle = FlexBoxStyler()
.direction(.horizontal)
.spacing(12)
.paddingAll(16)
.color(Colors.grey.shade100);
FlexBox(style: rowStyle, children: [...]);Callable stylers
Every styler also works as a widget factory. Call it directly instead of passing it to a constructor:
final box = BoxStyler().color(Colors.blue).size(100, 100);
final label = TextStyler().fontSize(18).color(Colors.white);
// Calling the styler creates the widget
box(child: label('Hello Mix'));This is equivalent to Box(style: box, child: StyledText('Hello Mix', style: label)), but more concise. Use whichever form you prefer.
Style Composition and Override
Build new styles on top of existing ones. Define a base once, then create variations by chaining additional properties:
final base = BoxStyler()
.paddingX(16)
.paddingY(8)
.borderRounded(8)
.color(Colors.black);
final solid = base.color(Colors.blue);
final soft = base.color(Colors.blue.shade100);How merging works
Each chained method internally merges a new property into the existing styler. Later values override earlier ones for the same property, while unrelated properties are preserved:
final a = BoxStyler().color(Colors.red).padding(.all(16));
final b = a.color(Colors.blue);
// b has color: blue (overridden) + padding: 16 (preserved)You can also merge two separate stylers explicitly:
final layout = BoxStyler().padding(.all(16)).borderRounded(12);
final visual = BoxStyler().color(Colors.blue).shadow(blurRadius: 10);
final card = layout.merge(visual); // combines bothDynamic and Context-Aware Styling
Styles can adapt to user interactions and context using variants. Instead of writing conditional logic in your widget tree, you declare what changes and when:
final button = BoxStyler()
.color(Colors.blue)
.onHovered(.color(Colors.blue.shade700))
.onDark(.color(Colors.blue.shade200));Variants are covered in depth in the Dynamic Styling guide.
See Also
- Utility-First Approach — the philosophy behind composable stylers
- Dynamic Styling — variants for hover, press, dark mode, and breakpoints
- Design Tokens — centralized values with
MixScope - Widget Modifiers — widget-level effects like opacity and clipping via
.wrap()