Dart Interfaces and Mixins

Although Dart only supports single inheritance, flexible multiple code reuse can be achieved through interfaces and Mixins.

This chapter introduces how to use implements for interfaces, mixin code reuse, and the with keyword.


implements: implement an interface

In Dart, every class implicitly defines an interface.

Using the implements keyword allows a class to implement another class's interface, and it can implement multiple interfaces.

Example

// Define a "printable" interface
class Printable {
  // Methods in an interface have no default implementation
  // Implementers must override all members
  void printContent() {
    // This method body is ignored when implements is used
  }
}

// Define a "savable" interface
class Savable {
  void save() {}
}

// Use implements to implement multiple interfaces
class Document implements Printable, Savable {
  String title;
  String content;

  Document(this.title, this.content);

  // Must implement all methods required by the Printable interface
  @override
  void printContent() {
    print('--- Document: $title ---');
    print(content);
    print('--- EXAMPLE printing finished ---');
  }

  // Must implement all methods required by the Savable interface
  @override
  void save() {
    print('Document "$title" has been saved to disk');
  }
}

void main() {
  var doc = Document(Dart Tutorial, 'This is a Dart introductory guide');

  // Polymorphism: Document is both Printable and Savable
  Printable printable = doc;
  printable.printContent();

  Savable savable = doc;
  savable.save();
}
--- 文档: Dart 教程 ---
这是一份 Dart 入门指南
--- EXAMPLE 打印结束 ---
文档 "Dart 教程" 已保存到磁盘

Key difference between implements and extends: extends inherits all implementations (method bodies) from the parent class, while implements only inherits the interface signature, requiring you to reimplement all methods. Additionally, extends can only inherit one class, while implements can implement multiple interfaces.

extends vs implements comparison

Featuresextendsimplements
Quantity limitCan only inherit oneCan implement multiple
Method implementationInherits the parent class's implementationMust implement all methods yourself
ConstructorCan call super()Does not inherit constructors
Use CaseAn "is-a" relationshipA contract of "what it can do"

Multiple interface implementation

Dart can implement multiple interfaces, which is the main way to break through the limitation of single inheritance.

Example

// Define multiple behavior interfaces
class Flyable {
  void fly() {}
}

class Swimmable {
  void swim() {}
}

class Walkable {
  void walk() {}
}

// A duck can fly, swim, and walk
class Duck implements Flyable, Swimmable, Walkable {
  String name;

  Duck(this.name);

  @override
  void fly() {
    print('$name flies in the sky');
  }

  @override
  void swim() {
    print('$name swims on the water');
  }

  @override
  void walk() {
    print('$name waddles on land');
  }
}

// A fish can only swim
class Fish implements Swimmable {
  @override
  void swim() {
    print('The fish swims in the water');
  }
}

void main() {
  var duck = Duck('EXAMPLE Duck');

  // A duck can play multiple roles
  (duck as Flyable).fly();
  (duck as Swimmable).swim();
  (duck as Walkable).walk();

  // Type check
  print('Can the duck fly? ${duck is Flyable}');
  print('Can the duck swim? ${duck is Swimmable}');

  var fish = Fish();
  print('Can a fish fly? ${fish is Flyable}');
}
EXAMPLE 鸭 在天空中飞翔
EXAMPLE 鸭 在水面上游泳
EXAMPLE 鸭 在陆地上摇摇晃晃地走
鸭子能飞吗? true
鸭子能游吗? true
鱼能飞吗? false

Mixin code reuse

Mixin is a mechanism for reusing code among multiple classes, solving the problem where "multiple classes need to share the same methods but are not suitable for inheritance."

Use the mixin keyword to define a Mixin, and use the with keyword to mix it into a class.

Example

// Using the mixin keyword to define
mixin Logger {
  // Methods in a Mixin can be reused by multiple classes
  void log(String message) {
    print('[${DateTime.now()}] $message');
  }

  void logError(String message) {
    print('[ERROR ${DateTime.now()}] $message');
  }
}

mixin TimestampFormatter {
  String formatTimestamp(DateTime dt) {
    return '${dt.year}-${dt.month.toString().padLeft(2, '0')}'
        '-${dt.day.toString().padLeft(2, '0')}';
  }
}

// Use the with keyword to mix in a Mixin
class UserService with Logger, TimestampFormatter {
  void createUser(String name) {
    log('Creating user: $name');
    var now = DateTime.now();
    print('Creation time: ${formatTimestamp(now)}');
  }
}

class OrderService with Logger {
  void createOrder(String orderId) {
    log('Creating order: $orderId');
  }

  void cancelOrder(String orderId) {
    logError('Failed to cancel order: $orderId');
  }
}

void main() {
  var userService = UserService();
  userService.createUser('example');

  var orderService = OrderService();
  orderService.createOrder('ORD-001');
  orderService.cancelOrder('ORD-001');
}
[2026-06-12 10:30:00.000] 创建用户: example
创建时间: 2026-06-12
[2026-06-12 10:30:00.001] 创建订单: ORD-001
[ERROR 2026-06-12 10:30:00.001] 取消订单失败: ORD-001

UserService and OrderService come from different business domains, so they are not suitable for sharing Logger through inheritance.

Through Mixin, they can both gain logging functionality without affecting each other.

Mixins cannot have constructors and cannot be instantiated. Their role is purely to provide reusable methods without involving state initialization.

Mixin constraints

Mixin can use the on keyword to restrict it to be used only by specific types of classes.

Example

// Base class
class Animal {
  String name;
  Animal(this.name);
}

// This Mixin can only be mixed into Animal and its subclasses
mixin FlyableMixin on Animal {
  void fly() {
    print('$name is flying!');
  }
}

// Bird is an Animal, so it can use FlyableMixin
class Bird extends Animal with FlyableMixin {
  Bird(String name) : super(name);
}

// The following line will error: Car is not an Animal, so it cannot use FlyableMixin
// class Car with FlyableMixin {} // Error!

void main() {
  var bird = Bird('EXAMPLE Little Bird');
  bird.fly();

  // Methods in FlyableMixin can access the Animal's property name
  print('Little bird's name: ${bird.name}');
}
EXAMPLE 小鸟 飞起来了!
小鸟的名字: EXAMPLE 小鸟

Complete combination of class, Mixin, extends, and implements

In Dart, a class can use extends, with, and implements at the same time:

Example

mixin Jumpable {
  void jump() => print('Jump up!');
}

mixin Runnable {
  void run() => print('Run forward!');
}

class Animal {
  void breathe() => print('Breathing...');
}

// Inherits Animal, mixes in Jumpable and Runnable, implements Comparable
class Athlete extends Animal
    with Jumpable, Runnable
    implements Comparable<Athlete> {
  String name;
  int score;

  Athlete(this.name, this.score);

  @override
  int compareTo(Athlete other) => score.compareTo(other.score);

  void showSkills() {
    breathe();
    run();
    jump();
  }
}

void main() {
  var a1 = Athlete('EXAMPLE Player A', 95);
  var a2 = Athlete('Player B', 88);

  a1.showSkills();
  print('${a1.name} vs ${a2.name}: ${a1.compareTo(a2) > 0 ? "A wins" : "B wins"}');
}
呼吸...
跑步前进!
跳起来!
EXAMPLE 选手A vs 选手B: A 胜

Complete declaration order:class Subclass extends Superclass with Mixin1, Mixin2 implements Interface1, Interface2

other extensions