DEV Community

Cover image for Flutter Enums in Flutter with Sealed Classes in Dart 3
مصعب يوسف
مصعب يوسف

Posted on • Edited on

Flutter Enums in Flutter with Sealed Classes in Dart 3

Enums have long been a reliable and expressive way to represent a set of constant values in both Dart and Flutter. With the arrival of Dart 3, enums are no longer just simple constants — they now support advanced features that make them incredibly versatile. When used alongside sealed classes, another exciting Dart 3 addition, enums can help you build cleaner, more robust, and type-safe application logic.

In this expanded article, we’ll dive into:

  • What are enums in Dart, and why they matter
  • A look at Dart 3’s powerful enum upgrades
  • Introduction to sealed classes and union types
  • Real-world examples combining Enums with sealed classes for elegant modeling in Flutter apps

What Are Enums in Dart?

Enums (short for “enumerations”) are a special kind of type in Dart that represent a fixed set of named values. They are particularly handy for situations where a variable should only take one of a limited number of possible values. Use cases include user roles, app themes, navigation tabs, button states, form field types, and more.

Here’s a basic example of how enums are defined and used in Dart:

enum AppTheme {
  light,
  dark,
  system,
}

void applyTheme(AppTheme theme) {
  switch (theme) {
    case AppTheme.light:
      print("Applying light theme");
      break;
    case AppTheme.dark:
      print("Applying dark theme");
      break;
    case AppTheme.system:
      print("Following system theme");
      break;
  }
}
Enter fullscreen mode Exit fullscreen mode

This approach ensures that only one of the defined values can be used, reducing errors and improving readability.

Dart 3: More Powerful Enums

Dart 3 takes Enumsto a whole new level by allowing them to carry data and implement behavior. These enhancements make enums act more like regular classes, opening new opportunities for modeling domain-specific logic right within an enum.

1. Instance Fields and Methods

Enums can now include constructor parameters, fields, and methods — giving them far more expressive power.

enum AppTheme {
  light("Light"),
  dark("Dark"),
  system("System");

final String displayName;
  const AppTheme(this.displayName);
  void log() => print("Selected theme: $displayName");
}
Enter fullscreen mode Exit fullscreen mode

You can now treat Enums like objects, which allows for cleaner UI representation and logic encapsulation.

2. Mixins and Interfaces

Enums are now capable of implementing interfaces and applying mixins, just like regular classes. This is great for unifying behavior across enum cases.

abstract interface class Loggable {
  void log();
}

enum LogLevel implements Loggable {
  info,
  warning,
  error;
  @override
  void log() => print("Level: $name");
}
Enter fullscreen mode Exit fullscreen mode

This technique makes it easier to integrate Enums into larger application architectures and design patterns.

Sealed Classes & Union Types (Dart 3)

Sealed classes are a new Dart 3 feature designed to model restricted class hierarchies, where all subclasses must be declared in the same file. This guarantees that the compiler knows all possible subclasses, enabling exhaustive checks and powerful pattern matching.

sealed class Result {}

class Success extends Result {
  final String data;
  Success(this.data);
}
class Failure extends Result {
  final String message;
  Failure(this.message);
}
void handle(Result result) {
  switch (result) {
    case Success(:final data):
      print("Success with $data");
      break;
    case Failure(:final message):
      print("Failed: $message");
      break;
  }
}
Enter fullscreen mode Exit fullscreen mode

This makes sealed classes ideal for modeling outcomes, states, or event flows, where every possible case should be handled explicitly.

Combining Enums with Sealed Classes

One of the most compelling use cases is combining enums and sealed classes to build modular validation logic. This approach leads to highly testable, decoupled components.

Step 1: Define an enum for the field type

enum FieldType {
  email,
  password,
  username,
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Define a sealed class hierarchy for validation results

sealed class ValidationResult {}

class Valid extends ValidationResult {}
class Invalid extends ValidationResult {
  final String reason;
  Invalid(this.reason);
}
class Empty extends ValidationResult {}

Enter fullscreen mode Exit fullscreen mode

Step 3: Use both together for validation

ValidationResult validate(String value, FieldType type) {
  if (value.isEmpty) return Empty();

switch (type) {
    case FieldType.email:
      return value.contains('@')
          ? Valid()
          : Invalid("Invalid email format");
    case FieldType.password:
      return value.length >= 6
          ? Valid()
          : Invalid("Password too short");
    case FieldType.username:
      return value.length >= 3
          ? Valid()
          : Invalid("Username too short");
  }
}
Enter fullscreen mode Exit fullscreen mode

This architecture is modular and keeps responsibilities clearly separated: Enums define types, while sealed classes represent outcomes.

💡 Real-World Use Case: Flutter UI State Modeling

A typical scenario where sealed classes shine is in managing UI states in a Bloc or Cubit. This lets you represent each state explicitly and attach data to it when needed.

sealed class FetchState {}

class Loading extends FetchState {}
class Success extends FetchState {
  final List<String> items;
  Success(this.items);
}
class Error extends FetchState {
  final String message;
  Error(this.message);
}
Widget build(BuildContext context) {
  return switch (state) {
    Loading() => const CircularProgressIndicator(),
    Success(:final items) => ListView(
      children: items.map(Text.new).toList(),
    ),
    Error(:final message) => Text('Error: $message'),
  };
}
Enter fullscreen mode Exit fullscreen mode

Using sealed classes in UI state management eliminates the need for nullable types, flags, or combining Enums with data-holder classes — resulting in more elegant and reliable code.

Conclusion

Dart 3’s support for enriched Enums and sealed classes transforms how we model logic and data flow in Flutter apps. This powerful duo enables you to:

  • Represent fixed choices with associated data using Enums
  • Build fully type-safe and exhaustive logic flows with sealed classes
  • Eliminate ambiguity and boilerplate when working with UI states or business logic

If you’re aiming for maintainable, testable, and scalable codebases, these tools should become a core part of your Flutter development workflow. Whether you’re building form validators, handling network states, modeling navigation routes, or structuring domain logic, combining Enums with sealed classes provides the clarity and precision modern apps demand.

you can see our blogs in https://medium.com/@ms3byoussef

Top comments (0)