Docs
/
Concepts
/
Variants

Variants

While building your design system you will find have the need to create certain variations of a Widget. This makes the design system more flexible and reusable, by leveraging shared visual properties between them.

In the following example hover() is a Variant that will be applied when a Pressable triggers the hover state. The bgColor and textColor properties will be overridden with the new value when the variant applies

final style = Mix(
  bgColor($primary),
  textColor($onPrimary),
  hover(
    bgColor($primary), 
    textColor($onPrimary),
  ),
);

Pressable(
  mix: style,
  child: const TextMix('Button'),
);

Variants can be extremely powerful, and allow you to create consistent variations of your Widgets.

Usage

To use a variant, call it on a Mix

final mix = Mix(
  dark(
    textColor(Colors.white),
  ),
  light(
    textColor(Colors.black),
  )
);

Conditional operators

The operators | and & can be used to add conditions to your mix:

  • |: whether one of the variants apply
final mix = Mix(
  padding(20.0),
  (small | medium)( // Whether it's small OR medium
    width(300),
    height(400),
    bgColor(Colors.white),
  ),
);
  • &: when all of the variants apply
final mix = Mix(
  padding(20.0),
  (hover & press)( // When it's hovering AND pressing
    textColor(Colors.black),
    bold(),
  ),
);

The not operator can also be called to execute the variant content only when the given variant is not called. For example:

final mix = Mix(
  (not(disabled))(
    scale(1.2),
  ),
);

Variants catalog

Mix already has some Reactive Variants defined which can be used to create responsive Widgets.

Pressable

These are Variants that will change based on different gestures and state of the Pressable Widget.

  • hover(): Applies attributes on hover.
  • focus(): Applies attributes on focus.
  • press(): Applies attributes when pressing.
  • disabled(): Applies attributes when disabled.

Breakpoints

  • small(): Applies attributes when the screen is at least small breakpoint.
  • medium(): Applies attributes when the screen is at least medium breakpoint.
  • large(): Applies attributes when the screen is at least large breakpoint.

Breakpoints for these sizes can be configured in the MixTheme:

MixTheme(
  data: MixThemeData(
    breakpoints: MixThemeBreakpoints(
      small: 600, // cellphone
      medium: 1240, // tablet
      large: 1440, // desktop
    ),
  ),
  child: ...,
),

Orientation

  • portrait(): Applies attributes when the screen is in portrait orientation.
  • landscape(): Applies attributes when the screen is in landscape orientation.

Dark Mode

  • dark(): Applies attributes when the theme is in dark mode.
  • light(): Applies attributes when the theme is in light mode.

State Holding

  • active(): Applies attributes when the widget state is marked as active
  • inactive(): Applies attributes when the widget state is marked as inactive

Text Styling

For widgets such as Dialogs, Cards and Modals, it's good to have titles and content.

  • title(): Applies attributes when the TextMix is marked as title
  • paragraph(): Applies attributes when the TextMix is marked as paragraph
final mix = Mix(
  title(
    titleCase(), // Makes the title uppercase
    font(
      weight: FontWeight.bold, // Makes the title bold
    ),
  ),
);

All other texts that are not marked as title will not be touched.

Custom Variants

Mix gives you complete control of how to define your own Variants and when they are applied.

There are two types of Variants that you are able to define: callable and context variants.

Callable Variants

Can be called manually when they need to be applied. You can define a variant the following way.

const outlined = Variant('outlined');

final mix = Mix(
  bgColor(Colors.black),
  textColor(Colors.white),
  outlined(
    bgColor(Colors.transparent),
    borderColor(Colors.black),
    textColor(Colors.black),
  ),
);

You are then able to choose when to apply the variant based on your own logic.

Pressable(
  mix: mix.withVariant(outlined),
  onPressed: () {},
  child: const TextMix('Outlined Button'),
);

You are also able to pass the variant through the variant parameter on the MixableWidget constructor

Pressable(
  mix: mix,
  variant: outlined,
  onPressed: () {},
  child: const TextMix('Outlined Button'),
);

Context Variants

These are Variants that have a method that defines when they should be applied based on the BuildContext. These will be applied automatically.

Here is an example implementation of the dark() to understand how it works.

final dark = Variant(
  'dark',
  shouldApply: (BuildContext context) {
    return Theme.of(context).brightness == Brightness.dark;
  },
);