Mixins

Medium PriorityAsked in ~50% of mid-level interviews

3 min read

OOP in Dart

RelationshipKeywordMental modelFlutter example
Inheritanceextends"is-a" — single parentMyState extends State<...>
Mixinwith"can-do" — composable behaviourwith TickerProviderStateMixin
Compositionfield"has-a" — most flexiblefinal ctrl = AnimationController(...);

Rule of thumb: composition first. Use mixins when the same code keeps appearing across classes that have nothing else in common.


Code in action

// Mixin with `on` clause — only valid on State<T>
mixin AutoDispose<T extends StatefulWidget> on State<T> {
  final List<ChangeNotifier> _toDispose = [];

  C register<C extends ChangeNotifier>(C c) {
    _toDispose.add(c);
    return c;
  }

  @override
  void dispose() {
    for (final c in _toDispose) c.dispose();
    super.dispose();
  }
}

// Use it
class _ScreenState extends State<Screen> with AutoDispose {
  late final _name = register(TextEditingController());
  late final _email = register(TextEditingController());
  // no manual dispose() needed — mixin handles it
}
// Real Flutter mixin you'll meet daily
class _AnimState extends State<MyAnim>
    with TickerProviderStateMixin {                 // can host MULTIPLE controllers
  late final fade  = AnimationController(vsync: this, duration: ...);
  late final slide = AnimationController(vsync: this, duration: ...);
}

// SingleTickerProviderStateMixin if you have exactly one

When to use which

SituationUse
One specific subclass relationship with shared implementationextends
Capability shared by unrelated classes (logging, disposal, observation)mixin … with …
Anything that could be passed in or replacedComposition (constructor injection)
One animation in a StateSingleTickerProviderStateMixin
Multiple animationsTickerProviderStateMixin
Observing app lifecycle in a Statemixin pattern with WidgetsBindingObserver

Common mistakes to avoid

// ❌ Using inheritance for code reuse where there's no IS-A
class CartScreen extends ApiHelper { ... }            // CartScreen is not an ApiHelper
// ✅ Mixin (if cross-cutting) or composition (if it's just a dep)

// ❌ Mixins without `on` clause when they rely on the base class
mixin Loggable {
  void log() => debugPrint(state.toString());         // ❌ State<T> unknown here
}
// ✅ mixin Loggable on State<StatefulWidget> { ... }

// ❌ Stacking many mixins → linearisation surprises
class X extends A with M1, M2, M3 { ... }
// super.foo() walks M3 → M2 → M1 → A. Be deliberate about order.

// ❌ Putting state in a mixin that you then forget about
mixin Counter { int _count = 0; }                    // every class with Counter has its own _count
// usually fine, but easy to misuse — composition is clearer

// ❌ Mixin where a simple extension method would do
// Extensions can't add state; mixins can. Pick the lightest that fits.

Interview follow-ups

  1. What does the on clause on a mixin do? It constrains where the mixin can be applied — only classes that extend or implement the type after on. Inside the mixin, you can call members of that type (e.g., mixin Loggable on State<StatefulWidget> lets you read widget and call setState).

  2. What's mixin linearisation and why does it matter? When you write class X extends A with M1, M2, Dart conceptually layers them as A → M1 → M2 → X. Method lookup walks up that chain, so a method in M2 overrides the same method in M1. super.method() inside M2 calls M1's version. Order matters.

  3. Mixin vs interface — when do you reach for each? implements says "I conform to this contract — I'll provide every member myself." with says "I want this implementation." Use interfaces for type contracts (and testing fakes); use mixins to share concrete code.

  4. Why does Dart 3 have the mixin class modifier? Backwards compatibility. Pre-Dart-3, any class could be used as a mixin. Dart 3 made mixin and class separate — but to avoid breaking existing code, mixin class lets a single declaration be both. New code should pick mixin or class, not both.


How helpful was this content?

Please sign in to rate this article.