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
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
| Features | extends | implements |
|---|---|---|
| Quantity limit | Can only inherit one | Can implement multiple |
| Method implementation | Inherits the parent class's implementation | Must implement all methods yourself |
| Constructor | Can call super() | Does not inherit constructors |
| Use Case | An "is-a" relationship | A 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
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
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
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
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