Skip to content

btwld/mix

Repository files navigation

Mix logo

GitHub stars Pub Version Pub Likes Pub Points MIT Licence Awesome Flutter

Mix is a simple and intuitive styling system for Flutter, enabling the creation of beautiful and consistent UIs with ease.

Mix brings industry-proven design system concepts to Flutter. It separates style semantics from widgets while maintaining an easy-to-understand and manageable relationship between them.

  • Easily compose, merge, and apply styles across widgets.
  • Write cleaner, more maintainable styling definitions.
  • Apply styles conditionally based on the BuildContext.
  • NEW: Optimized for Dart's dot notation syntax for even cleaner code.

Why Mix?

Flutter developers often face challenges when it comes to styling widgets and maintaining a consistent look and feel across their apps. Flutter is heavily dependent on the Material Design System and theming, and that can be challenging, especially when creating your own design system.

Mix addresses these challenges by creating a styling system that uses utility functions for a more intuitive and composable way to style. This approach can be kept consistent across widgets and files.

Goals with Mix

  • Define visual properties outside the widget's build method while still allowing access to the BuildContext. This is done by having the style definition resolved during widget build, similar to how the current Theme.of works, but with much more flexibility.
  • Ensure consistent styling throughout your app. By having separate style definitions, you can reuse not only specific values, like colors and typography, but also entire style definitions across other styles.
  • Quickly adapt to changing design requirements. By promoting style composability and inheritance, you can more easily maintain a DRY approach to managing your design system.
  • Create adaptive designs and layouts by leveraging style variants, which are based on existing styles but can be applied conditionally or responsively.
  • Type-safe composability. Mix leverages the power of Dart's type system and class to create a type-safe styling experience.

Installation

Prerequisites

  • Dart SDK: ≥ 3.10.0 (required for dot notation syntax)
  • Flutter: Latest stable version

Add Mix to Your Project

dependencies:
  mix: ^2.0.0-rc.0

Or using the Flutter CLI:

flutter pub add mix:^2.0.0-rc.0

Guiding Principles

  • Simple Abstraction: A low-cost layer over the Flutter API, letting you style widgets without altering their core behavior, ensuring they remain compatible and predictable.
  • Consistent: Even though we are creating a new styling system, we should always keep the styling API consistent with its Flutter equivalents.
  • Composable: Styles should be easily composable by combining simple, reusable elements, promoting code reuse and maintainability.
  • Extensible: Mix should allow for reasonable overrides and reuse of its utilities, making it easy to fit your own needs.

Key Features

Powerful Styling API:

Styles are easily defined using Styler classes like BoxStyler and TextStyler, which provide a fluent, chainable API for defining style properties:

final cardStyle = BoxStyler()
    .height(100)
    .width(240)
    .color(Colors.purple)
    .borderRounded(12)
    .paddingAll(16);

// Apply the style to a Box widget
Box(
  style: cardStyle,
  child: StyledText(
    'Hello Mix',
    style: TextStyler().color(Colors.white).fontSize(18),
  ),
);

Enabling Dot Notation Syntax (Recommended)

Mix 2.0 is optimized for Dart's experimental dot notation syntax, which provides an even cleaner API. To enable it, add this to your analysis_options.yaml:

analyzer:
  enable-experiment:
    - dot-shorthands

With dot notation enabled, you can write:

final cardStyle = BoxStyler()
    .height(100)
    .width(240)
    .color(Colors.purple)
    .borderRounded(12);

Note: This feature requires Dart SDK ≥ 3.10.0.

Learn more about styling

First-Class Variant Support:

First-class support for variants, allowing you to define styling variations that can be applied conditionally or responsively.

// Define styles with widget state variants
final buttonStyle = BoxStyler()
    .borderRounded(10)
    .color(Colors.blue)
    .paddingAll(16)
    // Variants are applied automatically based on widget state
    .onHovered(BoxStyler().color(Colors.blue.shade700))
    .onPressed(BoxStyler().color(Colors.blue.shade900));

// Use with a Pressable widget - states are tracked automatically
Pressable(
  onPress: () => print('Pressed!'),
  child: Box(style: buttonStyle, child: Text('Click me')),
);

Learn more about dynamic styling

BuildContext Responsive Styling:

Mix allows you to define styles that are context-aware, applying styles conditionally based on the BuildContext.

final style = BoxStyler()
    .color(Colors.black)
    .onDark(BoxStyler().color(Colors.white))
    .onLight(BoxStyler().color(Colors.black));

Learn more about dynamic styling

Design Tokens and Theming:

Mix goes beyond the Material Theme definitions by allowing the definition of design tokens and properties that can be used across all styling utilities.

Utility-First Approach:

A complete set of utility primitives allows you to define styling properties and values in a more intuitive and composable way.

BoxStyler()
    .paddingAll(20)           // Padding 20 on all sides
    .paddingX(16)             // Padding 16 on left and right
    .paddingY(8)              // Padding 8 on top and bottom
    .paddingTop(20)           // Padding 20 on top
    .paddingLeft(20);         // Padding 20 on left

Learn more about utilities

Contributors