Menu
Coddy logo textTech

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 icon

Challenge

Easy

Let'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 NetworkResponse that serves as the base for all response types
    • A Loading class that extends NetworkResponse with a String message field (e.g., "Fetching data...")
    • A Success class that extends NetworkResponse with a String data field and an int statusCode field
    • An Error class that extends NetworkResponse with a String errorMessage field and an int errorCode field
  • 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 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]
    • Create three response instances and print the result of handling each:
      • A Loading with message Please wait...
      • A Success with data User profile loaded and status code 200
      • An Error with error message Not found and error code 404

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 found

Try 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
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming