Beginner · Null safety and object-oriented Dart · topic 8 of 50 · 35 min
Dart 3: enums, records, patterns and sealed classes
Explanation
Enhanced enums
An enum is a fixed set of named values. Since Dart 2.17, enums can carry fields and methods, which removes a lot of switch statements scattered around an app.
enum PaymentMode {
upi('UPI', isOnline: true),
card('Card', isOnline: true),
cash('Cash on delivery', isOnline: false);
const PaymentMode(this.label, {required this.isOnline});
final String label;
final bool isOnline;
}
void main() {
for (final m in PaymentMode.values) {
print('${m.label}: ${m.isOnline ? 'online' : 'offline'}');
}
}Records
Sometimes you want to return two or three values from a function without creating a class. A record is a small, immutable group of values. It can have positional fields (accessed as $1, $2) and named fields.
(int, int) splitBill(int total, int people) => (total ~/ people, total % people);
({double lat, double lng}) shopLocation() => (lat: 18.52, lng: 73.85);
void main() {
final (each, left) = splitBill(1000, 3); // destructuring a record
print('Each pays $each, $left left over'); // Each pays 333, 1 left over
final loc = shopLocation();
print('${loc.lat}, ${loc.lng}');
}Patterns
A pattern describes the shape of a value and can pull parts out of it. Dart 3 uses patterns in switch, in if-case, in variable declarations and in for-in loops.
String describe(Object value) => switch (value) {
int n when n < 0 => 'negative number',
int n => 'number $n',
String s when s.isEmpty => 'empty text',
String s => 'text of length ${s.length}',
(int a, int b) => 'pair $a and $b',
_ => 'something else',
};
void main() {
final json = {'name': 'Asha', 'age': 21};
if (json case {'name': String name, 'age': int age}) {
print('$name is $age'); // Asha is 21
}
print(describe((3, 4))); // pair 3 and 4
}Patterns replace many lines of manual checks. Without them you would write "if the value is a Map, and it has a key name, and that value is a String..." step by step. With a pattern, you describe the shape you expect once, and Dart checks every part and binds the variables only if everything matches.
Sealed classes
A sealed class can only be extended or implemented inside its own library (file). The compiler therefore knows every subtype, and a switch over a sealed type must handle all of them, without a default case. This is the standard way to model screen states and results.
sealed class LoadState {}
class Loading extends LoadState {}
class Loaded extends LoadState {
final List<String> items;
Loaded(this.items);
}
class Failed extends LoadState {
final String message;
Failed(this.message);
}
String render(LoadState s) => switch (s) {
Loading() => 'Showing spinner',
Loaded(:final items) => 'Showing ${items.length} items',
Failed(:final message) => 'Error: $message',
}; // no default needed; add a new subtype and this stops compilingClass modifiers in one table
| Modifier | Meaning (outside the library) |
|---|---|
| sealed | Cannot be extended or implemented; enables exhaustive switch |
| final | Cannot be extended or implemented |
| base | Can be extended, not implemented |
| interface | Can be implemented, not extended |
| mixin class | Can be used both as a class and as a mixin |
Common mistakes
- Adding a default _ case to a switch over a sealed type: it hides newly added subtypes. Leave it out.
- Using records for data that travels across the whole app. For long-lived models, a named class with documentation is clearer.
- Writing JSON access like json['age'] as int without checking. if-case patterns check shape and type together.
- Forgetting that sealed subclasses must be in the same library (file) as the sealed class.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Dart 3: enums, records, patterns and sealed classes · 1/6
Dart three added powerful features for modelling data: enhanced enums, records, patterns and sealed classes.
Prefer a video by a teacher? Find free videos on “dart 3 records patterns sealed classes enhanced enums switch expressions” on YouTube ↗
In real life: Payment results in a checkout flow
A checkout screen can end in three ways: success with a transaction reference, failure with a reason, or "pending" when the bank has not confirmed. A sealed PaymentResult with three subclasses makes the result screen's switch exhaustive. When the team later adds "refunded", the compiler lists every screen that must handle it.
sealed class PaymentResult {}
class Paid extends PaymentResult { final String utr; Paid(this.utr); }
class Declined extends PaymentResult { final String reason; Declined(this.reason); }
class Pending extends PaymentResult {}
String message(PaymentResult r) => switch (r) {
Paid(:final utr) => 'Paid. Reference $utr',
Declined(:final reason) => 'Payment failed: $reason',
Pending() => 'Waiting for bank confirmation',
};
void main() => print(message(Declined('insufficient balance')));Practice (0/3 done)
What does this print? (int, String) info() => (7, 'seven'); void main() { final (n, word) = info(); print('$word=$n'); }
Why should a switch over a sealed class usually have no default case?
Model a delivery tracker: an enhanced enum Vehicle (bike, van) with a speedKmph field; a sealed class DeliveryState with Placed, OnTheWay(rider, minutes) and Delivered(time); and a function that returns a message using a switch with object patterns. Also return (eta, distance) as a record from a function and destructure it.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Sound null safetyNull safety and object-oriented Dart · same module
- Data, enum and sealed classes, and objectsKotlin and Android App Development · Classes, objects and collections
- Interview questions: DartFlutter careers and interviews
- InterfacesJava Programming (Core to Spring Boot) · Inheritance, polymorphism and interfaces
- switch statements and switch expressionsJava Programming (Core to Spring Boot) · Control flow and methods
- JSON models and handling errorsKotlin and Android App Development · Saving data and calling APIs
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What is a record in Dart?
Answer: A small immutable group of values, like (String, int)
Records bundle values without a class.
Where can subclasses of a sealed class be declared?
Answer: Only in the same library (file)
That is what makes the set known.
What does when do in a switch pattern?
Answer: Adds a guard condition to a case
The case matches only if the guard is true.
Which feature lets an enum value carry a label and a flag?
Answer: Enhanced enums with fields
Enums can have fields and constructors.
Why can a when over a sealed type skip else?Related
Answer: The compiler knows every subtype and checks all are handled
Exhaustiveness is checked at compile time.
Which Dart 3 feature makes a switch over UI states exhaustive?Related
What does sealed interface Payment permits Upi, Card {} do?Related
Answer: Allows only Upi and Card to implement Payment
Sealed types restrict their implementations.
From Interfaces
String s = null; switch (s) { case "a" -> … default -> … } gives…Related
Answer: NullPointerException
Without case null, switching on null throws NPE.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Null safety and object-oriented Dart”. The module exam comes next.
- Module 2 exam: Null safety and object-oriented Dart · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Async Dart and packages. 50 topics left in the course before the final exam.