Watch the video (12 min)
On this page26 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. What Is a Mixin?
  5. 2. Declaring a Mixin and Using with
  6. 3. Mixins Can Have Fields
  7. 4. Several Mixins at Once
  8. 5. extends and with Together
  9. 6. A Mixin Is Also a Type
  10. 7. No Constructors, No Objects
  11. 8. Two Mixins, Same Method: the Last One Wins
  12. 9. The Whole Stack: Class, Mixins, Parent
  13. 10. Restricting a Mixin with on
  14. 11. super Inside a Mixin
  15. 12. mixin class: Both at Once
  16. 13. Inheritance, Interface, or Mixin?
  17. 14. A Real-World Example
  18. Code Examples
  19. Common Beginner Mistakes
  20. Best Practices
  21. Mini Challenge
  22. Practice Questions
  23. Practice Answers
  24. Quick Revision
  25. Key Takeaways
  26. What's Next?

Lecture 64 — Mixins

Category10 — Advanced OOP
Lecture064 of 140
Estimated video length12 min
TargetDart 3.12.2
LevelIntermediate

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, and is checks
  • overriding with @override and calling super.method()
  • abstract classes and implements

Connection With Previous Lectures

In Lecture 059 Dart refused a class with two parents:

output
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 mixin keyword
  • Add a mixin to a class with with, alone or after extends
  • 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:

dart
mixin Swimmer {
  void swim() {
    print('Swimming');
  }
}

class Duck with Swimmer {}

void main() {
  Duck().swim();
}
output
Swimming

Duck 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:

dart
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}');
}
output
a: 2, b: 1

4. Several Mixins at Once

List as many mixins as you need after with, separated by commas:

dart
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();
}
output
Swimming
Flying

This 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:

dart
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();
}
output
Nemo is eating
Swimming

Read 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:

dart
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();
  }
}
output
true
true
Swimming

7. No Constructors, No Objects

A mixin is a piece, not a whole class, so it cannot have a constructor:

dart
mixin Swimmer {
  Swimmer() {
    print('Ready to swim');
  }
}
output
Error: Mixins can't declare constructors.
  Swimmer() {
  ^^^^^^^

And without a constructor, you cannot create a Swimmer object:

dart
mixin Swimmer {}

void main() {
  var s = Swimmer();
}
output
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:

dart
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();
}
output
quiet
LOUD

Same 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:

dart
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();
}
output
Child
M2

Dart 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:

dart
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}');
}
output
Sara: 1050.0

A class that is not an Account is rejected:

dart
class Wallet with Interest {}
output
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:

dart
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();
}
output
B
A
Base

B 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:

dart
class Logger {
  void log(String message) {
    print('[LOG] $message');
  }
}

class Shop with Logger {}
output
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:

dart
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');
}
output
[LOG] used on its own
[LOG] used as a mixin

A 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?

ToolSaysShares code?How many?
extends"B is an A"yesone
implements"B promises A's methods"no — B writes themmany
with"B can do what the mixin does"yesmany

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

dart
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);
}
output
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

dart
mixin Greeter {
  void greet() {
    print('Hello!');
  }
}

class Robot with Greeter {}

void main() {
  Robot().greet();
}
output
Hello!

Example 2 — A Mixin Field

dart
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}');
}
output
Likes: 3

Example 3 — Two Mixins

dart
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();
}
output
Printing
Scanning

Example 4 — Parent Plus Mixin

dart
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}%');
}
output
Model 3: 80%

Example 5 — Last Mixin Wins

dart
mixin Dark {
  String get theme => 'dark';
}

mixin Light {
  String get theme => 'light';
}

class App with Dark, Light {}

void main() {
  print(App().theme);
}
output
light

Getters stack exactly like methods.

Example 6 — on a Class

dart
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}');
}
output
Speed: 150

Example 7 — Student Marks

dart
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();
}
output
Sara: 92 (A)
Zayan: 65 (B)

Example 8 — Shopping Total

dart
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)}');
}
output
Total: 1500.0

Example 9 — Bank Balance History

dart
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);
}
output
Balance: 700.0
[Withdraw 200.0, Withdraw 100.0]

Example 10 — Temperature in Fahrenheit

dart
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');
}
output
25.0 C = 77.0 F

Common Beginner Mistakes

1. Giving a mixin a constructor

dart
mixin Counter {
  // int count;
  // Counter(this.count); // ✗
  int count = 0; // ✓
}
output
Error: Mixins can't declare constructors.

2. Using extends with a mixin

dart
mixin Swimmer {}

// class Duck extends Swimmer {} // ✗
class Duck with Swimmer {} // ✓
output
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

dart
// class Duck with Swimmer extends Animal {} // ✗
class Duck extends Animal with Swimmer {} // ✓
output
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

dart
// class Logger { ... }      // ✗ with a plain class
mixin Logger {
  void log(String message) => print(message);
}
output
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

dart
// class Wallet with Interest {} // ✗ Interest is "on Account"
class Wallet extends Account with Interest {} // ✓
output
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 on when 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 extends for "is a", and mixins for "can also do".
  • Use plain mixin; reach for mixin class only 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:

output
Pixel: 45%
Pixel: 100%

Try it yourself before opening the solution.

Show solution
dart
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}%');
}
output
Pixel: 45%
Pixel: 100%

Practice Questions

Work through these in order — they run from beginner to application to challenge.

Beginner

  1. Create a mixin Singer with sing() printing La la la. Use it in a class Bird and call sing().
  2. Create a mixin Score with a field points = 0 and addPoints(int p). Use it in Player, add 10 and 15, and print Points: 25.
  3. Create mixins Reader (read() prints Reading) and Writer (write() prints Writing). Make Editor use both and call both methods.

Application

  1. Predict the output, then run it:

    dart
    mixin X {
      void name() => print('X');
    }
    
    mixin Y {
      void name() => print('Y');
    }
    
    class Z with Y, X {}
    
    void main() {
      Z().name();
    }
  2. Create Person(this.name) and a mixin Driver on Person with drive() printing Ali is driving (use name). Create Chauffeur extends Person with Driver.

  3. This does not compile. Change one word so that both lines in main work:

    dart
    class Logger {
      void log(String m) => print('LOG: $m');
    }
    
    class Shop with Logger {}
    
    void main() {
      Logger().log('one');
      Shop().log('two');
    }
  4. This does not compile. Fix the mixin so Clicker().count prints 0:

    dart
    mixin Counter {
      int count;
      Counter(this.count);
    }
    
    class Clicker with Counter {}

Challenge

  1. Create Product(this.name, this.price) and a mixin Taxable on Product with a getter priceWithTax adding 10%. Print Bag: 1650.0 for a TaxedProduct('Bag', 1500).

  2. Predict the output, then explain it in one sentence:

    dart
    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();
    }
  3. Create Account(this.owner, this.balance), a mixin History (a list plus record(String)), and a mixin Fees on Account with chargeFee() that subtracts 25. BusinessAccount extends Account with History, Fees overrides chargeFee() to call super.chargeFee() and record Fee 25. Charge twice and print Sara: 950.0 and [Fee 25, Fee 25].


Practice Answers

Question 1 — Solution

dart
mixin Singer {
  void sing() {
    print('La la la');
  }
}

class Bird with Singer {}

void main() {
  Bird().sing();
}
output
La la la

Question 2 — Solution

dart
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}');
}
output
Points: 25

Question 3 — Solution

dart
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();
}
output
Reading
Writing

Question 4 — Solution

output
X

X is listed last, so its name() covers Y's.

Question 5 — Solution

dart
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();
}
output
Ali is driving

Question 6 — Solution

Make Logger a mixin class:

dart
mixin class Logger {
  void log(String m) => print('LOG: $m');
}

class Shop with Logger {}

void main() {
  Logger().log('one');
  Shop().log('two');
}
output
LOG: one
LOG: two

Question 7 — Solution

Remove the constructor and give the field a starting value:

dart
mixin Counter {
  int count = 0;
}

class Clicker with Counter {}

void main() {
  print(Clicker().count);
}
output
0

Question 8 — Solution

dart
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}');
}
output
Bag: 1650.0

price + price / 10 avoids the rounding surprise that price * 1.1 gives (1650.0000000000002, see Lecture 009).


Question 9 — Solution

output
Fast
Safe
Base

Fast is listed last, so it is on top; each super.run() steps one layer down, through Safe to Base.

Question 10 — Solution

dart
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);
}
output
Sara: 950.0
[Fee 25, Fee 25]

Quick Revision

  1. Which keyword declares a mixin, and which one adds it to a class?
  2. Can a mixin have fields? Does each object get its own copy?
  3. Where does with go when a class also uses extends?
  4. Two mixins define speak(). Which one runs?
  5. What does on Account allow a mixin to do, and what does it forbid?
  6. Why can't you write Swimmer() for a mixin Swimmer?
  7. What is a mixin class for?
  8. 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 with with.
  • A class can use many mixins, and extends comes before with.
  • A class that uses a mixin passes an is check 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, super means the next layer down.
  • on SomeClass limits a mixin to subclasses of SomeClass and lets it use their members.
  • Since Dart 3 only a mixin or mixin class can follow with.

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.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.