CLI tool
btwld/mix avatar
btwld/mix

Mix: a styling system that keeps Flutter styles out of the widget tree

A styling system for Flutter

804 stars49 forksDartBSD-3-Clause

At a glance

What is it?
Mix is a Flutter styling system that separates style definitions from widget structure, giving a fluent, type-safe API for composing styles, variants for hover, press, dark mode and breakpoints, and design tokens delivered through a scope. It is published on pub.dev and works without depending on Material Design.
Who is it for?
Mix suits a Flutter team whose widgets have grown past the point where styling inside build methods is manageable, because it gives you reusable style definitions, conditional variants and tokens without asking you to adopt Material Design or restructure the widget tree.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Dart, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Styles as values, not as widget code

Mix describes itself as a styling system for Flutter that separates style definitions from widget structure, and it gives a composable, type-safe way to define and apply styles through a fluent API, with design tokens and context-aware variants.

The framing in the first sentence is the whole idea. In a typical Flutter codebase the style lives inside the build method of the widget, so a card is a widget that happens to be blue, and the only way to reuse that blue is to copy the code. Mix makes the style a value that exists on its own, so a widget consumes a style the way it consumes a value.

The three bullets in the README summarise the offering: compose, merge and apply styles across widgets, write maintainable styling definitions separate from widget code, and adapt styles conditionally based on interactions and context.

The guiding principles say how the library intends to behave, and they are conservative in a useful way. It is a thin layer over Flutter with widgets that remain compatible and predictable, the API mirrors Flutter naming conventions, styles compose from simple reusable pieces, and the utilities can be overridden and extended.

That last point is what separates Mix from a design system. It is not a catalogue of buttons and inputs, it is the mechanism you would use to build your own.

The problems it is aimed at

The README is explicit about the pain points it addresses as an application grows, and they are worth reading because they are recognisable.

Style duplication comes first. The same colours, spacing and borders are repeated across widgets with no easy way to share them. That is the problem design tokens and style objects solve, and it is the one Mix is most directly about.

Tight coupling is second. Style logic lives inside build methods, which makes it hard to reuse or to test independently. Moving the style out means the visual definition of a widget can be asserted on without building the widget.

Conditional styling is third. Adapting styles for hover, press, dark mode or breakpoints requires manual boilerplate, which is the part that most often turns into a mess of ternaries inside a build method.

The goals that follow map onto those three problems. Define styles outside widgets while retaining build context access, resolved at build time like a theme lookup but with more flexibility. Reuse style definitions across the application for consistency. Adapt styles conditionally using variants. And do it with type-safe composability using Dart's type system.

The last point is the one that matters most for a Dart project. A styling layer that returns dynamic would undercut the type system the rest of the app relies on, and Mix's approach of typed style classes keeps the analyser useful.

The fluent API and how styles compose

Styles are built by chaining calls on a styler object, and later attributes override earlier ones.

dart
final boxStyle = BoxStyler()
    .height(100)
    .width(100)
    .color(Colors.purple)
    .borderRadius(.circular(10));

Because a style is a value, composition is a method call rather than a copy and paste.

dart
final base = BoxStyler()
    .padding(.horizontal(16))
    .padding(.vertical(8))
    .borderRadius(.circular(8))
    .color(Colors.black);
final solid = base.color(Colors.blue);

That second snippet is the composition model in miniature. A base style is defined once, and a derived style overrides one property, so a family of variants shares everything else. It is the same idea as CSS custom properties or a theme override, expressed in Dart.

The names are deliberately Flutter-like. There is a box styler and a text styler, the methods are named after the properties they set, and the enum-style arguments use a leading dot, such as a circular border radius or a solid border style, which keeps the call sites short and readable.

The result is that a widget takes a style and renders it.

dart
Box(
  style: cardStyle,
  child: StyledText(
    'Hello Mix',
    style: TextStyler().color(Colors.white).fontSize(18),
  ),
);

The wrapping widgets are part of the library rather than something you build yourself, so the style and the widget agree on what to do with the values you pass.

Variants, and the wrapping layouts

Variants are how a single style definition reacts to interaction and context, which is the answer to the conditional styling problem.

dart
final buttonStyle = BoxStyler()
    .height(50)
    .borderRadius(.circular(25))
    .color(Colors.blue)
    .onHovered(.color(Colors.blue.shade700))
    .onDark(.color(Colors.blue.shade200));

The built-in list covers the states you would otherwise write by hand: onHovered, onPressed, onFocused, onDisabled, onDark and onLight for the theme, onBreakpoint with onMobile, onTablet and onDesktop, and platform and context variants.

The important property is that all of these live in one place. In a build method you would branch on hover state, on pressed state and on the theme, and the branch would sit between the style you wanted and the style you applied. Here the style object carries all the branches.

Layout wrappers are covered as well. The WrapBox combines box styling with Flutter's run-based wrap layout, so a tag cloud or a chip row can be styled as a box and laid out as a wrap.

dart
final tagCloud = WrapBoxStyler()
    .padding(.all(16))
    .spacing(8)
    .runSpacing(10)
    .wrapAlignment(WrapAlignment.center);

WrapBox(style: tagCloud, children: tags);

The naming is a small but careful detail. Alignment and clip behavior configure the outer box, while wrap alignment and wrap clip behavior configure the inner wrap, and there is a nested style escape hatch for the cases where that split is not enough.

Tokens, scope and the rest of the feature set

Design tokens are defined once and provided to the tree through a scope, which is the mechanism for a consistent palette and spacing scale across an application.

dart
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())
    .padding(.all($spacingMd()));

The token names carry a dollar prefix, which is a convention borrowed from CSS custom properties and one that makes them easy to spot in a file. A token is a reference rather than a value, so a style that uses the primary colour token follows the scope, and changing the scope changes every style that uses it.

Animations come in three shapes. Implicit animations use an animate call with an animation configuration, phase animations handle multi-step flows such as a tap that compresses and then expands, and keyframes give full control with tracks and keyframes.

Widget modifiers exist because some visual effects are not style properties. Opacity, clipping and visibility cannot be expressed as a style attribute, so modifiers let a style declare the widget wrappers it needs, which keeps them composable and animatable rather than pushing them back into the widget tree.

Directives transform values at resolve time, covering text casing, number scaling and colour adjustments, so a style can say what to do with a value rather than requiring the widget to do it.

Installation, and the agent skill

The prerequisites are stated precisely: Dart 3.12.0 or higher and Flutter 3.44.0 or higher. The package is added with the pub command.

bash
flutter pub add mix

Or it goes into the pubspec file as a dependency, and the import is a single line.

dart
import 'package:mix/mix.dart';

The more unusual part of the README is the agent skill, which is a newer kind of distribution channel for a package.

bash
npx skills add btwld/mix --list
bash
npx skills add btwld/mix --skill mix -y

The skill is described as helping coding agents build Flutter UI with Mix and maintain the monorepo's specs, generators, variants, animations, tokens, modifiers and packages. Installing it in a project creates a local agent skills directory, which is git ignored because the skills directory in the repository is the committed source catalogue, and there are commands to list installed skills and update them later, or to install globally or invoke the skill ephemerally when a project local copy is not wanted.

The repository layout backs this up. Alongside the usual Dart package files there are a skills directory, a .claude directory, a .claude-plugin directory, a .codex-plugin directory, an AGENTS.md and a CLAUDE.md, plus a skills.sh badge in the readme.

So the project ships both a library and a description of how to use it, addressed to tools as well as people, which is a pattern more Flutter packages will have to deal with.

Versions, licensing and what to check

The release tags are mix-v2.0.1 from 2026-03-30, Mix 1.7.0 from 2025-07-02 and Mix 1.6.0 from 2025-06-19, and that last one carries a name, Nielsen, which is the author's habit of naming releases.

So the 2.x line is out and the 1.x line was maintained for a while, with the naming convention signalling a break. The licence is BSD 3-Clause according to the repository listing, and the readme badge points at the MIT licence page, so the terms are worth confirming from the LICENSE file before you ship.

The last push was on 2026-10-21, so the project is actively developed.

The repository layout is that of a Dart monorepo, with a packages directory holding the published packages, an examples directory with a showcase, guides for the documentation, schemas, scripts, an assets folder, and the build tooling pinned with melos, a Flutter version file and a setup script.

Three checks before you adopt it.

The version floor, because Dart 3.12 and Flutter 3.44 are recent requirements and will decide whether you can install the package at all.

The variant vocabulary, since the built-in list covers hover, press, focus, disabled, dark, light and the three breakpoints, and if your design system has states that are not in that list you will be adding your own.

And the conventions question. Mix separates styling from widget structure. If your team keeps a file per screen with markup and logic mixed together, that habit will work against the library rather than with it.

Editorial conclusion

Mix suits a Flutter team whose widgets have grown past the point where styling inside build methods is manageable, because it gives you reusable style definitions, conditional variants and tokens without asking you to adopt Material Design or restructure the widget tree. It is the wrong choice for a small application where the built-in styling is sufficient, and the wrong choice if you want a component library rather than a styling layer, since Mix supplies styling and wrappers rather than a catalogue of ready made controls. Verify first that the Flutter and Dart versions your project uses meet the stated minimums, that the variant names cover the states your design system defines, and that your team's convention for keeping markup and logic in separate files is one the library's separate namespaces support.

Frequently asked questions

What is the Mix styling system for Flutter?

Mix separates style definitions from widget structure, providing a fluent, type-safe API to define and compose styles, with variants for interaction and context states, design tokens provided through a scope, animations, widget modifiers and directives.

How do I install Mix?

With flutter pub add mix, or by adding the mix dependency to the pubspec file and importing package:mix/mix.dart. The prerequisites are Dart 3.12.0 or higher and Flutter 3.44.0 or higher.

Which conditional states does Mix support out of the box?

The built-in variants include onHovered, onPressed, onFocused, onDisabled, onDark, onLight, onBreakpoint with onMobile, onTablet and onDesktop, and platform and context variants.

How are design tokens shared across an app?

You define colour and space tokens, provide their values through a MixScope wrapping your application, and reference them in styles so that changing the scope changes every style that uses the token.

Official sources

  1. btwld/mix on GitHub
  2. License: BSD-3-Clause
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/btwld-mix.svg)](https://hysenlabs.com/projects/btwld-mix)

Was this useful?