ZetCode

Dart Mixins

last modified October 18, 2026

In this article, we show how to use mixins in Dart language. Mixins are a way to reuse code across multiple class hierarchies without using inheritance. They allow you to add functionality to a class without creating a full parent-child relationship.

Mixins are particularly useful when you want to share behavior between classes that don't share a common ancestor. They are declared using the mixin keyword and can be added to a class using the with keyword. A class can use several mixins, but it can extend only one class.

Dart Mixins Simple Example

The following example demonstrates how to create and use a simple mixin in Dart.

main.dart
mixin Logging {
  void log(String message) {
    print('Log: $message');
  }
}

class User with Logging {
  String name;

  User(this.name);

  void greet() {
    log('User $name says hello!');
  }
}

void main() {
  final user = User('Alice');
  user.greet();
}

In this program, we define a mixin Logging that provides a log method. We then use this mixin in the User class to log a greeting message.

mixin Logging {
  void log(String message) {
    print('Log: $message');
  }
}

We define a mixin named Logging that contains a log method. A mixin cannot be instantiated and has no constructors.

class User with Logging {

We create a User class that uses the Logging mixin with the with keyword. The greet method uses the log method from the mixin as if it were declared in the class.

final user = User('Alice');
user.greet();

We create an instance of the User class and call the greet method, which logs a message.

$ dart main.dart
Log: User Alice says hello!

Dart Mixins with Multiple Classes

Mixins can be used with multiple classes to share common behavior. The classes do not have to be related. A class also becomes a subtype of the mixin, so the is operator works.

main.dart
mixin Logging {
  void log(String message) {
    print('Log: $message');
  }
}

class User with Logging {
  String name;

  User(this.name);

  void greet() {
    log('User $name says hello!');
  }
}

class Admin with Logging {
  String name;

  Admin(this.name);

  void announce() {
    log('Admin $name makes an announcement!');
  }
}

void main() {
  final user = User('Alice');
  user.greet();

  final admin = Admin('Bob');
  admin.announce();

  print(user is Logging);
  print(admin is User);
}

In this program, we use the Logging mixin in both the User and Admin classes to share logging functionality. The two classes are not related; the last line shows that an Admin is not a User, but both are Logging.

$ dart main.dart
Log: User Alice says hello!
Log: Admin Bob makes an announcement!
true
false

Dart Mixins with Overriding

A class can override the methods it gets from a mixin. The members of the class take precedence over the members of the mixin.

main.dart
mixin Logging {
  void log(String message) {
    print('Log: $message');
  }
}

class User with Logging {
  String name;

  User(this.name);

  @override
  void log(String message) {
    print('User Log: $message');
  }

  void greet() {
    log('User $name says hello!');
  }
}

void main() {
  final user = User('Alice');
  user.greet();
}

In this program, the User class overrides the log method from the Logging mixin.

@override
void log(String message) {
  print('User Log: $message');
}

The User class overrides the log method to provide custom logging behavior. The @override annotation documents the intent and lets the analyzer check it.

$ dart main.dart
User Log: User Alice says hello!

Dart Mixins with state

Mixins can declare fields too. Each class that uses the mixin gets its own copy of the fields.

main.dart
mixin Counter {
  int _count = 0;

  int get count => _count;

  void increment() => _count++;
}

class Clicker with Counter {}

class Visitor with Counter {}

void main() {
  final a = Clicker();
  final b = Visitor();

  a.increment();
  a.increment();
  b.increment();

  print(a.count);
  print(b.count);
}

The Counter mixin keeps the state in a private field. The two objects have independent counters.

$ dart main.dart
2
1

Dart Mixins with Constraints

Mixins can specify constraints using the on keyword to restrict their usage to subclasses of a specific class (or implementations of a type). The mixin can then use the members of that class, and call the members of the superclass with super.

main.dart
class Person {
  String name;

  Person(this.name);
}

mixin Logging on Person {
  void log(String message) {
    print('Log: $name says $message');
  }
}

class User extends Person with Logging {
  User(super.name);

  void greet() {
    log('Hello!');
  }
}

void main() {
  final user = User('Alice');
  user.greet();
}

In this program, the Logging mixin is constrained to be used only with classes that extend Person. Using it elsewhere is a compile-time error.

mixin Logging on Person {
  void log(String message) {
    print('Log: $name says $message');
  }
}

The Logging mixin uses the on keyword to specify that it can only be used with classes that extend Person. Thanks to the constraint, the mixin can access the name property.

class User extends Person with Logging {
  User(super.name);

The User class extends Person and uses the Logging mixin. The constructor passes the name to the superclass with a super parameter.

$ dart main.dart
Log: Alice says Hello!

Dart Mixins with multiple mixins

A class can use several mixins: class C with A, B. If the mixins define the same member, the last one wins. When the mixins call super, the calls are chained in the order of declaration (from right to left), which is called linearization.

main.dart
class Base {
  String describe() => 'Base';
}

mixin A on Base {
  @override
  String describe() => 'A -> ${super.describe()}';
}

mixin B on Base {
  @override
  String describe() => 'B -> ${super.describe()}';
}

class C extends Base with A, B {}

class D extends Base with B, A {}

void main() {
  print(C().describe());
  print(D().describe());
}

In C, the mixin B is applied last, so its method is called first and its super call reaches A, and then Base. Reversing the order in D reverses the chain.

$ dart main.dart
B -> A -> Base
A -> B -> Base

Dart mixin class

Since Dart 3, a declaration can be marked as both a class and a mixin with mixin class. It can be extended as a class and mixed in with with. A plain mixin can only be mixed in, and a plain class can no longer be used as a mixin (unless it is declared as mixin class).

main.dart
mixin class Walker {
  void walk() => print('walking');
}

class Dog with Walker {}

class Person extends Walker {}

void main() {
  Dog().walk();
  Person().walk();
  Walker().walk();
}

The Walker declaration can be used as a mixin (Dog), as a superclass (Person), and instantiated directly. A mixin class cannot have a constructor with parameters, nor an extends or with clause.

$ dart main.dart
walking
walking
walking

Source

Dart Mixins - Language Documentation

In this article, we have covered the basics of using mixins in Dart. Mixins are a powerful feature for code reuse and can be used to share behavior across multiple classes.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.