10  Mixins

Mixins in Dart are a way to reuse code across multiple class hierarchies without using inheritance. Think of them as “chunks of functionality” that you can add to classes.

10.1 Key Differences Between Mixins and Classes

Mixins:

  • Cannot be instantiated directly (you can’t do new MyMixin())
  • Don’t have constructors
  • Used with the with keyword to add functionality to a class
  • Can be “mixed in” to multiple classes
  • Designed specifically for code reuse across unrelated classes

Classes:

  • Can be instantiated (you can create objects from them)
  • Have constructors
  • Use extends for inheritance (single inheritance only)
  • Represent complete entities or concepts
  • Can only inherit from one superclass

10.2 Creating and Using Mixins

Here’s a practical example:

// Define a mixin
mixin Flying {
  void fly() {
    print('Flying through the air!');
  }
  
  double altitude = 0;
  
  void changeAltitude(double meters) {
    altitude += meters;
    print('New altitude: $altitude meters');
  }
}

mixin Swimming {
  void swim() {
    print('Swimming in water!');
  }
  
  void dive(double depth) {
    print('Diving to $depth meters');
  }
}

// Regular classes
class Bird {
  String name;
  Bird(this.name);
}

class Fish {
  String species;
  Fish(this.species);
}

// Using mixins with classes
class Duck extends Bird with Flying, Swimming {
  Duck(String name) : super(name);
  
  void quack() {
    print('$name says: Quack!');
  }
}

class FlyingFish extends Fish with Flying {
  FlyingFish(String species) : super(species);
}

void main() {
  var donald = Duck('Donald');
  donald.fly();        // From Flying mixin
  donald.swim();       // From Swimming mixin
  donald.quack();      // Duck's own method
  
  var flyingFish = FlyingFish('Exocoetus');
  flyingFish.fly();    // From Flying mixin
  // flyingFish.swim(); // Error! FlyingFish doesn't have Swimming mixin
}

10.3 When to Use Mixins vs Inheritance

Use Mixins when:

  • You want to share functionality between classes that don’t share a common ancestor
  • You need to add the same capabilities to multiple unrelated classes
  • You want to compose behavior from multiple sources (like adding both Flying and Swimming)

Use Class Inheritance when:

  • There’s a clear “is-a” relationship (Duck is a Bird)
  • You’re modeling a hierarchy of related concepts
  • You need constructors and want to create instances

10.4 Real Flutter Example

In Flutter, mixins are commonly used. Here’s a practical example with TickerProviderStateMixin:

class AnimatedScreen extends StatefulWidget {
  @override
  _AnimatedScreenState createState() => _AnimatedScreenState();
}

// Using SingleTickerProviderStateMixin to add animation capabilities
class _AnimatedScreenState extends State<AnimatedScreen> 
    with SingleTickerProviderStateMixin {
  
  late AnimationController _controller;
  
  @override
  void initState() {
    super.initState();
    // The mixin provides 'this' as a TickerProvider
    _controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,  // 'this' works because of the mixin
    );
  }
  
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

The SingleTickerProviderStateMixin adds the ability to provide animation tickers to your State class without you having to implement all that functionality yourself.

Coming from Python, you might find mixins similar to multiple inheritance, but Dart’s approach is more controlled and avoids the “diamond problem” that can occur in languages with full multiple inheritance.