Sealed Classes (Dart 3)
Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 85 of 110.
Dart 3 introduced sealed classes, which restrict which classes can extend or implement them. When you mark a class as sealed, only classes in the same file can be its direct subtypes. This creates a closed set of known subtypes.
sealed class Result {}
class Success extends Result {
final String data;
Success(this.data);
}
class Failure extends Result {
final String error;
Failure(this.error);
}The power of sealed classes comes from exhaustiveness checking. When you switch on a sealed type, the compiler knows all possible subtypes and ensures you handle each one:
String handleResult(Result result) {
return switch (result) {
Success(data: var d) => 'Got: $d',
Failure(error: var e) => 'Error: $e',
};
// No default needed - compiler knows all cases are covered!
}
void main() {
var success = Success('Hello');
var failure = Failure('Not found');
print(handleResult(success)); // Got: Hello
print(handleResult(failure)); // Error: Not found
}If you add a new subtype to a sealed class, the compiler will flag every switch statement that doesn't handle it. This makes sealed classes ideal for modeling states, results, or any scenario where you have a fixed set of variants. Unlike abstract classes, sealed classes guarantee that no external code can add unexpected subtypes.
Challenge
EasyLet's build a network request handler using sealed classes! You'll create a type-safe system that models different states of a network response, ensuring every possible outcome is handled properly.
You'll organize your code into two files:
response.dart: Create a sealed class hierarchy that represents all possible states of a network response:- A sealed class called
NetworkResponsethat serves as the base for all response types - A
Loadingclass that extendsNetworkResponsewith aString messagefield (e.g., "Fetching data...") - A
Successclass that extendsNetworkResponsewith aString datafield and anint statusCodefield - An
Errorclass that extendsNetworkResponsewith aString errorMessagefield and anint errorCodefield
- A sealed class called
main.dart: Import your response file and create a function that handles all response types using a switch expression:- Create a function
handleResponse(NetworkResponse response)that returns aString - Use a switch expression with pattern matching to handle each case:
- For
Loading: returnStatus: [message] - For
Success: returnSuccess ([statusCode]): [data] - For
Error: returnError ([errorCode]): [errorMessage]
- For
- Create three response instances and print the result of handling each:
- A
Loadingwith messagePlease wait... - A
Successwith dataUser profile loadedand status code200 - An
Errorwith error messageNot foundand error code404
- A
- Create a function
The beauty of sealed classes is that the compiler ensures you handle every possible response type - no default case needed!
Expected output:
Status: Please wait...
Success (200): User profile loaded
Error (404): Not foundCheat sheet
Sealed classes restrict which classes can extend or implement them. Only classes in the same file can be direct subtypes, creating a closed set of known variants.
sealed class Result {}
class Success extends Result {
final String data;
Success(this.data);
}
class Failure extends Result {
final String error;
Failure(this.error);
}Sealed classes enable exhaustiveness checking in switch expressions. The compiler knows all possible subtypes and ensures you handle each one:
String handleResult(Result result) {
return switch (result) {
Success(data: var d) => 'Got: $d',
Failure(error: var e) => 'Error: $e',
};
// No default case needed - all cases are covered
}If you add a new subtype, the compiler flags every switch statement that doesn't handle it. This makes sealed classes ideal for modeling states, results, or any fixed set of variants where you want compile-time guarantees that all cases are handled.
Try it yourself
import 'response.dart';
// TODO: Create a function handleResponse(NetworkResponse response)
// that returns a String
// Use a switch expression with pattern matching to handle each case:
// - For Loading: return "Status: [message]"
// - For Success: return "Success ([statusCode]): [data]"
// - For Error: return "Error ([errorCode]): [errorMessage]"
void main() {
// TODO: Create a Loading instance with message "Please wait..."
// TODO: Create a Success instance with data "User profile loaded" and status code 200
// TODO: Create an Error instance with error message "Not found" and error code 404
// TODO: Print the result of handling each response
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesLibraries & ImportsIntroduction to OOPClasses vs ObjectsThe this KeywordMethodsInstance VariablesConstructor BasicsRecap - Simple Calculator4Null Safety
Intro to Null SafetyNullable vs Non-NullableThe ? and ! OperatorsLate Keyword & Null SafetyNull-Aware OperatorsNull Safety in ClassesRecap - User Profile System7Abstract Classes & Interfaces
Abstract ClassesAbstract MethodsInterfaces in DartImplicit InterfacesImplementing vs ExtendingMultiple InterfacesRecap - Shape Calculator10Collections & Generics
List, Set, Map OverviewType-Safe CollectionsGeneric ClassesGeneric MethodsGeneric ConstraintsIterable & IteratorRecap - Generic Storage13Advanced OOP Concepts
Composition vs InheritanceExtension MethodsCallable ClassesSealed Classes (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums with Methods16Project: Library Management
Project OverviewBook and User Classes2Constructors in Dart
Default ConstructorNamed ConstructorsInitializer ListsConstant ConstructorsFactory ConstructorsRedirecting ConstructorsRecap - Shape Builder5Encapsulation
Public vs Private MembersThe _ Prefix ConventionLibrary-Level PrivacyGetters & Setters DepthInformation HidingRecap - Student Records8Mixins
Introduction to MixinsCreating MixinsUsing Multiple Mixinson Keyword in MixinsMixin vs InheritanceMixin vs InterfaceRecap - Animal System11Special Methods
toString() OverridehashCode & == OverrideComparable Interfacecall() MethodnoSuchMethod OverrideRecap - Custom Collection14Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern3Class Properties
Instance vs Static MembersFinal & Const FieldsLate VariablesStatic Methods & FieldsGetters and SettersRecap - Bank Account Manager6Inheritance
Basic InheritanceThe super KeywordMethod OverridingThe @override AnnotationThe final Class KeywordConstructors & InheritanceRecap - Employee Hierarchy9Polymorphism
Polymorphism BasicsPolymorphism via InterfacesRuntime Type CheckingThe is & as OperatorsCovariant KeywordRecap - Payment Processor