Dart Mixins
Reusing code across multiple classes
🔄 What are Mixins?
Mixins in Dart allow you to reuse code across multiple classes without inheritance. They provide a way to share functionality between classes that don't have a natural parent-child relationship.
// Mixin definition
mixin Flyable {
void fly() => print('Flying high in the sky!');
}
// Using mixin with 'with' keyword
class Bird with Flyable {
void chirp() => print('Chirp chirp!');
}
Key Mixin Concepts
Code Reuse
Share methods across multiple classes
mixin Swimmable {
void swim() => print('Swimming!');
}
Multiple Mixins
Use multiple mixins in one class
class Duck with Flyable, Swimmable {}
No Instantiation
Mixins cannot be instantiated directly
// Flyable f = Flyable(); // Error!
On Constraint
Restrict mixin usage to specific types
mixin Flyable on Animal {}
🔹 Basic Mixin Example
Here's how to create and use a simple mixin:
// Define mixins
mixin Walkable {
void walk() => print('Walking on legs');
}
mixin Swimmable {
void swim() => print('Swimming in water');
}
mixin Flyable {
void fly() => print('Flying in the air');
}
// Use mixins in classes
class Human with Walkable, Swimmable {
void speak() => print('Hello!');
}
class Fish with Swimmable {
void breatheUnderwater() => print('Breathing through gills');
}
class Bird with Walkable, Flyable {
void chirp() => print('Tweet tweet!');
}
void main() {
Human human = Human();
human.walk(); // Walking on legs
human.swim(); // Swimming in water
human.speak(); // Hello!
Fish fish = Fish();
fish.swim(); // Swimming in water
fish.breatheUnderwater(); // Breathing through gills
Bird bird = Bird();
bird.walk(); // Walking on legs
bird.fly(); // Flying in the air
bird.chirp(); // Tweet tweet!
}
Output:
Walking on legs
Swimming in water
Hello!
Swimming in water
Breathing through gills
Walking on legs
Flying in the air
Tweet tweet!
🔹 Mixins with Properties
Mixins can also contain properties and getters:
mixin Identifiable {
String _id = '';
String get id => _id;
void setId(String newId) {
_id = newId;
}
void displayId() {
print('ID: $_id');
}
}
mixin Timestamped {
DateTime? _createdAt;
DateTime? get createdAt => _createdAt;
void markCreated() {
_createdAt = DateTime.now();
}
void showTimestamp() {
if (_createdAt != null) {
print('Created at: $_createdAt');
}
}
}
class User with Identifiable, Timestamped {
String name;
User(this.name) {
setId('USER_${name.toUpperCase()}');
markCreated();
}
void introduce() {
print('Hi, I am $name');
displayId();
showTimestamp();
}
}
void main() {
User user = User('Alice');
user.introduce();
// Hi, I am Alice
// ID: USER_ALICE
// Created at: 2024-01-15 10:30:45.123456
}
Output:
Hi, I am Alice
ID: USER_ALICE
Created at: 2024-01-15 10:30:45.123456
🔹 Mixin with 'on' Constraint
Use 'on' to restrict which classes can use a mixin:
// Base class
class Animal {
String name;
Animal(this.name);
void eat() => print('$name is eating');
}
// Mixin with constraint - can only be used on Animal or its subclasses
mixin Domesticated on Animal {
String? owner;
void setOwner(String ownerName) {
owner = ownerName;
}
void showOwnership() {
if (owner != null) {
print('$name belongs to $owner');
} else {
print('$name has no owner');
}
}
}
class Dog extends Animal with Domesticated {
Dog(String name) : super(name);
void bark() => print('$name says Woof!');
}
class Cat extends Animal with Domesticated {
Cat(String name) : super(name);
void meow() => print('$name says Meow!');
}
void main() {
Dog dog = Dog('Buddy');
dog.setOwner('John');
dog.bark(); // Buddy says Woof!
dog.showOwnership(); // Buddy belongs to John
Cat cat = Cat('Whiskers');
cat.meow(); // Whiskers says Meow!
cat.showOwnership(); // Whiskers has no owner
}
Output:
Buddy says Woof!
Buddy belongs to John
Whiskers says Meow!
Whiskers has no owner