tezvyn:

Dart Mixins: Constraining Reusable Code with `on`

AI-drafted, machine-checkedSource: dart.devadvanced

A Dart mixin is like a plugin of methods for a class. The `on` keyword acts as a gatekeeper, ensuring the class using the mixin already has specific base features. This is used for sharing UI logic only among `Widget` subclasses.

WHY IT EXISTS Dart has single inheritance, meaning a class can only extend one parent. This is restrictive when you want to share a common set of behaviors (like logging or data serialization) across different, unrelated class hierarchies. Mixins solve this by letting you "mix in" reusable code without forcing a class into a particular inheritance chain.

THE MENTAL MODEL Think of a mixin as a "skill set" you can bolt onto a class. The on keyword is a compatibility requirement for that skill set. It's like a hardware driver that says, "I can run, but only on this specific operating system." For example, a Flutter animation mixin might use on State because it needs access to the lifecycle methods provided by the State class. The mixin is declaring, "I can give you animation abilities, but only if you are a widget's State object."

HOW IT WORKS You define a mixin with the mixin keyword. To constrain it, you add on SuperType. This tells the compiler two things: first, this mixin can only be applied to classes that are subtypes of SuperType. Second, inside the mixin's code, you can now safely access all the public members (methods and properties) of SuperType. When you apply the mixin to a class using with, the compiler verifies the on constraint is met.

WHEN TO USE IT Use a mixin with on when your reusable logic is intrinsically tied to a specific class hierarchy and needs to call its methods. A classic Flutter example is a mixin that provides animation controllers. This logic needs the vsync provided by a TickerProviderStateMixin, which in turn requires the class to be a State object. Using on State makes this dependency explicit and type-safe.

WHEN NOT TO USE IT Do not use the on constraint if your mixin's logic is self-contained and doesn't depend on any methods or properties from the class it's applied to. Adding an unnecessary on constraint strictly reduces the mixin's reusability for no benefit. If the behavior is truly generic, it shouldn't be constrained.

ONE CANONICAL EXAMPLE Imagine a mixin to automatically dispose of a StreamSubscription. This is only useful on a StatefulWidget's State, which has a dispose method. You could write: mixin AutoDispose<T extends StatefulWidget> on State<T> { StreamSubscription? _sub; void setSub(StreamSubscription s) { _sub = s; } @override void dispose() { _sub?.cancel(); super.dispose(); } }. This AutoDispose mixin can only be used on a State object and can safely override dispose because it knows every State object has one.

Read the original → dart.dev

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.