Skip to main content
A mixin is a partial class definition that enables the reuse of a class’s code across multiple class hierarchies. It allows the injection of fields and methods into a class without establishing a strict parent-child inheritance relationship, effectively simulating multiple inheritance via a linearized inheritance chain.

Declaration Syntax

Mixins are defined using the mixin keyword. A mixin declaration resembles a class declaration but enforces specific restrictions:
  1. It cannot declare a constructor.
  2. It cannot be instantiated directly.
mixin Logger {
  String _logPrefix = 'LOG';

  void log(String message) {
    print('$_logPrefix: $message');
  }
}

Application Syntax

To apply a mixin to a class, use the with keyword followed by one or more mixin identifiers. This composes the mixin’s members into the target class.
class SystemBase {
  void init() => print('System initialized');
}

class Server extends SystemBase with Logger {
  void start() {
    init();
    log('Server starting...'); // Accesses Logger.log
  }
}

Superclass Constraints

The on keyword restricts which classes can use a specific mixin. This declares a superclass constraint, mandating that the class applying the mixin must have a superclass that is a subtype of the specified type. This constraint is satisfied if the superclass either extends or implements the specified type. This ensures that the mixin can safely invoke methods defined in the constrained type via super, as the compiler guarantees their existence in the inheritance chain.
abstract class Performer {
  void perform();
}

// Dancer can only be mixed into classes where the superclass is a subtype of Performer
mixin Dancer on Performer {
  void pirouette() {
    // Valid call because 'on Performer' guarantees perform() exists on super
    super.perform(); 
    print('Spinning...');
  }
}

class BasePerformer implements Performer {
  @override
  void perform() => print('Performing base act...');
}

// Valid: BasePerformer implements Performer
class BalletDancer extends BasePerformer with Dancer {}

// Invalid: Compilation Error (Object does not implement Performer)
// class Robot with Dancer {} 

Linearization and Order of Precedence

Dart resolves mixin application through linearization. When mixins are applied, they do not create a parallel inheritance tree. Instead, they create a chain of intermediate classes between the superclass and the child class. The order of the with clause determines precedence. The rightmost mixin represents the most specific implementation in the chain (excluding the class itself), overriding members from mixins to its left and the superclass.
mixin A {
  void identify() => print('A');
}

mixin B {
  void identify() => print('B');
}

class Base {
  void identify() => print('Base');
}

// The inheritance chain is: Implementation -> B -> A -> Base
class Implementation extends Base with A, B {}

void main() {
  final obj = Implementation();
  obj.identify(); // Output: B (B overrides A, which overrides Base)
}

super Context

Inside a mixin, the super keyword is dynamic relative to the class application. It refers to the class or mixin immediately preceding the current mixin in the linearized inheritance chain. Static type safety for super invocations is enforced based on the method being called:
  1. Object Methods: Methods defined in the Object class (e.g., toString, hashCode, noSuchMethod) can be invoked via super without any explicit constraints, as every Dart class implicitly extends Object.
  2. Custom Methods: To invoke a method via super that is not defined in Object, the mixin must declare a superclass constraint (on) that defines that method.
class CounterBase {
  int count = 0;
  void increment() => count++;
}

// The 'on' clause is required to call super.increment()
// It is NOT required if we were only calling super.toString()
mixin Reporting on CounterBase {
  @override
  void increment() {
    super.increment(); // Delegates to the predecessor in the chain
    print('Count is now $count');
  }
  
  @override
  String toString() {
    // Valid without 'on' clause because Object defines toString
    return "Report: ${super.toString()}"; 
  }
}

class MyCounter extends CounterBase with Reporting {}
Master Dart with Deep Grasping Methodology!Learn More