Sitemap
Nisarg Ratani

Nisarg Ratani

Flutter & Node.js developer with 6+ years of experience. Published few articles on Medium, sharing practical insights on Flutter, Dart, and Node.js.

Sealed Classes and Enhanced Enums in Dart: Build Type-Safe Flutter Applications

3 min readSep 16, 2026

--

Introduction

Modern Dart provides powerful tools for modeling application states:

  • Sealed classes
  • Enhanced enums

These features are extremely useful for:

  • API responses
  • UI states
  • Authentication states
  • Navigation states
  • Result types
  • Business logic

They help eliminate invalid states and make your code safer.

Sealed Classes

A sealed class restricts where subclasses can be created.

Example:

sealed class AuthState {
const AuthState();
}

Subclasses:

class AuthLoading extends AuthState {}

class Authenticated extends AuthState {
final User user;
Authenticated(this.user);
}

class Unauthenticated extends AuthState {}

class AuthError extends AuthState {
final String message;
AuthError(this.message);
}

Exhaustive Switching

Because Dart knows all possible subclasses, you can use exhaustive switching.

Widget buildAuth(AuthState state) {
return switch (state) {
AuthLoading() =>
const LoadingScreen(),
Authenticated(:final user) =>
HomeScreen(user: user),
Unauthenticated() =>
const LoginScreen(),
AuthError(:final message) =>
ErrorScreen(message: message),
};
}

No default case is required.

If you add another state:

class AuthExpired extends AuthState {}

the compiler can identify places where you forgot to handle it.

sealed vs abstract

An abstract class can be extended outside its library.

A sealed class provides stronger control over the hierarchy.

Use sealed classes when you want a known and controlled set of states.

Other Class Modifiers

Dart also provides:

base
interface
final
sealed

These modifiers help control inheritance and implementation.

Get Nisarg Ratani’s stories in your inbox

Join Medium for free to get updates from this writer.

For Flutter developers, the most commonly useful modifiers are:

  • sealed
  • final
  • base

Understanding them helps when designing SDKs, packages, and architecture libraries.

Enhanced Enums

Traditional enums:

enum Status {
pending,
success,
failed,
}

Enhanced enums allow:

  • Fields
  • Constructors
  • Methods
  • Interfaces

Example:

enum OrderStatus {
pending('Pending'),
processing('Processing'),
completed('Completed'),
cancelled('Cancelled');

const OrderStatus(this.label);
final String label;
}

Usage:

print(OrderStatus.pending.label);

Enum Methods

enum PaymentStatus {
pending,
completed,
failed;

bool get isCompleted {
return this == PaymentStatus.completed;
}
}

Usage:

if (status.isCompleted) {
print('Payment successful');
}

Enums With Behavior

enum UserRole {
admin,
manager,
employee;

bool get canManageUsers {
return switch (this) {
UserRole.admin => true,
UserRole.manager => true,
UserRole.employee => false,
};
}
}

This keeps role-related logic close to the enum.

Enum Interfaces

Enhanced enums can implement interfaces.

abstract interface class Displayable {
String get label;
}

Then:

enum ThemeMode implements Displayable {
light,
dark;

@override
String get label => name;
}

When to Use Sealed Classes vs Enums

Use an enum when:

  • You have a fixed set of simple values.
  • Each value has limited associated data.

Example:

enum ThemeType {
light,
dark,
system,
}

Use sealed classes when states have different data.

Example:

sealed class PaymentState {}

class PaymentLoading extends PaymentState {}

class PaymentSuccess extends PaymentState {
final Payment payment;

PaymentSuccess(this.payment);
}

class PaymentFailure extends PaymentState {
final String message;

PaymentFailure(this.message);
}

Real Flutter Example: API State

sealed class AsyncState<T> {
const AsyncState();
}

class Initial<T> extends AsyncState<T> {}

class Loading<T> extends AsyncState<T> {}

class Success<T> extends AsyncState<T> {
final T data;

const Success(this.data);
}

class Failure<T> extends AsyncState<T> {
final String message;

const Failure(this.message);
}

Usage:

switch (state) {
case Initial():
return const Placeholder();

case Loading():
return const CircularProgressIndicator();

case Success(:final data):
return DataView(data: data);

case Failure(:final message):
return ErrorView(message: message);
}

Best Practices

Use enums for:

  • Status values
  • Categories
  • Roles
  • Small fixed options

Use sealed classes for:

  • UI states
  • API results
  • Events
  • Complex domain states
  • States with different data structures

Avoid representing complex state with multiple booleans:

bool isLoading;
bool hasError;
bool isSuccess;

This can create invalid combinations.

A sealed hierarchy is safer.

Conclusion

Sealed classes and enhanced enums are powerful tools for modeling application state.

They provide:

  • Better type safety
  • Exhaustive switching
  • Clear state modeling
  • Fewer invalid states
  • More maintainable code

For modern Flutter applications, these features should be part of your regular Dart toolkit.

--

--

Nisarg Ratani
Nisarg Ratani

Flutter & Node.js developer with 6+ years of experience. Published few articles on Medium, sharing practical insights on Flutter, Dart, and Node.js.