skip to content

In Dart, what is a mixin, how do you declare one and apply it with `with`, and what can it contain?

level: juniorimportance: must knowfreq 65%

answer

  1. reuse without a superclass
  2. mixin keyword, then with
  3. fields and methods, no constructor
  4. abstract getter asks the host
  5. several mixins, comma-separated

basics

~20 s

A Dart mixin is a mixin declaration whose fields and methods are added to a class listed after with. It cannot be instantiated or declare a constructor, and one class can apply several mixins alongside its single superclass.

solid answer

~40 s

A mixin is a bundle of members declared with the `mixin` keyword and applied with `with`, for example `class UserApi with Logging`. It may hold instance fields, concrete methods, getters and abstract members; the class that applies it gets all of them and becomes a subtype of the mixin, so `UserApi() is Logging` is true. A mixin has no `extends` clause, cannot be constructed on its own and cannot declare a constructor, so any value it needs from its host comes through an abstract member such as `String get logTag;` that the class must implement. A class keeps exactly one superclass but may list several mixins: `class OrderApi extends BaseApi with Logging, Metrics`.

code

dart · 20 lines
dart
mixin Logging {
  String get logTag;

  void log(String message) => print('[$logTag] $message');
}

class UserApi with Logging {
  @override
  final String logTag = 'UserApi';

  Future<void> fetchUser(String id) async {
    log('GET /users/$id');
  }
}

void main() {
  final api = UserApi();
  api.fetchUser('42'); // [UserApi] GET /users/42
  print(api is Logging); // true
}

go deeper

for a junior

Recall the mixin keyword, the with keyword after extends, and that a mixin adds members without becoming the superclass. Know that it cannot be constructed.

for a middle

Explain how a mixin reaches host data through abstract getters since it has no constructor, and that the host becomes a subtype of every mixin it applies.

for a senior

Show judgement about what belongs in a mixin: small, cohesive cross-cutting behaviour such as logging, not a grab bag that hides dependencies behind abstract getters.

for a principal

Weigh mixins against composition by delegation for cross-cutting API client concerns, considering testability, discoverability and how much hidden coupling a team can absorb.

## What a mixin is in Dart A **mixin** is a reusable set of class members — fields, methods, getters, setters and operators — that you can add to many classes without making them share a superclass. Dart's own docs describe mixins as a way of defining code that can be reused in multiple class hierarchies, providing member implementations en masse. Picture three API clients in one Flutter app: `UserApi`, `OrderApi` and `PaymentApi`. Each already has its own place in a hierarchy, yet all three should log every request in the same format. A shared superclass would force them into one hierarchy; copy-paste would drift. A mixin supplies the logging once and each client opts in. ## Declaring a mixin You declare one with the `mixin` keyword: ```dart mixin Logging { final List<String> history = []; // mixins may hold state String get logTag; // abstract: the host must supply it void log(String message) { history.add(message); print('[$logTag] $message'); } } ``` What a `mixin` declaration may contain: - **instance fields**, including initialised ones like `history` above; - **concrete methods, getters and setters**, which the host inherits; - **abstract members**, which every non-abstract host must implement; - an **`implements` clause**, which obliges the host to satisfy an interface; - an **`on` clause**, which restricts which superclasses it can be applied on (a separate topic). What it may **not** contain: - an `extends` clause — a plain mixin never names a superclass; - a **constructor** of any kind — the analyzer's message is simply "Mixins can't declare constructors" — so it cannot take constructor parameters to initialise its own fields. Because it has no constructor, a mixin cannot be created with `Logging()`. It exists only to be applied. ## Applying a mixin with `with` ```dart class UserApi with Logging { @override final String logTag = 'UserApi'; Future<void> fetchUser(String id) async { log('GET /users/$id'); } } class OrderApi extends BaseApi with Logging, Metrics { @override String get logTag => 'OrderApi'; } ``` Key rules: 1. `with` comes **after** `extends` when both are present; with no `extends`, the superclass is `Object`. 2. You may list **several mixins**, comma-separated; their order matters when two of them define the same member. 3. The class **must implement every abstract member** the mixin declares, unless the class is itself `abstract`. 4. The resulting class is a **subtype of each mixin**: `UserApi() is Logging` is `true`, so a function can accept any `Logging` value. ## Getting data from the host Since a mixin cannot take constructor parameters, the standard way to reach host data is an **abstract getter** (`String get logTag;`). The host satisfies it with a field or a getter. The alternative, an `implements` clause on the mixin, works the same way: it makes the mixin declare that the host provides an interface's members without implementing them itself. ## Mixins in the Dart 3 era Before Dart 3.0, any class with no declared constructors and a superclass of `Object` could be mixed in. Since 3.0, a class in a library at language version 3.0 or later can be mixed in only if it is declared `mixin` or `mixin class`. Code written today should declare mixins with `mixin`. | Feature | `mixin` | `class` | |---|---|---| | Instantiate with a constructor call | No | Yes | | Declare a constructor | No | Yes | | Hold instance fields | Yes | Yes | | Apply with `with` | Yes | No (since Dart 3.0) | | Use as a type (`x is Logging`) | Yes | Yes | ## Where interviewers probe A screening question usually asks for the declaration syntax, the `with` keyword, the no-constructor rule and the difference from `extends`. Good candidates add that mixins can carry state, that host data arrives through abstract members, and that listing order matters when members collide.

  • Can a Dart mixin hold state, and how does that state get initialised?
    Yes. A mixin may declare instance fields, and each object of a class that applies it gets its own copy. Because a mixin cannot declare a constructor, those fields are initialised by their declared initialisers, or declared `late`, or set by the host class after construction. Data the mixin needs from outside usually arrives through an abstract getter the host implements.
  • In Dart, is a class that applies a mixin a subtype of that mixin?
    Yes. `class UserApi with Logging` makes `UserApi` a subtype of `Logging`, so `UserApi() is Logging` is true and a `UserApi` can be passed wherever a `Logging` is expected. That lets you write helpers typed against the mixin rather than against each client.

A mixin is like a plug-in module for a set of different appliances: the same timer board fits a kettle, an oven and a heater, each appliance keeps its own body, and the board asks the appliance for power through a socket it must provide.

saying these in an interview costs you the question

  • A mixin can be instantiated directly like a class.
  • A mixin cannot have fields; it only adds methods.
  • A class can only apply one mixin at a time.
  • A mixin takes constructor parameters to receive host data.
  • Any ordinary Dart 3 class can be listed after with.