Introduction
Mix is a styling system for Flutter that separates style definitions from widget structure. It provides a composable, type-safe way to define and apply styles.
- Compose, merge, and apply styles across widgets
- Write maintainable styling definitions separate from widget code
- Apply styles conditionally based on BuildContext
Why Mix?
Mix provides utility functions for intuitive, composable styling that stays consistent across widgets and files—without being tied to Material Design.
Goals
- Define styles outside widgets while retaining
BuildContextaccess - Reuse style definitions across your app for consistency
- Adapt styles conditionally using variants (hover, dark mode, breakpoints)
- Type-safe composability using Dart’s type system
Guiding Principles
- Simple: Thin layer over Flutter—widgets remain compatible and predictable
- Consistent: API mirrors Flutter naming conventions
- Composable: Build complex styles from simple, reusable pieces
- Extensible: Override and extend utilities to fit your needs
Understanding the Styler Pattern
Mix provides two constructor patterns for Styler classes. Understanding when to use each is key to effective styling:
Fluent API: BoxStyler()
The default constructor provides a fluent API for chaining style properties:
final style = BoxStyler()
.color(Colors.blue)
.paddingAll(16)
.borderRounded(8);Use this pattern when:
- Defining styles with direct values
- Chaining multiple style properties together
- Building most common styling use cases
Prop-Based API: BoxStyler.create()
The .create() constructor accepts Prop<T> parameters for advanced composition:
final style = BoxStyler.create(
color: Prop.token($primaryColor),
padding: Prop.value(EdgeInsets.all(16)),
);Use this pattern when:
- Working with design tokens (
Prop.token()) - Applying number directives like
multiply()orclamp() - Building custom widget libraries that need deferred resolution
How Prop<T> Works
Prop<T> is a wrapper that enables deferred value resolution. It can hold:
- Direct values:
Prop.value(Colors.blue) - Token references:
Prop.token($primaryColor) - Directives:
Prop.value(16.0).multiply(2).clamp(0, 50)
Values are resolved when the style is applied, allowing tokens to be looked up from MixScope and directives to transform values at build time.
Key Features
Powerful Styling API:
Define styles using a fluent, chainable API with Styler classes:
import 'package:flutter/material.dart';
import 'package:mix/mix.dart';
final boxStyle = BoxStyler()
.height(100)
.width(100)
.color(Colors.purple)
.borderRounded(10);
final textStyle = TextStyler()
.fontSize(20)
.fontWeight(FontWeight.bold)
.color(Colors.black);Learn more about styling
Dynamic Styling
Styles can adapt to interactions and context (hover, press, dark mode):
final buttonStyle = BoxStyler()
.height(50)
.borderRounded(25)
.color(Colors.blue)
.onHovered(BoxStyler().color(Colors.blue.shade700))
.onDark(BoxStyler().color(Colors.blue.shade200));Learn more about Dynamic Styling
Animation Support:
Mix supports implicit animations, phase animations (multi-step sequences), and keyframe animations. Learn more in the Animations Guide.
Design Tokens and Theming:
Define reusable design tokens for colors, spacing, radii, and more:
final $primaryColor = ColorToken('primary');
final $borderRadius = RadiusToken('borderRadius');
final tokenDefinitions = <MixToken, Object>{
$primaryColor: Colors.blue,
$borderRadius: Radius.circular(8),
};
final tokenStyle = BoxStyler()
.color($primaryColor())
.width(100)
.height(100)
.borderRadius(BorderRadiusGeometryMix.all($borderRadius()));
// Set the tokens in the MixScope
MixScope(
tokens: tokenDefinitions,
child: MyApp(),
);Learn more about Design Tokens and Theming