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 using a fluent API, design tokens, and context-aware variants.
- Compose, merge, and apply styles across widgets
- Write maintainable styling definitions separate from widget code
- Adapt styles conditionally based on interactions and context
Flutter's built-in styling works well for simple widgets, but as your app grows, common pain points emerge:
- Style duplication: The same colors, spacing, and borders are repeated across widgets with no easy way to share them.
- Tight coupling: Style logic lives inside
build()methods, making it hard to reuse or test independently. - No conditional styling: Adapting styles for hover, press, dark mode, or breakpoints requires manual boilerplate.
Mix solves these by giving you a dedicated styling layer that stays consistent across widgets and files — without being tied to Material Design.
- Define styles outside widgets while retaining
BuildContextaccess (resolved at build time, likeTheme.of, with more flexibility) - 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
- 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
- Dart SDK: 3.11.0 or higher
- Flutter: 3.41.0 or higher
flutter pub add mixOr in pubspec.yaml:
dependencies:
mix: <latest>import 'package:mix/mix.dart';final cardStyle = BoxStyler()
.size(240, 100)
.color(Colors.blue)
.alignment(.center)
.borderRounded(12)
.border(.all(.color(Colors.black).width(1).style(.solid)));
Box(
style: cardStyle,
child: StyledText(
'Hello Mix',
style: TextStyler().color(Colors.white).fontSize(18),
),
);Define styles with a fluent, chainable API. Later attributes override earlier ones when chained:
final boxStyle = BoxStyler()
.height(100)
.width(100)
.color(Colors.purple)
.borderRounded(10);
// Compose from a base style
final base = BoxStyler()
.paddingX(16)
.paddingY(8)
.borderRounded(8)
.color(Colors.black);
final solid = base.color(Colors.blue);Styles adapt to interactions and context in one place:
final buttonStyle = BoxStyler()
.height(50)
.borderRounded(25)
.color(Colors.blue)
.onHovered(.color(Colors.blue.shade700))
.onDark(.color(Colors.blue.shade200));Built-in variants include onHovered, onPressed, onFocused, onDisabled, onDark, onLight, onBreakpoint, onMobile, onTablet, onDesktop, and platform/context variants.
Define reusable tokens and provide them via MixScope:
final $primary = ColorToken('primary');
final $spacingMd = SpaceToken('spacing.md');
MixScope(
colors: { $primary: Colors.blue },
spaces: { $spacingMd: 16.0 },
child: MyApp(),
);
final style = BoxStyler()
.color($primary())
.paddingAll($spacingMd());- Implicit — Values animate smoothly with
.animate(AnimationConfig....) - Phase — Multi-step flows (e.g. tap → compress → expand) with
.phaseAnimation(...) - Keyframe — Full control with tracks and keyframes
Some visual effects — opacity, clipping, visibility — aren't style properties. Modifiers let you declare widget wrappers inside your style so they stay composable and animatable.
Directives transform values (text casing, number scaling, color adjustments) at resolve time, keeping transformations inside the style so they survive merging.
| Package | Description |
|---|---|
| mix | Core styling framework |
| mix_annotations | Annotations for code generation |
| mix_generator | build_runner generator for specs |
| mix_lint | Custom linter rules |
| mix_tailwinds | Utility-first styling inspired by Tailwind CSS |
- Introduction
- Getting started
- Styling
- Dynamic styling
- Design tokens
- Animations
- Widgets (Box, Text, Icon, FlexBox, Stack, etc.)
- Ecosystem