Dart Sealed Classes
last modified June 5, 2025
This tutorial explores Dart sealed classes, demonstrating their use in creating type-safe, exhaustive hierarchies for state management and pattern matching.
A sealed class in Dart is a special abstract class marked with the
sealed
keyword. It restricts its subclasses to be defined
within the same library, enabling the compiler to know all possible
subclasses. This allows for exhaustive pattern matching in switch
expressions, ensuring all cases are handled, which enhances type safety
and reduces runtime errors.
Dart Sealed Class Overview
Sealed classes are ideal for modeling finite state hierarchies, such as UI states, network responses, or algebraic data types. They leverage Dart's pattern matching to handle each subclass concisely, often with switch expressions that destructure properties for direct access.
Feature | Description | Example |
---|---|---|
Sealed | Restricts subclasses to same library | sealed class Shape {} |
Subclasses | Fixed set of implementations | class Circle extends Shape {} |
Pattern Matching | Exhaustive switch handling | switch (shape) { case Circle(): ... } |
Basic Sealed Class for Shapes
This example defines a sealed class hierarchy for geometric shapes, using pattern matching to calculate their areas.
sealed class Shape {} class Circle extends Shape { final double radius; Circle(this.radius); } class Square extends Shape { final double side; Square(this.side); } class Triangle extends Shape { final double base; final double height; Triangle(this.base, this.height); } double calculateArea(Shape shape) { switch (shape) { case Circle(:final radius): return 3.14 * radius * radius; case Square(:final side): return side * side; case Triangle(:final base, :final height): return 0.5 * base * height; } } void main() { var circle = Circle(5); var square = Square(4); var triangle = Triangle(3, 6); print('Circle area: ${calculateArea(circle)}'); print('Square area: ${calculateArea(square)}'); print('Triangle area: ${calculateArea(triangle)}'); }
The Shape
sealed class has three subclasses: Circle
,
Square
, and Triangle
. The calculateArea
function uses a switch expression to compute areas, destructuring properties
like radius
for concise access. The compiler ensures all cases
are covered, preventing unhandled states.
$ dart run shapes.dart Circle area: 78.5 Square area: 16.0 Triangle area: 9.0
Network Request States
Sealed classes are useful for modeling network request states, such as loading, success, or error conditions, with type-safe handling.
sealed class HttpState {} class Loading extends HttpState {} class Error extends HttpState { final String message; Error(this.message); } class Loaded extends HttpState { final String data; Loaded(this.data); } void handleState(HttpState state) { switch (state) { case Loading(): print('⏳ Loading...'); case Loaded(:final data): print('✅ Success! Response: $data'); case Error(:final message): print('❌ Error: $message'); } } void main() { handleState(Loading()); handleState(Loaded('Data loaded successfully!')); handleState(Error('Failed to load data.')); }
The HttpState
sealed class represents network states.
Loading
indicates an ongoing request, Loaded
holds response data, and Error
stores an error message. The
handleState
function uses pattern matching to process each
state, ensuring all possibilities are handled exhaustively.
$ dart run http_state.dart ⏳ Loading... ✅ Success! Response: Data loaded successfully! ❌ Error: Failed to load data.
Sealed Class with Factory Constructors
Sealed classes can use factory constructors to create instances of subclasses, providing a clean API for result handling.
sealed class Result<T> { const Result(); factory Result.success(T value) = Success<T>; factory Result.failure(String error) = Failure<T>; } class Success<T> implements Result<T> { final T value; Success(this.value); } class Failure<T> implements Result<T> { final String error; Failure(this.error); } void processResult(Result<int> result) { switch (result) { case Success(:final value): print('Success with value: $value'); case Failure(:final error): print('Failure with error: $error'); } } void main() { Result<int> successResult = Result.success(42); Result<int> failureResult = Result.failure('An error occurred'); processResult(successResult); processResult(failureResult); }
The Result<T>
sealed class models success or failure
outcomes with a generic type. Factory constructors
Result.success
and Result.failure
create
Success
or Failure
instances. The
processResult
function uses pattern matching to handle
results, accessing the value
or error
directly.
$ dart run result.dart Success with value: 42 Failure with error: An error occurred
User Authentication States
Sealed classes can model user authentication states, such as unauthenticated, authenticated, or guest access, with type-safe handling.
sealed class AuthState {} class Unauthenticated extends AuthState {} class Authenticated extends AuthState { final String userId; Authenticated(this.userId); } class Guest extends AuthState { final String sessionId; Guest(this.sessionId); } void handleAuth(AuthState state) { switch (state) { case Unauthenticated(): print('🔓 Please log in to continue.'); case Authenticated(:final userId): print('👤 Logged in as user: $userId'); case Guest(:final sessionId): print('🌐 Guest access with session: $sessionId'); } } void main() { handleAuth(Unauthenticated()); handleAuth(Authenticated('user123')); handleAuth(Guest('session456')); }
The AuthState
sealed class defines authentication states.
Unauthenticated
represents no login,
Authenticated
includes a user ID, and Guest
holds a session ID. The handleAuth
function processes each
state using pattern matching, ensuring all cases are covered.
$ dart run auth_state.dart 🔓 Please log in to continue. 👤 Logged in as user: user123 🌐 Guest access with session: session456
Mathematical Expressions
Sealed classes can represent mathematical expressions, enabling recursive evaluation with pattern matching for complex calculations.
sealed class Expression {} class Number extends Expression { final double value; Number(this.value); } class Add extends Expression { final Expression left; final Expression right; Add(this.left, this.right); } class Multiply extends Expression { final Expression left; final Expression right; Multiply(this.left, this.right); } double evaluate(Expression expr) { switch (expr) { case Number(:final value): return value; case Add(:final left, :final right): return evaluate(left) + evaluate(right); case Multiply(:final left, :final right): return evaluate(left) * evaluate(right); } } void main() { var expr = Add( Number(2), Multiply(Number(3), Number(4)), ); print('Result: ${evaluate(expr)}'); }
The Expression
sealed class models a mathematical
expression tree with Number
, Add
, and
Multiply
subclasses. The evaluate
function
recursively computes the result using pattern matching, handling the
expression 2 + (3 * 4)
to yield 14.
$ dart run expression.dart Result: 14.0
Source
Dart Sealed Classes - language reference
This tutorial demonstrated Dart sealed classes, showcasing their power in creating type-safe, exhaustive hierarchies for state management and pattern matching.
Author
List all Dart tutorials.