Dart: What are mixins?

It’s a kind of magic ✨

Romain Rastel
Flutter Community
8 min readSep 16, 2018

--

Dart mixer (modified from shareicon)

When I started to learn Dart, the concept of mixins was new for me.
I come from the C# world, where it does not exist (at least prior to C# 8.0, to the best of my knowledge).
At first, I found this concept somewhat difficult to understand, until I realized how powerful it was.

🤔 Why do we need mixins?

If languages such as C# do not have mixins, it probably isn’t that useful, is it?

Let’s have a look at the following class inheritance diagram:

We have here a superclass called Animal which has three subclasses (Mammal, Bird, and Fish). At the bottom, we have concrete classes.
The little squares represent behavior. For example, the blue square indicates that an instance of a class with this behavior can swim.

Some animals share common behavior: A cat and a dove can both walk, but the cat cannot fly (except for Nyan Cat 😀).
These kinds of behavior are orthogonal to this classification, so we cannot implement these behavior in the superclasses.

If a class could have more than one superclass, it would be easy, we could create three other classes: Walker, Swimmer, Flyer. After that, we would just have to inherit Dove and Cat from the Walker class. But in Dart, every class (except for Object) has exactly one superclass.

Instead of inheriting from the Walker class, we could implement it, as it if was an interface, but we should have to implement the behavior in multiple classes, so it’s not a good solution.

We need a way of reusing a class’s code in multiple class hierarchies.
You know what? Mixins are exactly that:

Mixins are a way of reusing a class’s code in multiple class hierarchies.

dartlang.org

Described like that, it sounds easy 😁.

🔒 Constraints (as of Dart 3.0)

The mixin feature comes with a few restrictions (from dartlang.org):

  • Mixins cannot declare a constructor
  • Mixins can’t extend classes or other mixins

📝 Syntax

We saw how mixins can be useful, let’s see how to create and use them.

To create a mixin we have to declare it using the mixin modifier:

mixin Walker {
void walk() {
print("I'm walking");
}
}

To use a mixin, use the with keyword followed by one or more mixin name:

class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}

Defining the Walker mixin on the Cat class, allows us to call the walk method but not the fly method (defined in Flyer).

main(List<String> arguments) {
Cat cat = Cat();
Dove dove = Dove();

// A cat can walk.
cat.walk();

// A dove can walk and fly.
dove.walk();
dove.fly();

// A normal cat cannot fly.
// cat.fly(); // Uncommenting this does not compile.
}

🔎 Details

I told you I found this concept somewhat difficult to understand, but so far it’s not so difficult, is it?
Well, can you tell what the output of the following program is 😵?

mixin A {
String getMessage() => 'A';
}

mixin B {
String getMessage() => 'B';
}

class P {
String getMessage() => 'P';
}

class AB extends P with A, B {}

class BA extends P with B, A {}

void main() {
String result = '';

final ab = AB();
result += ab.getMessage();

final ba = BA();
result += ba.getMessage();

print(result);
}

Both, AB and BA classes extend the P class and apply A and B mixins but in a different order. Both A, B mixins and the P classe have a method called getMessage.

First, we call the getMessage method of the AB class, then the getMessage method of the BA class.

So, what do you think the resulting output will be?
I’m giving you five propositions:

A. It does not compile
B. BA
C. AB
D. BAAB
E. ABBA

🥁🥁🥁The answer is the proposition B! The program prints BA.

I think you guessed that the order in which the mixins are declared is very important.

Why? How is it working?

✨ Linearization

When you apply a mixin to a class, keep in mind this:

Mixins in Dart work by creating a new class that layers the implementation of the mixin on top of a superclass to create a new class — it is not “on the side” but “on top” of the superclass, so there is no ambiguity in how to resolve lookups.

Lasse R. H. Nielsen on StackOverflow.

In fact, the code

class AB extends P with A, B {}

class BA extends P with B, A {}

is semantically equivalent to

class PA = P with A;
class PAB = PA with B;

class AB extends PAB {}

class PB = P with B;
class PBA = PB with A;

class BA extends PBA {}

The final inheritance diagram can be represented like this:

New classes are created between AB and P. These new classes are a mix-in between the superclass P and the mixins A and B.

As you can see, there is no multiple inheritance in there!

Mixins is not a way to get multiple inheritance in the classical sense. Mixins is a way to abstract and reuse a family of operations and state. It is similar to the reuse you get from extending a class, but it is compatible with single-inheritance because it is linear.

Lasse R. H. Nielsen on StackOverflow.

One important thing to remember is that the order in which mixins are declared represents the inheritance chain, from the top superclass to the bottom one.

📄 Types

What is the type of a mixin application instance? In general, it is a subtype of its superclass, and also a subtype of the type denoted by the mixin name itself, that is, the type of the original class.

— dartlang.org

So it means that this program

mixin A {
String getMessage() => 'A';
}

mixin B {
String getMessage() => 'B';
}

class P {
String getMessage() => 'P';
}

class AB extends P with A, B {}

class BA extends P with B, A {}

void main() {
AB ab = AB();
print(ab is P);
print(ab is A);
print(ab is B);

BA ba = BA();
print(ba is P);
print(ba is A);
print(ba is B);
}

will print six lines with true in the console.

Detailed explanation

Lasse R. H. Nielsen gave me a great explanation:

Since each mixin application creates a new class, it also creates a new interface (because all Dart classes also define interfaces). As described, the new class extends the superclass and includes copies of the mixin class members, but it also implements the mixin class interface.

In most cases, there is no way to refer to that mixin-application class or its interface; the class for Super with Mixin is just an anonymous superclass of the class declared like class C extends Super with Mixin {}. If you name a mixin application like class CSuper = Super with Mixin {}, then you can refer to the mixin application class and its interface, and it will be a sub-type of both Super and Mixin.

— Lasse R. H. Nielsen

🎖 Specialized mixins

What if you would want to be able to invoke a method defined in a superclass within a mixin?

This is possible with the on clause:

abstract class Super {
void method() {
print("Super");
}
}

class MySuper implements Super {
@override
void method() {
print("MySuper");
}
}

mixin Mixin on Super {
@override
void method() {
super.method();
print("Sub");
}
}

class Client extends MySuper with Mixin {}

void main() {
Client().method();
}

The mixin declaration mixin Mixin on Super indicates a superclass constraint on Super. It means that in order to apply this mixin to a class, this class must extend or implementSuper because the mixin uses a feature provided by Super.

The output of this program would be:

MySuper
Sub

If you wonder why, keep in mind how mixins are linearized:

In fact, the call to super.method() on line 17 actually calls the method declared on line 9.

🙄 When to use mixins?

Mixins are very helpful when we want to share a behavior across multiple classes that don’t share the same class hierarchy, or when it doesn’t make sense to implement such a behavior in a superclass.

It’s typically the case for serialization (Take a look at jaguar_serializer for example) or persistence. But you can also use mixins to provide some utility functions (like the RenderSliverHelpers in Flutter).

Take a time to play with this feature, and I’m sure you’ll find new use cases 😉. Don’t restrict yourself to stateless mixins, you can absolutely store variables and use them!

🐬Complete Animal example

You can find below, the complete Animals example with which I introduced the mixins:

abstract class Animal {}

abstract class Mammal extends Animal {}

abstract class Bird extends Animal {}

abstract class Fish extends Animal {}

mixin Walker {
void walk() {
print("I'm walking");
}
}

mixin Swimmer {
void swim() {
print("I'm swimming");
}
}

mixin Flyer {
void fly() {
print("I'm flying");
}
}

class Dolphin extends Mammal with Swimmer {}

class Bat extends Mammal with Walker, Flyer {}

class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}

class Duck extends Bird with Walker, Swimmer, Flyer {}

class Shark extends Fish with Swimmer {}

class FlyingFish extends Fish with Swimmer, Flyer {}

We can see below how these mixins are linearized:

📗 Conclusion

We saw that mixins are a powerful concept that allows you to reuse code across multiple class hierarchies.
Flutter uses this feature a lot, and I found it important to understand it better, that’s why I shared my comprehension with you.

If you have any questions, feel free to post them 👇.

Any feedback is welcome 😃.

If you enjoyed this story, you can support me by 👏 this.
You can also follow me on Twitter.

Thanks to Jeroen Meijer for the proofreading.

--

--