MIX: A tool for building design systems in Flutter

Souvik Biswas
Flutter Community
Published in
11 min readDec 23, 2021

--

Mix helps you to build design systems in Flutter expressively and effortlessly. It offers primitive building blocks to help developers and designers create beautiful and consistent user interfaces for their applications. This makes even complex design systems easily maintainable and scalable.

What is a design system?

A design system is a set of standards to be followed throughout the user interface (UI) of a certain brand to maintain consistency and make it easily scalable. All the components that are created for that brand follow these design guidelines throughout their entire ecosystem, whether it be a mobile application or a web application (though the design guidelines vary a bit according to the different screen sizes).

Typically, most of the applications build for Android follow the Material Design guidelines for designing their user interface, iOS follow Human Interface Guidelines, and Windows follow Fluent Design System.

But certain brands created their own design system to make their applications stand out from the apps following the traditional design systems, and can be easily distinguished from them. Some of the bands following their own design guidelines includes Uber, Shopify, Atlassian, IBM, Airbnb, and many more.

Know more about the other design systems that companies follow from here.

Why are there so limited design systems?

Now that you know what’s the importance of having a proprietary design system, you would assume that most popular brands should have their own design system. But in reality, it’s not the case because creating a design system from scratch is a time-consuming task and would also require a lot of creative people to work in sync to give shape to an entirely new design language.

You can take a look at the latest Design Systems Survey conducted by Sparkbox.

Also keeping in mind, once the design system is ready, that the design language has to be converted into code to build components for a mobile or web app which is more difficult, time-consuming, and would require teams to work in proper sync.

Once you have a design system in place, the hardest part is maintaining the design system overtime keeping the consistency.

Today, we’ll mostly focus on the part of transferring a design language to code for creating components that can be incorporated within an app. So, we are assuming that after a lot of iterations you finally have a proper design system, now you need to build components for your app that are future-proof and easily scalable.

In this article, we have chosen the Flutter framework, as it is currently the fastest and easiest framework to iterate upon UI designs, to be used for building the user interface by following those design guidelines.

How Mix helps?

Though we have said Flutter to be the best to work on user interfaces of any application, once you start working and maintaining a design system in a large-scale app that is growing over time, things might start getting a bit messy and hard to manage.

Using Theme in Flutter helps to keep a bit of consistency across user interfaces but it doesn’t extend across all visual attributes. So, even if you are using themes, most of the time you need to define attributes inside the widgets which are really hard to maintain over time.

The Flutter widgets favor composition (meaning wrapping one widget with another) over inheritance, which is a nice decision as it makes it easy to interact with the API. But to maintain a consistent design language, inheritance also plays an important role as it helps in automatically passing attributes down the widget tree.

This is where Mix comes into the picture which takes the help of composition style of Flutter with the added advantage of inheritance for all attributes, Mix acts as a helper for maintaining a design language easily.

Following is a code snippet for building a simple decorated Container widget using the basic in-built Flutter composition:

Notice in the above code snippet, the attributes and widgets are very tightly integrated so it’s really hard to separate out the attributes.

Using Mix a similar component can be designed using the following:

Here, you can see that the attributes are defined completely out of the widgets, this makes it a lot easier to maintain the design system over time. Also, if you notice closely, we have defined the style Mix inside the Box widget but the text color is automatically set to white as the TextMix inherits the design attributes from the Box.

Don’t worry too much about how a Mix is defined as you’ll get a deeper dive into the core concepts of Mix and how it works.

Implementing Basic Mix

The best way to understand how you can use Mix is by creating a new Flutter project and integrating Mix with it.

Run the following command to create a new Flutter project:

flutter create mix_design_samples

The project is created using Flutter version 2.8 with null safety support.

To add Mix, run the following command:

flutter pub add mix

This will automatically add the latest version of Mix present on pub.dev to your project’s pubspec.yaml file.

You can also add it to your pubspec.yaml file manually:

dependencies:
mix: <version>

Run flutter pub get after adding the Mix dependency.

Now, go to the main.dart file inside your project’s lib folder and replace with the following code:

This is just a sample Flutter app template. Let’s define a simple Mix with attributes for building a Box with red as the background color.

Here, a Mix is defined with some attributes: height, width, rounded, elevation and bgColor (background color), and stored inside a final variable. Then, that mix can be used inside some widgets provided by Mix, in this code snippet a Box widget is used.

The widget will look like this:

Notice here we have defined the mix inside the build method. To keep your code clean, you should keep the mixes in a separate file. But if you define the mix as a final variable outside of the build method, you will lose one of the most advantageous Flutter features, hot reload.

To solve this issue, you can define the same mix that we have created for the Box widget in a completely separate file, like this:

Mix attribute hot reload in action:

The Box widget that has been used here is similar to the Container widget of Flutter. And, you may refer to the Mix attributes as the properties that you usually use inside a Container.

Fundamentals of Mix

There are some core concepts that you should know about before starting to use Mix. Let’s go through them one by one by taking the Basic Mix as a reference.

Attributes

Attributes in Mix are basically the widget properties that you usually specify while defining a widget. But doing the same thing in the Mix way, allows you to take advantage of the in-built inheritance of Mix and also keeps your code clean. So, an attribute for a Box widget can be defined like this:

Mix(BoxAttribute(height: 100));

Here, 100 is the height attribute. But there’s an even better and recommended way of defining attributes using Mix.

Utilities

Utilities are functions that help you to easily compose an attribute, you can define the same Mix like this:

Mix(height(100));

This helps in defining the attributes with an even lesser and cleaner code.

Decorators

In Mix, a Box widget is equivalent to a Container but it doesn’t have any scale property. Using Box we can still apply properties that are not a part of the widget directly, so the scale attribute can be used with Box, and these are called decorators.

So, the following in Flutter:

Transform.scale(
scale: 0.5,
child: Container(),
);

Is equivalent to this in Mix:

Box(mix: Mix(scale(0.5)));

Directives

To modify a widget’s properties directives can be used, they can also be passed similar to attributes. The best example of this would be text directives.

It can be defined as follows:

This will display the text in Title Case:

Design Tokens

Mix provides design tokens for certain attributes like colors, text styles, and spacing. The advantage of using design tokens is that you can use the context reference values.

An example is as follows:

Here, $primary is a design token for using the primary color present in the colorScheme of ThemeData.

If the primary colorScheme inside the MaterialApp widget is set to orange:

return MaterialApp(
theme: ThemeData(
colorScheme: ThemeData.light().colorScheme.copyWith(
primary: Colors.orange,
),
),
);

Then, using the above Mix will generate the following widget:

Variants

One of the most powerful features of Mix is variant. They help in making the design system more flexible and reusable. A variant can be used for overriding certain properties of a Mix and it only applies to the widget(s) implementing the variant.

There are some in-built variants that some of the Mix widgets can take advantage of. One example is below:

Within a single Mix, you can define multiple variants.

In the above code snippet, hover & press are two in-built variants that are automatically applied if the Mix is used inside any Pressable widget. A Pressable widget is similar to a button (ElevatedButton, TextButton, etc.) or GestureDetector widget in Flutter. Here, the attributes that are defined inside the hover variant will be automatically applied to the Pressable widget when it is in the hovering state, and similarly, the attributes under press variant will be applied when the widget is in the pressed state.

Pressable widget with variants in action:

You can also define your own (custom) variants. For example, if you are using the same Mix inside multiple TextMix widgets but you want to add or override certain properties for a particular TextMix then you can do it in the following way:

Here, myStyle is a custom variant and certain attributes are defined inside it, on applying the variant to a TextMix it will look like this:

The first TextMix is without the variant and the second one is with the variant applied.

There are some advanced features of Mix that allows you to combine the attributes of two Mix, inherit the attributes of another Mix, or override them. More information about these are present in the documentation.

Overview of Mix Widgets

While explaining the core concepts, a number of Mix Widgets are used. To get you more comfortable with some of the basic widgets present inside Mix, here’s an overview of the widgets.

Box

This widget is similar to the Container widget of Flutter but has some additional advantages of using it.

You can also use the Box widget in place of the Card widget of Flutter.

FlexBox

You can think of the FlexBox widget to be similar to the flexible widgets of Flutter like Column, Row, Flex. Following is an example of placing widgets in a column using FlexBox:

TextMix

This widget is equivalent to the Text widget of Flutter, a basic example of it is given below:

IconMix

Equivalent to the Icon widget of Flutter but supports inheritance and is easily customizable using variants. Following is an example of defining IconMix widget:

Pressable

This is equivalent to the button widgets (ElevatedButton, TextButton, etc.) or GestureDetector widget of Flutter. It can be defined as follows.

Dynamic user interface

You can easily create dynamic user interfaces (dark mode and light mode) using Mix.

For implementing a dynamic theme, it is recommended to use a state management library so that it’s easier to apply changes across your whole app as the theme mode changes. But today for the sake of showing how Mix helps in applying a dynamic theme, we’ll use a simple StatefulWidget with setState() to update the theme mode.

  • In the above code snippet, a MaterialApp is defined with theme set to just the default ThemeData() and darkTheme set to ThemeData.dark(), it is important to set this otherwise dark theme won’t apply.
  • A global boolean variable, isDarkMode is defined that stores the dark theme mode state, and the themeMode is set based on that variable.
  • Now, a simple IconButton is used to toggle the state of the dark mode variable.

The code for the DynamicBoxSample widget is as follows:

Here, we have defined a Mix called dynamicMix, notice that a dark variant (in-built) is used here, this is what does the trick of applying the theme mode. The attributes that are specified inside dark gets used only when the theme mode is set to dark.

The app in action:

Adding subtle animations

Mix also helps you to incorporate animations in your Flutter app easily. The following is an example of simple animation of a Pressable widget:

Inside the animPressableMix, notice that an attribute called animation is defined, and scale is set to 1. And, inside the press variant scale is set to 0.9. So, when the button is pressed it will animate between scale 1 and 0.9.

NOTE: By default the scale is null, so if you don’t define the scale to be 1 initially, the animation won’t work properly.

Building your own design system

At this point, you should be able to build your own design system using Mix. You can take the Recipe App UI template as a reference, it is built completely using attributes and widgets provided by Mix.

The codebase of this UI template is divided into the following directories:

  • screens: contains the Flutter app screens
  • widgets: contains various widgets which are used inside the screens
  • res: contains the color palette, Mix(s), image paths & text strings used in the app

One more thing to keep in mind, Mix is created to be used alongside Material or Cupertino widgets.

Wrapping up

Mix is created to improve the developer experience while building design systems in Flutter. The design systems created using Mix are easily maintainable as the attributes can be defined separately and inheritance makes the code concise. Explore more about Mix from the links present below in the references.

Mix is created by Leo Farias.

Mix is still in the experimental phase, and major APIs might change until the 1.0 release of the package.

References

You can follow me on Twitter and find some of my projects on GitHub. Also, don’t forget to checkout my Website. Thank you for reading, if you enjoyed the article make sure to show me some love by hitting that clap (👏) button!

Happy coding…

Souvik Biswas

Follow Flutter Community on Twitter: https://twitter.com/FlutterComm

--

--

Souvik Biswas
Flutter Community

Mobile Developer (Android, iOS & Flutter) | Technical Writer (FlutterFlow) | IoT Enthusiast | Avid video game player