Dart (3.13) Module 9 – Inheritance, mixins, class modifiers and extensions
Mixins and the mixin class
Write a Dart mixin and a mixin class, apply them with `with`, use the `on` clause to call super, and predict how two mixins compose under linearization.
What you will learn
- Write a mixin and a mixin class, and apply them to a Notifier with `with`
- Explain the on clause and the super chain of applied mixins
- Predict which body runs for super.send under both orderings of a with list
- Decide between a mixin, a base class and composition for a shared behaviour
- Design a tiny host that tests a mixin without touching the real gateway
Before you start
What you’ll learn · Before you start
What you will learn
- Write a mixin and a mixin class, and apply them to a Notifier with `with`
- Explain the on clause and the super chain of applied mixins
- Predict which body runs for super.send under both orderings of a with list
- Decide between a mixin, a base class and composition for a shared behaviour
- Design a tiny host that tests a mixin without touching the real gateway
Before you start
On this page
An SMS notifier and an email notifier both need a retry wrapper, a log line and a greeting template: three blocks of
code, each written twice. Dart mixins remove that repetition: a mixin: A Dart declaration that adds its members to any class that lists it with the with keyword. A mixin cannot have an extends clause, cannot declare a generative constructor, and may name a superclass constraint with on. is a named block of
fields and methods that classes add through the with keyword without changing their parent. Dart mixins suit
behaviour that unrelated classes share, such as retries or logging, where a common base class would force them all
under one parent.
You will write a mixin and a mixin class: A Dart declaration since 3.0 that is both a class (usable with extends) and a mixin (usable with with); it has the same restrictions as a mixin: no extends on itself, no generative constructor., which works both as a mixin and as a parent,
and test the Dart 3.0 rule that a plain class cannot be mixed in. Then the on clause lets a mixin call super, and two
mixins compose through mixin linearization: Dart's rule for stacking a with list: mixins are layered left to right in the declaration and bottom to top in the chain, so dispatch starts at the last mixin and super steps one slot down.. The outputs below are what each program
printed on Dart 3.13.5.
A mixin adds members with with
A mixin declaration looks like a class but is used only through the with keyword, and the fields and methods it
declares are added to every class that lists it. A mixin class can also stand alone as a parent after extends.
This program applies both to the shop’s SMS notifier:
// notifier.dart: the shared abstract Notifier and a concrete SmsNotifier.
//
// The mixins lesson reuses the shape from dart/inheritance-and-interfaces: a
// Notifier that promises a single send, and an SmsNotifier that writes it to
// the console in the shop's shape. Nothing in this file is a mixin yet; it is
// the base every example extends.
abstract class Notifier {
void send(String to, String text);
}
class SmsNotifier extends Notifier {
final String sender;
SmsNotifier(this.sender);
@override
void send(String to, String text) {
print('[SMS from $sender] +91 $to: $text');
}
} // mixin_basic.dart: declare a mixin, declare a mixin class, and apply both
// with `with` to an SmsNotifier. The mixin adds a log method and a counter;
// the mixin class is reused as a plain parent too, which is what makes it
// "both a class and a mixin" (dart.dev/language/mixins, Dart 3.0).
//
// Run: dart run mixin_basic.dart
import 'notifier.dart';
mixin Log {
int logs = 0;
void log(String line) {
logs++;
print('[log] $line');
}
}
// A `mixin class` can be used in both `extends` and `with`. The restrictions
// of a mixin apply (no extends on the declaration, no generative constructor).
mixin class Templated {
String build(String name, String text) => 'Dear $name, $text';
}
class SmsWithLog extends SmsNotifier with Log, Templated {
SmsWithLog(super.sender);
}
// Templated is also used as a plain parent class: the "class" half.
class EagerTemplate extends Templated {}
void main() {
final s = SmsWithLog('MSCOPL');
s.log('getting the message ready');
final msg = s.build('Priya', 'Your order is on the way.');
s.send('98765 43210', msg);
final e = EagerTemplate();
print(e.build('Kabir', 'Thanks for the review.'));
print('log entries: ${s.logs}');
} Output
[log] getting the message ready [SMS from MSCOPL] +91 98765 43210: Dear Priya, Your order is on the way. Dear Kabir, Thanks for the review. log entries: 1
Recorded with Dart SDK 3.13.5 (stable) on macos_arm64 on macOS 26 arm64. To run it yourself: dart run mixin_basic.dart
The run printed four lines: a [log] line from the mixin, the SMS line from send, a build call from the mixin
class used as a parent (EagerTemplate extends Templated), and log entries: 1. SmsWithLog has one parent,
SmsNotifier, and two mixins stacked above it, and an email notifier could take the same two.
Neither a mixin nor a mixin class may have an extends clause or declare a generative constructor
(dart.dev/language/mixins). A mixin keeps its own state in the fields it declares.
Dart 3.0 made another rule firm: a plain class may not be used with with. This file tries it and does not compile:
// plain_class_fail.dart: a plain class may not be used with `with` since
// Dart 3.0; the declaration must be `mixin` or `mixin class`.
//
// Compile-time error, so this program never runs.
import 'notifier.dart';
class PlainLog {
void log(String line) {
print('[log] $line');
}
}
class Bad extends SmsNotifier with PlainLog {
Bad(super.sender);
}
void main() {
Bad('MSCOPL').log('hi');
} Output (exit status 254)
Printed as an error (standard error)
plain_class_fail.dart:14:36: Error: The class 'PlainLog' can't be used as a mixin because it isn't a mixin class nor a mixin.
class Bad extends SmsNotifier with PlainLog {
^
Recorded with Dart SDK 3.13.5 (stable) on macos_arm64 on macOS 26 arm64. To run it yourself: dart run plain_class_fail.dart
The compile stopped with “The class ‘PlainLog’ can’t be used as a mixin because it isn’t a mixin class nor a mixin.”
Declaring it as mixin class or as mixin fixes it.
Prefer `mixin` when the type is only mixed in
A declaration marked mixin says the design: no code will ever write extends PlainLog. Reach for mixin class
only when the same declaration is also used as a parent, as Templated is in mixin_basic.dart.
Checkpoint
Check: Mixins, with and mixin class
The on clause opens super and fixes where a mixin applies
A mixin can wrap a method of its host when its on clause names a type every host must be a subtype of. This
superclass constraint: A mixin's on T clause: it promises every host of the mixin is a subtype of T, so super calls inside the mixin resolve to T's members at compile time. does two things: it tells the compiler which super
calls the mixin may make, and it refuses hosts outside that type. mixin Retry on Notifier can call
super.send(to, text) because every host is a Notifier:
// retry_super.dart: a Retry mixin that wraps every send in up to three tries.
// The `on Notifier` clause lets the mixin call `super.send(...)`, which goes
// to the host's own send once this mixin is applied.
//
// Run: dart run retry_super.dart
import 'notifier.dart';
// FlakySms fails its first two attempts, then delivers. The number is fixed
// so the output of this file never changes between runs.
class FlakySms extends SmsNotifier {
int attempts = 0;
FlakySms(super.sender);
@override
void send(String to, String text) {
attempts++;
if (attempts <= 2) {
throw StateError('gateway timeout on attempt $attempts');
}
super.send(to, text);
}
}
mixin Retry on Notifier {
int retries = 0;
@override
void send(String to, String text) {
for (var i = 1; i <= 3; i++) {
try {
super.send(to, text);
return;
} on StateError catch (e) {
retries++;
if (i == 3) {
print('giving up after $i tries: ${e.message}');
rethrow;
}
print('attempt $i failed: ${e.message}; retrying');
}
}
}
}
class RetryingFlakySms extends FlakySms with Retry {
RetryingFlakySms(super.sender);
}
void main() {
final s = RetryingFlakySms('MSCOPL');
s.send('98765 43210', 'Your OTP is 419237.');
print('retries counted by the mixin: ${s.retries}');
} Output
attempt 1 failed: gateway timeout on attempt 1; retrying attempt 2 failed: gateway timeout on attempt 2; retrying [SMS from MSCOPL] +91 98765 43210: Your OTP is 419237. retries counted by the mixin: 2
Recorded with Dart SDK 3.13.5 (stable) on macos_arm64 on macOS 26 arm64. To run it yourself: dart run retry_super.dart
The run printed two “attempt N failed” lines, the delivery, and “retries counted by the mixin: 2”. FlakySms fails
twice and delivers on the third try, so Retry’s loop runs three times for one send. Without on Notifier,
super.send(...) would not compile, and neither would with Retry on a host that is not a Notifier
(dart.dev/language/mixins).
A mixin may also declare an abstract member and rely on the host to provide it, in place of the constructor it cannot
have. And when two mixins add a member with the same name, Dart linearizes them left to right in the with list, so
the mixin written later wins.
Key idea
on T promises every host of the mixin is a subtype of T, so super calls in the mixin resolve to T’s members at
compile time, and dispatch at run time follows the host’s chain through to T.
Checkpoint
Check: The on clause and super calls
Super chains, same-name members and the four rules a mixin must keep Pro: opens with a Pro, Premium or Ultimate pass
Two mixins in one with list write one chain, and super from each layer steps one slot down it. This part measures
the chain with a program that prints before and after every super call, then tests two more rules: when two mixins
declare the same member, and when a mixin asks the host for one it does not have. The four rules at the end are the
restrictions dart.dev/language/mixins lists.
In this part:
- How Dart orders super when two mixins share a method
- Same-name members and the abstract members a mixin asks the host for
2 diagrams · 4 runnable examples (Dart) · about 8 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Choosing between a mixin, a base class and composition, and testing the mixin alone Pro: opens with a Pro, Premium or Ultimate pass
A design can share behaviour three ways: a mixin layered over several hosts, a base class every host extends, and composition that holds a smaller object inside a bigger one. Each one costs the design something, and the right choice depends on which cost the team can pay. This part lines them up with the module’s shared Notifier, then tests a mixin on its own through a tiny host.
In this part:
- When a mixin fits and when a base class or composition does
- Testing a mixin through a tiny host
1 runnable example (Dart) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Practise: build your own Counting mixin
Write a mixin that counts every outgoing send. Any notifier may be its host, so its on clause names Notifier and
nothing more. Start from notifier.dart:
// notifier.dart: the shared abstract Notifier and a concrete SmsNotifier.
//
// The mixins lesson reuses the shape from dart/inheritance-and-interfaces: a
// Notifier that promises a single send, and an SmsNotifier that writes it to
// the console in the shop's shape. Nothing in this file is a mixin yet; it is
// the base every example extends.
abstract class Notifier {
void send(String to, String text);
}
class SmsNotifier extends Notifier {
final String sender;
SmsNotifier(this.sender);
@override
void send(String to, String text) {
print('[SMS from $sender] +91 $to: $text');
}
} - Declare
mixin Counting on Notifierwith anint sent = 0;field and an@override void send(String to, String text)that incrementssentand callssuper.send(to, text). - Apply it:
class CountingSms extends SmsNotifier with Counting. Build an instance, callsendthree times, and prints.sent. - Apply the mixin in both orders,
with Counting, Logandwith Log, Counting, predict which line prints first in each, then run both and compare. - Write a tiny host of your own that drives
Countingwithout the SMS gateway.
When all four steps work, answer the questions below; they begin with your weakest concept:
Practice that adapts to you
Practise this lesson
5 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): In Dart 3, when must a type be declared with mixin or mixin class rather than class?
Any type on the right of with. Since Dart 3.0, a plain class may not be used as a mixin: the declaration must be
mixin (only mixed in, never a parent) or mixin class (both at once). The compiler reports “The class ‘C’ can’t be
used as a mixin because it isn’t a mixin class nor a mixin.” for the slip, so no program with it will build. A plain
class is still fine when it is only ever used with extends or implements; it is only with that needs the
newer declaration.
Three longer questions from this module’s bank take mixin design into a real codebase:
-
Mid to senior
Four services and three repositories need the same log, metrics and retry behaviour. Draft one is a base class LoggedRetrying the seven extend; draft two is three mixins the seven list in with; draft three is a Pipeline that wraps every call. Which would you ship, and where does each fail first?
-
Senior to staff
A payments service uses class Pay extends GatewayBase with Log, Metrics, Retry. A new "verify after retry" check runs twice for every retry, and the team suspects the super chain. Walk through how you would read the order, the measurement you would take to confirm it, and the one-token fix if the chain is wrong.
-
Mid to senior
The team asks why mixin Metrics on Notifier cannot take a MetricsClient in its constructor. Explain what Dart forbids there and show two patterns that keep every host independent: the mixin's own abstract getter, and a static Metrics.of lookup. Say which you would ship and why.
Flashcards · 6 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
What is a mixin, and how does a class use one?
A named block of fields and methods that a class adds with the with keyword, without changing its parent. It suits behaviour that unrelated classes share, such as retries or logging.
-
How does a mixin class differ from a mixin?
A mixin class can also stand alone as a parent after extends; a mixin is only ever mixed in. Prefer mixin when the type is only mixed in, since the declaration then says the design.
-
What happens since Dart 3.0 when a plain class appears after with?
It does not compile: "can't be used as a mixin because it isn't a mixin class nor a mixin." Declare it mixin or mixin class; a plain class is still fine after extends or implements.
-
What can neither a mixin nor a mixin class declare?
An extends clause or a generative constructor. A mixin keeps its own state in the fields it declares, and it may declare an abstract member for the host to provide.
-
What does the on clause of mixin Retry on Notifier allow?
super.send(...) inside the mixin, because every host must be a Notifier; with Retry on a class that is not a Notifier does not compile.
-
Two mixins in one with list add a member with the same name. Which one wins?
The mixin written later. Dart linearizes the mixins left to right in the with list.
Key takeaways
- A
mixinadds its members to any class that names it withwith, and amixin classis both a mixin and a plain parent; a plain class may not be mixed in since Dart 3.0. - An
on Tclause makes every host a subtype of T, which lets the mixin callsuperwith T’s members. - The
withlist is layered left to right, so the last mixin ends up on top and dispatch starts there. - A mixin cannot have a generative constructor; state the mixin needs comes from the host through abstract getters.
Terms in this lesson
- mixin
- A Dart declaration that adds its members to any class that lists it with the with keyword. A mixin cannot have an extends clause, cannot declare a generative constructor, and may name a superclass constraint with on.
- mixin class
- A Dart declaration since 3.0 that is both a class (usable with extends) and a mixin (usable with with); it has the same restrictions as a mixin: no extends on itself, no generative constructor.
- mixin linearization
- Dart's rule for stacking a with list: mixins are layered left to right in the declaration and bottom to top in the chain, so dispatch starts at the last mixin and super steps one slot down.
- superclass constraint
- A mixin's on T clause: it promises every host of the mixin is a subtype of T, so super calls inside the mixin resolve to T's members at compile time.
References
- Language: mixins (The Dart project (Google))
- Language: class modifiers (The Dart project (Google))
- Effective Dart: design (type hierarchies and prefer composition) (The Dart project (Google))
- Dart SDK CHANGELOG (3.0.0: class and mixin modifiers) (The Dart project (Google))
- Dart language evolution (The Dart project (Google))
Report a problem with this lesson
Kept only in this browser. Your Learn progress