Introduction
Dart 3 introduced sealed classes — a powerful new feature that enables more robust control over class hierarchies and pattern matching. If you’re coming from traditional Dart or Java/OOP backgrounds, you’re probably familiar with abstract classes. But now, with sealed classes in play, the question arises:
Should I use a sealed class or an abstract class in Dart?
In this in-depth guide, we’ll explore the difference between Dart sealed classes vs abstract classes, when to use each, how they work under the hood, and why sealed classes are revolutionizing Dart’s type system — especially in Flutter and backend Dart development.
What Is an Abstract Class in Dart?
An abstract class in Dart defines a class that cannot be instantiated directly. It’s commonly used as a base class to define interfaces and share functionality between subclasses.
Example:
abstract class Animal {
void speak();
}
class Dog extends Animal {
@override
void speak() => print("Woof!");
}
Key Features:
- Cannot be instantiated
- Can contain abstract and non-abstract methods
- Subclasses must implement abstract methods
- Not type-restrictive (any class can extend)
What Is a Sealed Class in Dart?
A sealed class is a class that restricts which classes can extend or implement it. Introduced in Dart 3, it provides exhaustive control over class hierarchies, making it ideal for pattern matching, state modeling, and domain-driven design.
Read : How to Debounce a Function in Dart?
Example:
sealed class Shape {}
class Circle extends Shape {}
class Rectangle extends Shape {}
Here, only classes in the same file can extend Shape
. This enables Dart to know all possible subclasses at compile time, which is essential for switch
expressions and exhaustive pattern matching.
Sealed Classes vs Abstract Classes – Comparison Table
Feature | Abstract Class | Sealed Class |
---|---|---|
Can be instantiated | ❌ No | ❌ No |
Can have implementations | ✅ Yes | ✅ Yes |
Restricts subclassing | ❌ No | ✅ Yes (file-scope only) |
Ideal for switch/pattern match | ❌ No (not exhaustive) | ✅ Yes (fully exhaustive) |
Open hierarchy | ✅ Yes | ❌ No (sealed to same file) |
Use case | Shared logic/interface | Fixed state modeling, unions |
Introduced in | Dart 1.x+ | Dart 3.x |
Real-World Use Case for Sealed Class
Let’s say you’re building a state model for a Flutter BLoC or Cubit:
sealed class AuthState {}
class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class AuthSuccess extends AuthState {}
class AuthError extends AuthState {
final String message;
AuthError(this.message);
}
Now, in your widget or logic layer, you can pattern match with complete confidence:
void handleAuthState(AuthState state) {
switch (state) {
case AuthInitial():
// Show welcome screen
break;
case AuthLoading():
// Show loading spinner
break;
case AuthSuccess():
// Navigate to home
break;
case AuthError(:final message):
print("Login failed: $message");
break;
}
}
Because the class is sealed, Dart knows no other subclasses can exist, and this switch
is exhaustive — which means safer, bug-free code.
Read Articles : Mastering Extension Methods in Dart – Cleaner Code with Real World Use Cases
When to Use Abstract Classes
Use an abstract class when:
- You want to define a shared interface or contract
- You don’t need subclass restriction
- Your hierarchy spans multiple files or packages
- You need default implementations (e.g., utility methods)
Example:
abstract class Logger {
void log(String message);
void logError(String message) {
log("ERROR: $message");
}
}
When to Use Sealed Classes
Use a sealed class when:
- You want to model fixed sets of types
- You’re using switch statements with pattern matching
- You want to prevent external classes from extending the base class
- You’re designing a state system, event model, or result type
Limitations of Sealed Classes
- Only classes in the same file can extend/implement them
- Not supported in older Dart versions
- Cannot be used across modules/libraries
- Not meant for reusability across features like abstract classes
Read Articles : How to Use Isolates for CPU-Intensive Tasks in Dart?
FAQ – Dart Sealed vs Abstract Classes
Abstract classes define structure but allow open extension. Sealed classes restrict subclassing and allow pattern matching.
Yes. Sealed classes can contain both methods and fields, just like abstract classes.
Not necessarily faster, but they enable safer and more expressive code, especially with pattern matching.
Absolutely. They’re perfect for modeling State
, Events
, or Navigation Routes
in Flutter apps.
No — they solve different problems. Use sealed for fixed hierarchies and abstract for open contracts.
Understanding the difference between Dart sealed classes vs abstract classes is essential for writing clean, safe, and maintainable Dart code — especially in large apps and frameworks like Flutter.
- Use abstract classes for shared behavior and reusability across modules.
- Use sealed classes when your type hierarchy should be restricted and exhaustive — especially when used with pattern matching.
If you’re building state-driven UIs, domain models, or functional-style logic in Dart 3 or Flutter, sealed classes are a powerful new tool worth mastering.