In Dart 3, why can you no longer mix in an ordinary class, and when would you declare a `mixin class` instead of a `mixin`?
answer
- Dart 3.0 breaking change
- language version of the library
- both extends and with
- no extends, with or on
- prefer pure mixin or class
basics
~20 sSince Dart 3.0, a class in a library at language version 3.0 or later can be applied with with only if declared mixin or mixin class. Use mixin class only when the same type must work both as a superclass and as a mixin.
solid answer
~40 sBefore Dart 3.0, any class with no declared constructors and `Object` as its superclass could be mixed in, so a class author could break users by adding a constructor. Dart 3.0 made mixability opt-in: a class from a 3.0+ library can no longer be listed after `with` unless it is declared `mixin class`; libraries still on an older language version keep the old behaviour. A `mixin class` is one type usable both with `extends` and with `with`, so it carries the restrictions of both: no `extends`, `with` or `on` clause. Effective Dart recommends a pure `mixin` or a pure `class` for new code; `mixin class` exists mainly to migrate old class-as-mixin types, as the SDK did with `ListBase` and `MapBase`.
code
dart · 14 lines// Dart 3.x library
class Retry {
int maxAttempts = 3;
}
// class UserApi with Retry {} // error: Retry is not a mixin or mixin class
mixin class Retrying {
int maxAttempts = 3;
bool shouldRetry(int attempt) => attempt < maxAttempts;
}
class UserApi with Retrying {} // OK: used as a mixin
class OrderApi extends Retrying {} // OK: used as a superclassgo deeper
Recall that since Dart 3 only mixin and mixin class declarations can follow with, and that mixin class means usable both ways.
Explain why mixability became opt-in, that the rule follows the declaring library's language version, and the restrictions a mixin class inherits from both sides.
Plan a package migration: decide per type whether users extend, mix in or both, and choose mixin, class or mixin class without breaking them.
Treat mixability as public API surface: weigh the lock-in of mixin class against the cost of breaking users, and use @Deprecated.mixin() to stage removals.
## The pre-3.0 situation Up to language version 2.19, Dart let you mix in **almost any class**: if it declared no constructors and extended `Object`, it could appear after `with`. That flexibility was accidental coupling. A library author who wrote `class Retry { ... }` might never have intended it as a mixin, yet a downstream team could write `with Retry`. If the author later added a constructor or a superclass, that downstream code broke. Effective Dart calls the old rule confusing for exactly this reason. ## What Dart 3.0 changed The Dart 3.0 SDK changelog lists it as a **breaking change**: class declarations from libraries upgraded to Dart 3.0 can no longer be used as mixins by default. - To make a type usable **only as a mixin**, declare it `mixin`. - To make it usable **both as a class and as a mixin**, declare it `mixin class`. - Classes in libraries that have **not** been upgraded to language version 3.0 can still be mixed in, so migration can happen package by package. The rule keys on the **language version of the library that declares the class**, which comes from the package's SDK constraint lower bound in `pubspec.yaml` or a per-file version comment. That is why an old package may still export mixable classes while your own 3.x code cannot declare one. ## What a `mixin class` is ```dart mixin class Retrying { int maxAttempts = 3; bool shouldRetry(int attempt) => attempt < maxAttempts; } class UserApi with Retrying {} // used as a mixin class OrderApi extends Retrying {} // used as a superclass ``` A `mixin class` defines one declaration that is both a class and a mixin, with the same name and the same type. Anything forbidden to either is forbidden to it: 1. No **`extends`** or **`with`** clause, because mixins cannot have them. 2. No **`on`** clause, because classes cannot have one — so a `mixin class` cannot make constrained `super` calls. 3. No **non-trivial generative constructor**: it may declare at most a generative constructor with no parameters, initializer list or body, so its fields rely on their initialisers. It combines with some other modifiers: `abstract mixin class`, `base mixin class` and `abstract base mixin class` are valid, while `interface`, `final` and `sealed` cannot be combined with `mixin` because they prevent mixing in. The details of those modifiers belong to the class-modifiers topic. ## Real examples in the SDK The Dart 3.0 changelog notes that non-`mixin` classes in the platform libraries could no longer be mixed in unless marked `mixin class`, and several were converted. In Dart 3.13's `dart:collection`, `ListBase`, `MapBase` and `SetBase` are declared `abstract mixin class`, and the older names `ListMixin`, `MapMixin` and `SetMixin` are now `typedef`s for them. In `dart:core`, `Iterable` is itself an `abstract mixin class`, and `IterableMixin` is a typedef for it. So both `class MyList extends ListBase<int>` and `class MyList with ListMixin<int>` still work. ## When to choose which | Situation | Declaration | |---|---| | New reusable behaviour for several API clients | `mixin` | | New type meant to be subclassed or constructed | `class` | | Existing type that users both extend and mix in, and you cannot break either | `mixin class` | | Mixin that must wrap an inherited method via `super` | `mixin ... on T` (not `mixin class`) | Effective Dart's guideline is to **prefer a pure `mixin` or a pure `class`**; the `prefer_mixin` lint flags mixing in a type that is not a `mixin`. Declaring `mixin class` for new code makes the author's intent ambiguous and locks the type into both sets of restrictions. ## Deprecating mixability Removing `mixin` from a `mixin class` breaks every user who applies it with `with`. Dart 3.10 added `@Deprecated.mixin()` to `dart:core`, which marks the ability to mix a class in as deprecated, so a library can warn users one release before it drops the `mixin` keyword. ## Summary - Pre-3.0: mixability was implicit and fragile. - Dart 3.0: it is explicit, via `mixin` or `mixin class`, per library language version. - `mixin class` is a migration tool, not the default.
- In Dart 3, can a mixin class have an on clause so it can call super?No. A class cannot have an `on` clause, and a `mixin class` must satisfy the rules of both a class and a mixin. If you need constrained `super` calls, declare a pure `mixin ... on T` instead.
- In Dart 3, how did the SDK keep `with ListMixin<E>` working after the change?It converted the collection bases to `abstract mixin class` declarations (`ListBase`, `MapBase`, `SetBase`) and turned `ListMixin`, `MapMixin` and `SetMixin` into typedefs for them, so both extending and mixing in still compile.
saying these in an interview costs you the question
- Any class without a constructor can still be mixed in under Dart 3.
- mixin class is the recommended default for new mixins.
- A mixin class can use an on clause to call super.
- The Dart 3 rule applies to every package regardless of language version.
- A mixin class can extend another class like a normal class.