Mixins
3 min read
OOP in Dart
| Relationship | Keyword | Mental model | Flutter example |
|---|---|---|---|
| Inheritance | extends | "is-a" — single parent | MyState extends State<...> |
| Mixin | with | "can-do" — composable behaviour | with TickerProviderStateMixin |
| Composition | field | "has-a" — most flexible | final 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
| Situation | Use |
|---|---|
| One specific subclass relationship with shared implementation | extends |
| Capability shared by unrelated classes (logging, disposal, observation) | mixin … with … |
| Anything that could be passed in or replaced | Composition (constructor injection) |
| One animation in a State | SingleTickerProviderStateMixin |
| Multiple animations | TickerProviderStateMixin |
| Observing app lifecycle in a State | mixin 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
-
What does the
onclause on a mixin do? It constrains where the mixin can be applied — only classes that extend or implement the type afteron. Inside the mixin, you can call members of that type (e.g.,mixin Loggable on State<StatefulWidget>lets you readwidgetand callsetState). -
What's mixin linearisation and why does it matter? When you write
class X extends A with M1, M2, Dart conceptually layers them asA → M1 → M2 → X. Method lookup walks up that chain, so a method inM2overrides the same method inM1.super.method()insideM2callsM1's version. Order matters. -
Mixin vs interface — when do you reach for each?
implementssays "I conform to this contract — I'll provide every member myself."withsays "I want this implementation." Use interfaces for type contracts (and testing fakes); use mixins to share concrete code. -
Why does Dart 3 have the
mixin classmodifier? Backwards compatibility. Pre-Dart-3, any class could be used as a mixin. Dart 3 mademixinandclassseparate — but to avoid breaking existing code,mixin classlets a single declaration be both. New code should pickmixinorclass, not both.
How helpful was this content?
Please sign in to rate this article.