Design Tokens
When you hardcode Colors.blue or EdgeInsets.all(16) across your app, changing your brand color or spacing scale means hunting through dozens of files. Design tokens solve this: you define a named value once, provide it at the top of your widget tree, and every style that references it updates automatically — including when you switch themes.
Mix provides built-in support for tokens through MixToken and MixScope.
Getting Started
Using a token takes three steps: declare it, provide a value, and reference it in a style.
// 1. Declare the token (the $ prefix is a naming convention to distinguish tokens from regular variables)
final $primary = ColorToken('primary');
// 2. Provide the token value via MixScope
MixScope(
colors: {
$primary: Colors.lightBlue,
},
child: MyApp(),
);
// 3. Reference the token in a style — call() resolves it from MixScope at build time
final style = BoxStyler()
.color($primary())
.size(100, 100);MixScope
MixScope is the widget that provides token values to all its descendants. It works like Flutter’s Theme and ThemeData pattern — place it near the top of your tree and every descendant can resolve tokens from it.
MixScope(
colors: {
$primary: Colors.blue,
$background: Colors.white,
},
spaces: {
$spacingMd: 16.0,
},
radii: {
$radiusMd: Radius.circular(8),
},
child: MaterialApp(home: MyHomePage()),
);Nested MixScope widgets override token values for their subtree. The preview below shows an outer scope providing blue, and an inner scope overriding the same token to red for a child widget:
Built-in Token Types
Mix provides token types for common styling needs:
| Token Type | Value Type | Use Case |
|---|---|---|
ColorToken | Color | Colors and backgrounds |
SpaceToken | double | Spacing values (padding, margin) |
DoubleToken | double | Any numeric value |
RadiusToken | Radius | Border radii |
TextStyleToken | TextStyle | Typography styles |
BorderSideToken | BorderSide | Border definitions |
ShadowToken | List<Shadow> | Text shadows |
BoxShadowToken | List<BoxShadow> | Box shadows |
FontWeightToken | FontWeight | Font weights |
DurationToken | Duration | Animation durations |
BreakpointToken | Breakpoint | Responsive breakpoints |
Using Tokens in Styles
Mix offers two ways to reference a token. Both resolve to the same value — the difference is ergonomics.
Call syntax (recommended)
The simplest approach. Call the token like a function to get a reference that resolves at build time:
final style = BoxStyler()
.color($primary())
.paddingAll($spacingMd());Prop.token (for directives)
Use Prop.token() with .create() when you need to chain directives on a token value — for example, doubling a spacing token:
final style = BoxStyler.create(
width: Prop.token($baseSize).multiply(2),
padding: Prop.token($spacingMd),
);For most styles, use the call syntax. Reach for Prop.token() only when you need directives like .multiply(), .add(), or .clamp().
Theme Switching
Tokens make theme switching straightforward: define two maps and swap them in MixScope.
final $background = ColorToken('background');
final $foreground = ColorToken('foreground');
final lightColors = {
$background: Colors.white,
$foreground: Colors.black,
};
final darkColors = {
$background: Colors.grey[900]!,
$foreground: Colors.white,
};
// Swap themes by toggling the map
MixScope(
colors: isDark ? darkColors : lightColors,
child: MyApp(),
);Every style referencing $background or $foreground updates automatically when the map changes.
Resolving Tokens Programmatically
You can resolve tokens outside of styles using BuildContext. This is useful when you need a token value in non-Mix code, such as a plain Flutter widget:
@override
Widget build(BuildContext context) {
final primaryColor = $primary.resolve(context);
final spacing = $spacingMd.resolve(context);
return Container(
color: primaryColor,
padding: EdgeInsets.all(spacing),
child: Text('Resolved tokens'),
);
}Best Practices
- Use the
$prefix for token variables ($primary,$spacingMd) — it makes tokens visually distinct from regular values at a glance - Use hierarchical names for the token string identifier:
ColorToken('color.primary'),SpaceToken('spacing.md') - Group related tokens in separate files (
tokens/colors.dart,tokens/spacing.dart) to keep declarations organized - Prefer tokens over hardcoded values — even if you only have one theme today, tokens make future changes a single-line edit instead of a find-and-replace
Common Pitfalls
Using $token() outside of Mix APIs. Calling a token (e.g. $primary()) returns a placeholder value, not the real resolved value. Mix replaces this placeholder internally when the style is resolved. If you pass $primary() to a plain Flutter widget like Container(color: $primary()), you’ll get the placeholder — not your actual color. Use $token.resolve(context) instead:
// Wrong — $primary() is a placeholder, not a real Color
Container(color: $primary())
// Correct — resolve gives you the actual value from MixScope
Container(color: $primary.resolve(context))See Also
- Styling — using tokens inside styler chains
- Directives —
Prop.token()with.multiply(),.add(), and other transforms - Creating Custom Tokens — building your own token types for values not covered by built-ins
- Theming Tutorial — step-by-step guide to building a full themed app