Skip to Content
Mix 2.0 is in development! You can access the Mix 1.0 docs here.
DocsGuidesDesign Tokens

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.

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MixScope( colors: { $primary: Colors.blue, $secondary: Colors.green, $background: Colors.white, }, spaces: { $spacingSm: 8.0, $spacingMd: 16.0, $spacingLg: 24.0, }, radii: { $radiusSm: Radius.circular(4), $radiusMd: Radius.circular(8), }, textStyles: { $headingStyle: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), $bodyStyle: TextStyle(fontSize: 16), }, child: MaterialApp( home: MyHomePage(), ), ); } }
Resolving preview metadata...

Built-in Token Types

Mix provides token types for common styling needs:

Token TypeValue TypeUse Case
ColorTokenColorColors and backgrounds
SpaceTokendoubleSpacing values (padding, margin)
DoubleTokendoubleAny numeric value
RadiusTokenRadiusBorder radii
TextStyleTokenTextStyleTypography styles
BorderSideTokenBorderSideBorder definitions
ShadowTokenList<Shadow>Text shadows
BoxShadowTokenList<BoxShadow>Box shadows
FontWeightTokenFontWeightFont weights
DurationTokenDurationAnimation durations
BreakpointTokenBreakpointResponsive breakpoints

Using Tokens in Styles

Mix offers three ways to reference a token. They all resolve to the same value — the difference is ergonomics.

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 of values and swap them in MixScope.

// Define tokens final $background = ColorToken('background'); final $foreground = ColorToken('foreground'); final $surface = ColorToken('surface'); // Light theme values final lightTheme = { $background: Colors.white, $foreground: Colors.black, $surface: Colors.grey[100]!, }; // Dark theme values final darkTheme = { $background: Colors.grey[900]!, $foreground: Colors.white, $surface: Colors.grey[800]!, }; class ThemedApp extends StatefulWidget { @override State<ThemedApp> createState() => _ThemedAppState(); } class _ThemedAppState extends State<ThemedApp> { bool isDark = false; @override Widget build(BuildContext context) { return MixScope( colors: isDark ? darkTheme : lightTheme, child: MaterialApp( home: Scaffold( body: Box( style: BoxStyler().color($background()), child: Column( children: [ StyledText( 'Hello, World!', style: TextStyler().color($foreground()), ), ElevatedButton( onPressed: () => setState(() => isDark = !isDark), child: Text('Toggle Theme'), ), ], ), ), ), ), ); } }

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'), ); }

Creating Custom Tokens

The built-in token types cover most styling needs. If you need a token for a type they don’t cover (e.g. EdgeInsetsGeometry), you can create one by extending MixToken<T> and providing a reference class.

/// Token for EdgeInsetsGeometry values class EdgeInsetsGeometryToken extends MixToken<EdgeInsetsGeometry> { const EdgeInsetsGeometryToken(super.name); @override EdgeInsetsGeometryRef call() => EdgeInsetsGeometryRef(Prop.token(this)); } /// Reference class — wraps the Prop so it can be used in styles class EdgeInsetsGeometryRef extends Prop<EdgeInsetsGeometry> { EdgeInsetsGeometryRef(Prop<EdgeInsetsGeometry> prop) : super.fromProp(prop); }

Custom tokens use the generic tokens map in MixScope (instead of colors, spaces, etc.):

final $contentPadding = EdgeInsetsGeometryToken('padding.content'); final $cardPadding = EdgeInsetsGeometryToken('padding.card'); MixScope( tokens: { $contentPadding: EdgeInsets.all(16), $cardPadding: EdgeInsets.symmetric(horizontal: 24, vertical: 16), }, child: MyApp(), ); // Use via merge + .create() since custom tokens work through Prop final style = BoxStyler() .color(Colors.red) .size(100, 100) .merge(.create(padding: $contentPadding()));

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. To get the real value outside of Mix, 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))