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 foundTry 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