Mixins: Sharing Behavior Across Class Trees
So far you have seen two ways classes share code. Inheritance lets one class extend another and reuse its members. Abstract classes define a common shape that subclasses must fill in. Both have a limitation: a class can only have one parent. If you want a class to gain a small, reusable ability—say, "can describe itself as text"—inheritance forces awkward choices about where that ability lives in your family tree.
Mixins solve this. A mixin is a way to add a bundle of methods and fields to a class without making it the class's parent. Think of it like attaching a skill to a class. One class can attach several skills from several unrelated sources.
What exactly is a mixin?
A mixin is declared with the mixin keyword. Here is a mixin that gives any class the ability to describe itself:
mixin Describable {
String describe();
}
mixin Logger {
void log(String message) {
print('[log] $message');
}
}To use a mixin, a class combines it with with:
class Robot with Logger implements Describable {
final String name;
Robot(this.name);
@override
String describe() => 'A robot named $name';
}
void main() {
final r = Robot('Rex');
r.log(r.describe()); // [log] A robot named Rex
}The Robot class now has a log method it never wrote itself. So do any other classes that mix in Logger. The same mixin can be reused across completely unrelated class trees—a Robot, a BankAccount, and a GameCharacter can all mix in Logger even though they share no parent.
The full recipe
A class declaration can combine a parent and mixins in this order:
class Vehicle extends Machine with Logger implements Describable {
...
}Read it left to right: extends picks the one parent, with lists mixins, implements lists required shapes. Only the parent can be inherited from; mixins are added on top.
Show diagram source
classDiagram
class Container
class Logger
class Describable
class Storable
class Box
Container <|-- Box : extends
Logger -- Box : with
Describable -- Box : with
Storable -- Box : implementsRestricting who can use a mixin
Sometimes a mixin only makes sense for classes that already have certain members. For example, a mixin that renames things needs something with a name property. Dart lets a mixin state its requirements with on:
mixin Shouter on Object {
String get name;
void shout() {
print('${name.toUpperCase()}!!!');
}
}Here on Object means any class can use it, but the mixin may use name, so every class mixing it in must supply a name getter. If you write on Animal, only classes that extend (or mix in) Animal can use the mixin, and the mixin can call all of Animal's members.
Order matters when mixins overlap
If two mixins define the same method, the last one in the with list wins. It literally replaces the one before it:
mixin Quiet {
void speak() => print('...');
}
mixin Loud {
void speak() => print('LOUD!');
}
class Person with Quiet, Loud {}
class QuietPerson with Loud, Quiet {}
void main() {
Person().speak(); // LOUD!
QuietPerson().speak(); // ...
}This is worth remembering: mixin order is not decoration, it is behavior. When you read code with multiple mixins, check them right to left to see which versions of overlapping members survive.
Mixins can also be abstract
A mixin can mix concrete methods and abstract ones—methods it declares but does not define. Any class using the mixin must supply the abstract parts. That is what made Describable work earlier: it declared describe() with no body, and Robot provided it.
When to choose a mixin
Use this decision guide:
| Situation | Better choice |
|---|---|
| A true "is-a" relationship with shared state and identity | Inheritance (`extends`) |
| A small reusable ability used by unrelated classes | Mixin (`with`) |
| Just a contract, no code at all | Abstract class or `implements` |
| Adding behavior to a class you cannot modify | Mixin, if you own the hierarchy it restricts to |
A good rule of thumb: if you find yourself creating a base class that holds one method used by many unrelated subclasses, it probably wants to be a mixin.
Gotchas to keep in mind
- Hidden dependencies. A mixin that calls members it does not define (like
nameabove) creates an invisible requirement. Document those members, or useonto make the requirement explicit. - State in mixins. Mixins can hold fields, but shared mutable fields across many classes can cause surprising bugs. Keep mixins stateless when you can.
- Too many mixins. A class with six mixins is hard to reason about. Two or three that each do one clear job is a healthy ceiling.
- No constructors. Mixins cannot have constructors. They are attached, not built.
Where you will see mixins in real Dart
The standard library uses mixins heavily. Iterable helpers, Comparable-style classes, and many Flutter widgets (such as SingleTickerProviderStateMixin) are mixins. Learning to read a with clause is essential for reading real Dart and Flutter code.
Try it yourself
mixin Greeter {
String get name;
void greet() => print('Hello, $name!');
}
class Dog with Greeter {
@override
String get name => 'Rex';
}
void main() {
Dog().greet(); // Hello, Rex!
}Change name and watch the output change. Then add a second mixin that also defines greet and see which one wins.
Exercise
A class is declared as class C with A, B { } and both A and B define a method called run(). Which version does C use?
Show answer
The one from B, because the last mixin in the with list replaces earlier ones when they define the same member.
Exercise
Why would you write mixin Swimmer on Animal instead of just mixin Swimmer?
Show answer
The on clause restricts the mixin to classes related to Animal and lets the mixin use Animal's members. It makes the mixin's hidden requirements explicit and prevents misuse on unrelated classes.
Spotted a mistake, or code that does not work?