On this page26 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is a Mixin?
- 2. Declaring a Mixin and Using with
- 3. Mixins Can Have Fields
- 4. Several Mixins at Once
- 5. extends and with Together
- 6. A Mixin Is Also a Type
- 7. No Constructors, No Objects
- 8. Two Mixins, Same Method: the Last One Wins
- 9. The Whole Stack: Class, Mixins, Parent
- 10. Restricting a Mixin with on
- 11. super Inside a Mixin
- 12. mixin class: Both at Once
- 13. Inheritance, Interface, or Mixin?
- 14. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 64 — Mixins
| Category | 10 — Advanced OOP |
| Lecture | 064 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
A class can extend only one parent, and an interface hands over no code. A mixin is a bundle of fields and methods that many unrelated classes can plug in with
with.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.
Prerequisites
Students should already understand, from Lectures 052–063:
- classes, fields, methods, and constructors
extends, super parameters, andischecks- overriding with
@overrideand callingsuper.method() - abstract classes and
implements
Connection With Previous Lectures
In Lecture 059 Dart refused a class with two parents:
Error: Each class definition can have at most one extends clause.
Try choosing one superclass and define your class to implement (or mix in) the others.Lecture 063 covered the "implement" part: implements promises that a class has certain
methods, but the class must write every one of them itself. Now picture a Duck, a Fish
and a Swimmer robot. They share no sensible parent, yet all three should get the same
swim() code. Copying it three times is what classes were meant to stop. The error's last
words, "mix in", are the answer.
Learning Objectives
By the end of this lecture you will be able to:
- Declare a mixin with the
mixinkeyword - Add a mixin to a class with
with, alone or afterextends - Use fields and methods that come from a mixin
- Combine several mixins in one class
- Predict which method runs when two mixins define the same name
- Restrict a mixin to one family of classes with
on - Recognize why mixins have no constructors, and when to use
mixin class - Choose between inheritance, an interface, and a mixin
1. What Is a Mixin?
A mixin is a reusable piece of a class: fields and methods written once and added to any class that wants them. It is not a parent and you cannot create objects from it.
Analogy — think of toppings at an ice-cream shop. The ice cream is your class. Sprinkles and chocolate sauce are mixins: the same topping goes on any flavour, and one cup can have several.
2. Declaring a Mixin and Using with
Write mixin instead of class, then add it to a class with with:
mixin Swimmer {
void swim() {
print('Swimming');
}
}
class Duck with Swimmer {}
void main() {
Duck().swim();
}SwimmingDuck has no body at all, yet it has swim(). The mixin's code is copied into the class.
3. Mixins Can Have Fields
A mixin may declare fields as well as methods. Each object gets its own copy:
mixin Counter {
int count = 0;
void increment() {
count++;
}
}
class Clicker with Counter {}
void main() {
var a = Clicker();
var b = Clicker();
a.increment();
a.increment();
b.increment();
print('a: ${a.count}, b: ${b.count}');
}a: 2, b: 14. Several Mixins at Once
List as many mixins as you need after with, separated by commas:
mixin Swimmer {
void swim() {
print('Swimming');
}
}
mixin Flyer {
void fly() {
print('Flying');
}
}
class Duck with Swimmer, Flyer {}
void main() {
var duck = Duck();
duck.swim();
duck.fly();
}Swimming
FlyingThis is how a Dart class collects behaviour from several places while keeping one parent.
5. extends and with Together
A class can have a parent and mixins. extends always comes first:
class Animal {
Animal(this.name);
final String name;
void eat() {
print('$name is eating');
}
}
mixin Swimmer {
void swim() {
print('Swimming');
}
}
class Fish extends Animal with Swimmer {
Fish(super.name);
}
void main() {
var fish = Fish('Nemo');
fish.eat();
fish.swim();
}Nemo is eating
SwimmingRead it as "a Fish is an Animal, with the ability to swim".
6. A Mixin Is Also a Type
An object of a class that uses a mixin passes an is check for that mixin:
mixin Swimmer {
void swim() {
print('Swimming');
}
}
class Animal {}
class Duck extends Animal with Swimmer {}
void main() {
Object duck = Duck();
print(duck is Animal);
print(duck is Swimmer);
if (duck is Swimmer) {
duck.swim();
}
}true
true
Swimming7. No Constructors, No Objects
A mixin is a piece, not a whole class, so it cannot have a constructor:
mixin Swimmer {
Swimmer() {
print('Ready to swim');
}
}Error: Mixins can't declare constructors.
Swimmer() {
^^^^^^^And without a constructor, you cannot create a Swimmer object:
mixin Swimmer {}
void main() {
var s = Swimmer();
}Error: Couldn't find constructor 'Swimmer'.
var s = Swimmer();
^^^^^^^Give mixin fields a starting value (like int count = 0;) instead. Constructors belong to
the class that uses the mixin.
8. Two Mixins, Same Method: the Last One Wins
What if two mixins both define speak()? Dart stacks them in the order you list them, and
each new layer covers the one below. The mixin listed last wins:
mixin Loud {
void speak() {
print('LOUD');
}
}
mixin Quiet {
void speak() {
print('quiet');
}
}
class A with Loud, Quiet {}
class B with Quiet, Loud {}
void main() {
A().speak();
B().speak();
}quiet
LOUDSame two mixins, opposite order, opposite result. This stacking order is called
linearization: class A with Loud, Quiet is built as Object → Loud → Quiet → A.
9. The Whole Stack: Class, Mixins, Parent
The class's own method sits on top of everything. Below it come the mixins (last listed first), and the parent is at the bottom:
class Base {
void hello() {
print('Base');
}
}
mixin M1 {
void hello() {
print('M1');
}
}
mixin M2 {
void hello() {
print('M2');
}
}
class Child extends Base with M1, M2 {
@override
void hello() {
print('Child');
}
}
class Plain extends Base with M1, M2 {}
void main() {
Child().hello();
Plain().hello();
}Child
M2Dart searches from the top down: Child → M2 → M1 → Base, and runs the first hello()
it finds.
10. Restricting a Mixin with on
Sometimes a mixin needs fields from a particular class. on Account means "this mixin can
only be used by classes that extend Account", so the mixin may use balance freely:
class Account {
Account(this.owner, this.balance);
final String owner;
double balance;
}
mixin Interest on Account {
void addInterest(double rate) {
balance += balance * rate;
}
}
class SavingsAccount extends Account with Interest {
SavingsAccount(super.owner, super.balance);
}
void main() {
var account = SavingsAccount('Sara', 1000);
account.addInterest(0.05);
print('${account.owner}: ${account.balance}');
}Sara: 1050.0A class that is not an Account is rejected:
class Wallet with Interest {}Error: 'Object' doesn't implement 'Account' so it can't be used with 'Interest'.Wallet has no parent, so its parent is Object, which has no balance. (Dart also prints
a second error about the missing balance; both have the same cause.)
11. super Inside a Mixin
A mixin with on may override a method and call super. Because of linearization, super
means "the next layer down", which may be another mixin:
class Base {
void log() {
print('Base');
}
}
mixin A on Base {
@override
void log() {
print('A');
super.log();
}
}
mixin B on Base {
@override
void log() {
print('B');
super.log();
}
}
class Item extends Base with A, B {}
void main() {
Item().log();
}B
A
BaseB is on top, so it runs first; its super is A, and A's super is Base.
12. mixin class: Both at Once
Since Dart 3, an ordinary class cannot be used after with:
class Logger {
void log(String message) {
print('[LOG] $message');
}
}
class Shop with Logger {}Error: The class 'Logger' can't be used as a mixin because it isn't a mixin class nor a mixin.
class Shop with Logger {}
^If you want something usable both as a normal class and as a mixin, declare it as a
mixin class:
mixin class Logger {
void log(String message) {
print('[LOG] $message');
}
}
class Shop with Logger {}
void main() {
Logger().log('used on its own');
Shop().log('used as a mixin');
}[LOG] used on its own
[LOG] used as a mixinA mixin class follows both sets of rules, so it cannot use on or declare a constructor
with parameters. Use a plain mixin unless you really need objects of it.
13. Inheritance, Interface, or Mixin?
| Tool | Says | Shares code? | How many? |
|---|---|---|---|
extends | "B is an A" | yes | one |
implements | "B promises A's methods" | no — B writes them | many |
with | "B can do what the mixin does" | yes | many |
Ask "is it a kind of…?" for extends, "must it look like…?" for implements, and "should it
also be able to…?" for with. A Fish is an Animal (extends), and it can swim (with).
Choosing between inheritance and plain fields is the topic of Lecture 066.
14. A Real-World Example
class Account {
Account(this.owner, this.balance);
final String owner;
double balance;
}
mixin History {
final List<String> history = [];
void record(String entry) {
history.add(entry);
}
}
mixin Interest on Account {
void addInterest(double rate) {
balance += balance * rate;
}
}
class SavingsAccount extends Account with History, Interest {
SavingsAccount(super.owner, super.balance);
void deposit(double amount) {
balance += amount;
record('Deposit $amount');
}
@override
void addInterest(double rate) {
super.addInterest(rate);
record('Interest ${rate * 100}%');
}
}
void main() {
var account = SavingsAccount('Ali', 1000);
account.deposit(500);
account.addInterest(0.1);
print('${account.owner}: ${account.balance}');
print(account.history);
}Ali: 1650.0
[Deposit 500.0, Interest 10.0%]SavingsAccount is an Account, gets a history list from one mixin and interest from
another, and builds on the mixin's addInterest with super.
Code Examples
Example 1 — A Basic Mixin
mixin Greeter {
void greet() {
print('Hello!');
}
}
class Robot with Greeter {}
void main() {
Robot().greet();
}Hello!Example 2 — A Mixin Field
mixin Likeable {
int likes = 0;
void like() {
likes++;
}
}
class Post with Likeable {}
void main() {
var post = Post();
post.like();
post.like();
post.like();
print('Likes: ${post.likes}');
}Likes: 3Example 3 — Two Mixins
mixin Printer {
void printPage() {
print('Printing');
}
}
mixin Scanner {
void scan() {
print('Scanning');
}
}
class OfficeMachine with Printer, Scanner {}
void main() {
var machine = OfficeMachine();
machine.printPage();
machine.scan();
}Printing
ScanningExample 4 — Parent Plus Mixin
class Vehicle {
Vehicle(this.model);
final String model;
}
mixin Electric {
int battery = 100;
void drive() {
battery -= 20;
}
}
class Tesla extends Vehicle with Electric {
Tesla(super.model);
}
void main() {
var car = Tesla('Model 3');
car.drive();
print('${car.model}: ${car.battery}%');
}Model 3: 80%Example 5 — Last Mixin Wins
mixin Dark {
String get theme => 'dark';
}
mixin Light {
String get theme => 'light';
}
class App with Dark, Light {}
void main() {
print(App().theme);
}lightGetters stack exactly like methods.
Example 6 — on a Class
class Car {
int speed = 100;
}
mixin Turbo on Car {
void boost() {
speed += 50;
}
}
class RaceCar extends Car with Turbo {}
void main() {
var car = RaceCar();
car.boost();
print('Speed: ${car.speed}');
}Speed: 150Example 7 — Student Marks
mixin Grading {
String gradeFor(int marks) {
if (marks >= 80) return 'A';
if (marks >= 60) return 'B';
return 'C';
}
}
class Student with Grading {
Student(this.name, this.marks);
final String name;
final int marks;
void report() {
print('$name: $marks (${gradeFor(marks)})');
}
}
void main() {
Student('Sara', 92).report();
Student('Zayan', 65).report();
}Sara: 92 (A)
Zayan: 65 (B)Example 8 — Shopping Total
class Cart {
Cart(this.subtotal);
final double subtotal;
}
mixin Discount on Cart {
double totalAfter(double rate) => subtotal - subtotal * rate;
}
class SaleCart extends Cart with Discount {
SaleCart(super.subtotal);
}
void main() {
print('Total: ${SaleCart(2000).totalAfter(0.25)}');
}Total: 1500.0Example 9 — Bank Balance History
mixin History {
final List<String> history = [];
void record(String entry) {
history.add(entry);
}
}
class Account with History {
double balance = 1000;
void withdraw(double amount) {
balance -= amount;
record('Withdraw $amount');
}
}
void main() {
var account = Account();
account.withdraw(200);
account.withdraw(100);
print('Balance: ${account.balance}');
print(account.history);
}Balance: 700.0
[Withdraw 200.0, Withdraw 100.0]Example 10 — Temperature in Fahrenheit
class Thermometer {
Thermometer(this.celsius);
final double celsius;
}
mixin Fahrenheit on Thermometer {
double get fahrenheit => celsius * 9 / 5 + 32;
}
class SmartThermometer extends Thermometer with Fahrenheit {
SmartThermometer(super.celsius);
}
void main() {
var t = SmartThermometer(25);
print('${t.celsius} C = ${t.fahrenheit} F');
}25.0 C = 77.0 FCommon Beginner Mistakes
1. Giving a mixin a constructor
mixin Counter {
// int count;
// Counter(this.count); // ✗
int count = 0; // ✓
}Error: Mixins can't declare constructors.2. Using extends with a mixin
mixin Swimmer {}
// class Duck extends Swimmer {} // ✗
class Duck with Swimmer {} // ✓Error: The superclass, 'Swimmer', has no unnamed constructor that takes no arguments.A mixin has no constructor at all, so it can never be a parent.
3. Putting with before extends
// class Duck with Swimmer extends Animal {} // ✗
class Duck extends Animal with Swimmer {} // ✓Error: The extends clause must be before the with clause.
Try moving the extends clause before the with clause.4. Mixing in an ordinary class
// class Logger { ... } // ✗ with a plain class
mixin Logger {
void log(String message) => print(message);
}Error: The class 'Logger' can't be used as a mixin because it isn't a mixin class nor a mixin.Declare it as mixin (or mixin class if you also need Logger() objects).
5. Ignoring the on restriction
// class Wallet with Interest {} // ✗ Interest is "on Account"
class Wallet extends Account with Interest {} // ✓Error: 'Object' doesn't implement 'Account' so it can't be used with 'Interest'.6. Forgetting that order matters
with Loud, Quiet and with Quiet, Loud are different classes when both mixins share a
method name. The last one listed wins.
Best Practices
- Name mixins after an ability:
Swimmer,Printable,History. - Keep each mixin small and focused on one job.
- Use
onwhen the mixin needs members of a particular class. - Avoid relying on two mixins defining the same name; if you must, list the winner last.
- Prefer
extendsfor "is a", and mixins for "can also do". - Use plain
mixin; reach formixin classonly when you also need objects of it.
Mini Challenge
Create a Device class with a name, and a mixin Rechargeable with battery = 100,
use(int amount) (lowers the battery) and recharge() (sets it back to 100). Create
Phone extends Device with Rechargeable, use it for 30 and then 25, print, recharge,
and print again.
Your program should print exactly:
Pixel: 45%
Pixel: 100%Try it yourself before opening the solution.
Show solution
class Device {
Device(this.name);
final String name;
}
mixin Rechargeable {
int battery = 100;
void use(int amount) {
battery -= amount;
}
void recharge() {
battery = 100;
}
}
class Phone extends Device with Rechargeable {
Phone(super.name);
}
void main() {
var phone = Phone('Pixel');
phone.use(30);
phone.use(25);
print('${phone.name}: ${phone.battery}%');
phone.recharge();
print('${phone.name}: ${phone.battery}%');
}Pixel: 45%
Pixel: 100%Practice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
- Create a mixin
Singerwithsing()printingLa la la. Use it in a classBirdand callsing(). - Create a mixin
Scorewith a fieldpoints = 0andaddPoints(int p). Use it inPlayer, add10and15, and printPoints: 25. - Create mixins
Reader(read()printsReading) andWriter(write()printsWriting). MakeEditoruse both and call both methods.
Application
-
Predict the output, then run it:
mixin X { void name() => print('X'); } mixin Y { void name() => print('Y'); } class Z with Y, X {} void main() { Z().name(); } -
Create
Person(this.name)and a mixinDriver on Personwithdrive()printingAli is driving(usename). CreateChauffeur extends Person with Driver. -
This does not compile. Change one word so that both lines in
mainwork:class Logger { void log(String m) => print('LOG: $m'); } class Shop with Logger {} void main() { Logger().log('one'); Shop().log('two'); } -
This does not compile. Fix the mixin so
Clicker().countprints0:mixin Counter { int count; Counter(this.count); } class Clicker with Counter {}
Challenge
-
Create
Product(this.name, this.price)and a mixinTaxable on Productwith a getterpriceWithTaxadding 10%. PrintBag: 1650.0for aTaxedProduct('Bag', 1500). -
Predict the output, then explain it in one sentence:
class Base { void run() => print('Base'); } mixin Fast on Base { @override void run() { print('Fast'); super.run(); } } mixin Safe on Base { @override void run() { print('Safe'); super.run(); } } class Car extends Base with Safe, Fast {} void main() { Car().run(); } -
Create
Account(this.owner, this.balance), a mixinHistory(a list plusrecord(String)), and a mixinFees on AccountwithchargeFee()that subtracts25.BusinessAccount extends Account with History, FeesoverrideschargeFee()to callsuper.chargeFee()and recordFee 25. Charge twice and printSara: 950.0and[Fee 25, Fee 25].
Practice Answers
Question 1 — Solution
mixin Singer {
void sing() {
print('La la la');
}
}
class Bird with Singer {}
void main() {
Bird().sing();
}La la laQuestion 2 — Solution
mixin Score {
int points = 0;
void addPoints(int p) {
points += p;
}
}
class Player with Score {}
void main() {
var player = Player();
player.addPoints(10);
player.addPoints(15);
print('Points: ${player.points}');
}Points: 25Question 3 — Solution
mixin Reader {
void read() {
print('Reading');
}
}
mixin Writer {
void write() {
print('Writing');
}
}
class Editor with Reader, Writer {}
void main() {
var editor = Editor();
editor.read();
editor.write();
}Reading
WritingQuestion 4 — Solution
XX is listed last, so its name() covers Y's.
Question 5 — Solution
class Person {
Person(this.name);
final String name;
}
mixin Driver on Person {
void drive() {
print('$name is driving');
}
}
class Chauffeur extends Person with Driver {
Chauffeur(super.name);
}
void main() {
Chauffeur('Ali').drive();
}Ali is drivingQuestion 6 — Solution
Make Logger a mixin class:
mixin class Logger {
void log(String m) => print('LOG: $m');
}
class Shop with Logger {}
void main() {
Logger().log('one');
Shop().log('two');
}LOG: one
LOG: twoQuestion 7 — Solution
Remove the constructor and give the field a starting value:
mixin Counter {
int count = 0;
}
class Clicker with Counter {}
void main() {
print(Clicker().count);
}0Question 8 — Solution
class Product {
Product(this.name, this.price);
final String name;
final double price;
}
mixin Taxable on Product {
double get priceWithTax => price + price / 10;
}
class TaxedProduct extends Product with Taxable {
TaxedProduct(super.name, super.price);
}
void main() {
var bag = TaxedProduct('Bag', 1500);
print('${bag.name}: ${bag.priceWithTax}');
}Bag: 1650.0price + price / 10 avoids the rounding surprise that price * 1.1 gives
(1650.0000000000002, see Lecture 009).
Question 9 — Solution
Fast
Safe
BaseFast is listed last, so it is on top; each super.run() steps one layer down, through
Safe to Base.
Question 10 — Solution
class Account {
Account(this.owner, this.balance);
final String owner;
double balance;
}
mixin History {
final List<String> history = [];
void record(String entry) {
history.add(entry);
}
}
mixin Fees on Account {
void chargeFee() {
balance -= 25;
}
}
class BusinessAccount extends Account with History, Fees {
BusinessAccount(super.owner, super.balance);
@override
void chargeFee() {
super.chargeFee();
record('Fee 25');
}
}
void main() {
var account = BusinessAccount('Sara', 1000);
account.chargeFee();
account.chargeFee();
print('${account.owner}: ${account.balance}');
print(account.history);
}Sara: 950.0
[Fee 25, Fee 25]Quick Revision
- Which keyword declares a mixin, and which one adds it to a class?
- Can a mixin have fields? Does each object get its own copy?
- Where does
withgo when a class also usesextends? - Two mixins define
speak(). Which one runs? - What does
on Accountallow a mixin to do, and what does it forbid? - Why can't you write
Swimmer()for a mixinSwimmer? - What is a
mixin classfor? - Which tool fits "is a", which "promises to look like", and which "can also do"?
Key Takeaways
- A mixin (
mixin Name { }) is reusable code — fields and methods — added withwith. - A class can use many mixins, and
extendscomes beforewith. - A class that uses a mixin passes an
ischeck for it. - Mixins have no constructors, so they cannot be created or extended.
- Mixins are stacked in order (linearization): the class's own member wins, then the last mixin listed, down to the parent.
- Inside a mixin,
supermeans the next layer down. on SomeClasslimits a mixin to subclasses ofSomeClassand lets it use their members.- Since Dart 3 only a
mixinormixin classcan followwith.
What's Next?
Lecture 65 — Polymorphism. You have now seen a Duck that is an Animal, a Swimmer and
a Flyer all at once. Next we will use that idea on purpose: one variable or list typed as a
parent, many different objects inside it, and each one responding in its own way.