What Are Mixins
A Mixin is a code reuse mechanism that lets you share code across unrelated class inheritance hierarchies. Its core purpose is to inject member implementations into classes in bulk. It resembles multiple inheritance, but it’s designed to attach modular feature sets to a class.
Using Mixins
Use the with keyword followed by one or more Mixin names.
class Musician extends Performer with Musical {
// Code here
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
Code language: Dart (dart)
Defining Mixins
Declare a mixin using the mixin keyword.
Note:
mixin class(Dart 3.0+) can function both as a regular class and a Mixin.
Mandatory Restrictions
mixin/mixin classcannot useextends- No generative constructors may be declared
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
class Musician with Musical {}
void main() {
var m = Musician();
m.canConduct = true;
m.entertainMe(); // Waving hands
}
Code language: Dart (dart)
Execution Output
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playingCode language: CSS (css)
Mixins Calling External Members
Methods / properties the Mixin itself cannot define
Mixins often need to invoke members they don’t implement themselves. Below are four standard approaches.
Declare Abstract Members Inside the Mixin
Define abstract methods within the Mixin. Every class using this Mixin must implement those abstract methods.
mixin Musician {
// Abstract method to be implemented by consumers
void playInstrument(String instrumentName);
void playPiano() {
playInstrument('Piano');
}
void playFlute() {
playInstrument('Flute');
}
}
// Consumers must override and implement
class Virtuoso with Musician {
@override
void playInstrument(String instrumentName) {
print('Plays the $instrumentName beautifully');
}
}
void main() {
var artist = Virtuoso();
artist.playPiano();
artist.playFlute();
}
Code language: Dart (dart)
Plays the Piano beautifully
Plays the Flute beautifully
Access Host Class State via Abstract Getters
The Mixin defines abstract getters to read fields belonging to the host class, enabling cross-class state access.
/// Can be mixed into any class with a name field, automatically supplying == and hashCode based on name
mixin NameIdentity {
String get name;
@override
int get hashCode => name.hashCode;
@override
bool operator ==(Object other) =>
other is NameIdentity && name == other.name;
}
class Person with NameIdentity {
final String name;
Person(this.name);
}
void main() {
var p1 = Person("Alice");
var p2 = Person("Alice");
var p3 = Person("Bob");
print(p1 == p2); // true
print(p1 == p3); // false
}
Code language: JavaScript (javascript)
Mixin Implementing Interfaces with implements
Use implements on a Mixin to enforce an interface contract, forcing mixing classes to implement all interface methods.
abstract interface class Tuner {
void tuneInstrument();
}
mixin Guitarist implements Tuner {
void playSong() {
tuneInstrument();
print('Strums guitar majestically.');
}
}
class PunkRocker with Guitarist {
@override
void tuneInstrument() {
print("Don't bother, being out of tune is punk rock.");
}
}
void main() {
PunkRocker().playSong();
}
Code language: JavaScript (javascript)
Don't bother, being out of tune is punk rock.
Strums guitar majestically.
on Clause: Restrict Mixins to Subclasses of Specific Parent Types
The on keyword limits usage: only subclasses inheriting from the specified type may apply this Mixin.
Additionally, the Mixin can invoke parent class members and call super.xxx().
Only use
onwhen you need to call parent methods viasuperinside the Mixin!
class Musician {
void musicianMethod() {
print('Playing music!');
}
}
// Can only be mixed into subclasses of Musician
mixin MusicalPerformer on Musician {
void performerMethod() {
print('Performing music!');
super.musicianMethod(); // Call parent methods constrained by on clause
}
}
// SingerDancer extends Musician so it can use MusicalPerformer
class SingerDancer extends Musician with MusicalPerformer {}
void main() {
SingerDancer().performerMethod();
}
Code language: Dart (dart)
Performing music!
Playing music!
SingerDancer mixes MusicalPerformer, so it gains access to the performerMethod defined inside it.
Mixing works in this fashion: a class imports all methods defined inside another Mixin and can use them directly.
⚠️ Incorrect Example:
If a class does not inherit
Musician, directly writingwith MusicalPerformertriggers a compile error.
Differences Between class / mixin / mixin class
mixin: Can only be applied viawith, cannot be inherited viaextendsclass: Supportsextends, cannot be used directly as a Mixinmixin class:- Can act as a Mixin using
with - Can also serve as a normal parent class via
extends
- Can act as a Mixin using
The same constraints apply:
- mixin / mixin class cannot declare extends or with
- class / mixin class cannot use on
mixin class Musician {
void play() {
print("Musician is playing");
}
}
// Used as Mixin
class Novice1 with Musician {}
// Used as regular parent class
class Novice2 extends Musician {}
void main() {
Novice1().play();
Novice2().play();
}
Code language: Dart (dart)
Running Result
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Musician is playing
Musician is playing
Code language: CSS (css)
Mixins