Dart Interfaces and Mixins | Beginner's Tutorial
Although Dart only supports single inheritance, multiple code reuse can be flexibly achieved through interfaces and mixins.
This chapter introduces how to use implements to implement interfaces, code reuse with mixin, and the with keyword.
implements for Interfaces
In Dart, every class implicitly defines an interface.
Using the implements keyword allows a class to implement another class's interface, and multiple interfaces can be implemented.
Example
// Define a "printable" interface
class Printable {
// Methods in the interface have no default implementation
// Implementers must override all members
void printContent(){
// This method body is ignored when using implements
}
}
// 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('--- TUTORIAL print end ---');
}
// 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 beginner guide');
// Polymorphism: Document is both Printable and Savable
Printable printable = doc;
printable.printContent();
Savable savable = doc;
savable.save();
}
--- Document: Dart Tutorial ---
This is a Dart beginner guide
--- TUTORIAL print end ---
Document "Dart Tutorial" has been saved to disk
The key difference between
implementsandextends:extendsinherits all implementations (method bodies) from the parent class, whileimplementsonly inherits the interface signaturesβyou must re-implement all methods. Additionally,extendscan only inherit from one class, whileimplementscan implement multiple interfaces.
extends vs implements Comparison
| Feature | extends | implements |
|---|---|---|
| Quantity Limit | Can only inherit one | Can implement multiple |
| Method Implementation | Inherits parent's implementation | Must implement all methods yourself |
| Constructor | Can call super() | Does not inherit constructors |
| Use Case | "Is a" relationship | "Can do" contract |
Multiple Interface Implementation
Dart can implement multiple interfaces, which is the main way to break through the single inheritance limitation.
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 is flying in the sky');
}
@override
void swim(){
print('$name is swimming on the water');
}
@override
void walk(){
print('$name is waddling on land');
}
}
// A fish can only swim
class Fish implements Swimmable {
@override
void swim(){
print('The fish is swimming in the water');
}
}
void main(){
var duck = Duck('TUTORIAL Duck');
// 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 the fish fly? ${fish is Flyable}');
}
TUTORIAL Duck is flying in the sky
TUTORIAL Duck is swimming on the water
TUTORIAL Duck is waddling on land
Can the duck fly? true
Can the duck swim? true
Can the fish fly? false
Mixin Code Reuse
Mixin is a mechanism for reusing code across multiple classes. It solves the problem where "multiple classes need to share the same methods but inheritance is not suitable."
Use the mixin keyword to define a Mixin, and use the with keyword to mix it into a class.
Example
// Use mixin keyword to define
mixin Logger {
// Methods in 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 with keyword to mix in 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('tutorial');
var orderService = OrderService();
orderService.createOrder('ORD-001');
orderService.cancelOrder('ORD-001');
}
[2026-06-12 10:30:00.000] Creating user: tutorial
Creation time: 2026-06-12
[2026-06-12 10:30:00.001] Creating order: ORD-001
[ERROR 2026-06-12 10:30:00.001] Failed to cancel order: ORD-001
UserService and OrderService come from different business domains and are not suitable for sharing Logger through inheritance.
Through Mixin, they can both obtain logging functionality without affecting each other.
Mixin cannot have constructors, nor can it be instantiated. Its purpose is pure: to provide reusable methods without involving state initialization.
Mixin Restriction Conditions
Mixin can use the on keyword to restrict that it can only be used with 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 took flight!');
}
}
// Bird is an Animal, so it can use FlyableMixin
class Bird extends Animal with FlyableMixin {
Bird(String name): super(name);
}
// The following line will cause an error: Car is not an Animal, cannot use FlyableMixin
// class Car with FlyableMixin {} // Error!
void main(){
var bird = Bird('TUTORIAL Little Bird');
bird.fly();
// Methods in FlyableMixin can access Animal's property name
print('Bird name: ${bird.name}');
}
TUTORIAL Little Bird took flight!
Bird name: TUTORIAL Little Bird
Complete Combination of class, Mixin, extends, and implements
In Dart, a class can simultaneously use extends, with, and implements:
Example
mixin Jumpable {
void jump() => print('Jumped!');
}
mixin Runnable {
void run() => print('Running forward!');
}
class Animal {
void breathe() => print('Breathing...');
}
// Inherit Animal, mix in Jumpable and Runnable, implement 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('TUTORIAL 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"}');
}
Breathing...
Running forward!
Jumped!
TUTORIAL Player A vs Player B: A wins
Complete declaration order: class Subclass extends Parent with Mixin1, Mixin2 implements Interface1, Interface2
YouTip