Watch the video (12 min)
On this page23 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. The Problem: An Open Family
  5. 2. Declaring a Sealed Class
  6. 3. A Sealed Class Is Implicitly Abstract
  7. 4. Subtypes Must Live in the Same File
  8. 5. Exhaustive switch Without _
  9. 6. A Missing Subtype Is an Error
  10. 7. Object Patterns in the Cases
  11. 8. Modelling Outcomes: PaymentResult
  12. 9. Result<T> Revisited as a Sealed Type
  13. 10. A New Subtype: The Compiler Finds Every switch
  14. 11. A Real-World Example
  15. Code Examples
  16. Common Beginner Mistakes
  17. Best Practices
  18. Mini Challenge
  19. Practice Questions
  20. Practice Answers
  21. Quick Revision
  22. Key Takeaways
  23. What's Next?

Lecture 82 — Sealed Classes

Category13 — Patterns & Modern Dart
Lecture082 of 140
Estimated video length12 min
TargetDart 3.12.2
LevelIntermediate

A switch over subclasses always needed _, because anyone could add another subclass. sealed closes the family, so Dart knows every subtype and tells you when a case is missing.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error and warning in this lecture is real. Errors come from dart run, warnings from dart analyze, on Dart 3.12.2.


Prerequisites

Students should already understand, from Lectures 053–081:

  • classes, constructors, extends, and files connected with import (Lectures 053–060)
  • abstract classes and polymorphism (Lectures 062 and 065)
  • enums and exhaustive switches on them (Lecture 067)
  • generic classes, including the Result<T> holder (Lecture 073)
  • object patterns and pattern switch with guards (Lectures 080–081)

Connection With Previous Lectures

In Lecture 081, Dart checked that a switch on bool, an enum, or a record covered every case. A switch over an ordinary class's subclasses still needed _ — and that _ hides mistakes: a new subclass quietly falls into it. We need a class family Dart knows is complete.


Learning Objectives

By the end of this lecture you will be able to:

  • Declare a sealed class and its subclasses
  • Explain why a sealed class is implicitly abstract
  • Recognize that subtypes must be declared in the same file (library)
  • Write exhaustive switches over subtypes with no _ arm
  • Read the error Dart gives when a subtype is missing
  • Use object patterns to read each subtype's fields in its case
  • Model results such as Success and Failure, including a sealed Result<T>
  • Let the compiler find every switch to update when a new subtype is added

1. The Problem: An Open Family

Here is an abstract Shape with two subclasses, and a switch covering both:

dart
abstract class Shape {}

class Circle extends Shape {}

class Square extends Shape {}

void main() {
  Shape shape = Circle();
  // var name = switch (shape) {
  //   Circle() => 'circle',
  //   Square() => 'square',
  // }; // ✗
}
output
Error: The type 'Shape' is not exhaustively matched by the switch cases since it doesn't match 'Shape()'.
Try adding a wildcard pattern or cases that match 'Shape()'.

Dart is right: any other file could add class Triangle extends Shape, and this switch would have nothing for it.


2. Declaring a Sealed Class

Write sealed in front of class. The subclasses are written exactly as before:

Analogy — a sealed class is a guest list printed on the invitation. Nobody can join later, so the host knows every guest in advance.

dart
sealed class Shape {}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);
}

class Square extends Shape {
  final double side;
  Square(this.side);
}

void main() {
  Shape shape = Square(4);
  print(shape is Square);
}
output
true

A sealed class can hold fields, constructors, and methods, like any abstract class (Lecture 062).


3. A Sealed Class Is Implicitly Abstract

You never write abstract sealed: a sealed class is abstract automatically, so you cannot create one directly:

dart
sealed class Shape {}

class Circle extends Shape {}

void main() {
  // var oops = Shape(); // ✗
  var shape = Circle();
  print(shape);
}
output
Error: The class 'Shape' is abstract and can't be instantiated.

Every real object must be one of the subtypes.


4. Subtypes Must Live in the Same File

Every class that extends or implements a sealed class must be in the same library — the same .dart file (Lecture 057). Put Shape in shapes.dart:

shapes.dart

dart
sealed class Shape {}

class Circle extends Shape {}

Now try to add subtypes from main.dart:

main.dart

dart
import 'shapes.dart';

// class Triangle extends Shape {} // ✗
// class Hexagon implements Shape {} // ✗

void main() {
  print(Circle());
}
output
main.dart:3:24: Error: The class 'Shape' can't be extended, implemented, or mixed in outside of its library because it's a sealed class.
main.dart:4:26: Error: The class 'Shape' can't be extended, implemented, or mixed in outside of its library because it's a sealed class.

main.dart can still use Shape and Circle; it just cannot add to the family. So Dart sees every subtype by reading one file. We will study libraries in Lectures 101–102.


5. Exhaustive switch Without _

The switch that failed in section 1 now compiles, with no fallback arm:

dart
sealed class Shape {}

class Circle extends Shape {}

class Square extends Shape {}

String name(Shape shape) => switch (shape) {
  Circle() => 'circle',
  Square() => 'square',
};

void main() {
  print(name(Circle()));
  print(name(Square()));
}
output
circle
square

Circle() is an object pattern with no fields (Lecture 080): it only checks the type. Switch statements on a sealed type must be exhaustive too, as on an enum.


6. A Missing Subtype Is an Error

Add Triangle to the family, but forget it in the switch:

dart
sealed class Shape {}

class Circle extends Shape {}

class Square extends Shape {}

class Triangle extends Shape {}

void main() {
  Shape shape = Triangle();
  // switch (shape) {
  //   case Circle():
  //     print('Round');
  //   case Square():
  //     print('Four corners');
  // } // ✗
}
output
Error: The type 'Shape' is not exhaustively matched by the switch cases since it doesn't match 'Triangle()'.
Try adding a default case or cases that match 'Triangle()'.

Dart names the subtype you forgot, as it does for enums.


7. Object Patterns in the Cases

An object pattern checks the type and reads that subtype's own fields:

dart
sealed class Shape {}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);
}

class Square extends Shape {
  final double side;
  Square(this.side);
}

double area(Shape shape) => switch (shape) {
  Circle(:var radius) => 3.14 * radius * radius,
  Square(:var side) => side * side,
};

void main() {
  print(area(Circle(2)));
  print(area(Square(3)));
}
output
12.56
9.0

radius is a double inside its arm — no is check. Unlike an enum value, each Circle can hold its own radius.


8. Modelling Outcomes: PaymentResult

Sealed classes shine when something can end in a few different ways, each with its own details:

dart
sealed class PaymentResult {}

class Success extends PaymentResult {
  final String receipt;
  Success(this.receipt);
}

class Failure extends PaymentResult {
  final String reason;
  Failure(this.reason);
}

PaymentResult pay(int balance, int amount) => amount <= balance
    ? Success('RCPT-${balance - amount}')
    : Failure('Not enough money');

void main() {
  for (var amount in [300, 900]) {
    var message = switch (pay(500, amount)) {
      Success(:var receipt) => 'Paid. Receipt $receipt',
      Failure(:var reason) => 'Declined: $reason',
    };
    print(message);
  }
}
output
Paid. Receipt RCPT-200
Declined: Not enough money

A Success has no reason and a Failure has no receipt, so no field is ever null.


9. Result<T> Revisited as a Sealed Type

Lecture 073's Result<T> had two nullable fields and an isOk flag. As a sealed generic family, each case holds only what it needs:

dart
sealed class Result<T> {}

class Ok<T> extends Result<T> {
  final T value;
  Ok(this.value);
}

class Fail<T> extends Result<T> {
  final String error;
  Fail(this.error);
}

Result<int> parseMarks(String text) {
  var marks = int.tryParse(text);
  return marks == null ? Fail('"$text" is not a number') : Ok(marks);
}

void main() {
  for (var input in ['85', 'abc']) {
    print(switch (parseMarks(input)) {
      Ok(:var value) => 'Marks: $value',
      Fail(:var error) => 'Error: $error',
    });
  }
}
output
Marks: 85
Error: "abc" is not a number

value is a non-nullable int, reachable only in the Ok arm. Exceptions start in Lecture 083.


10. A New Subtype: The Compiler Finds Every switch

The biggest payoff comes when the family grows. Add a Triangle to a family with two switches:

dart
sealed class Shape {}

class Circle extends Shape {}

class Square extends Shape {}

class Triangle extends Shape {} // new

// String name(Shape s) => switch (s) {
//   Circle() => 'circle',
//   Square() => 'square',
// }; // ✗

void describe(Shape s) {
  // switch (s) {
  //   case Circle():
  //     print('c');
  //   case Square():
  //     print('s');
  // } // ✗
}

void main() {
  describe(Triangle());
}
output
main.dart:9:33: Error: The type 'Shape' is not exhaustively matched by the switch cases since it doesn't match 'Triangle()'.
Try adding a wildcard pattern or cases that match 'Triangle()'.
main.dart:15:11: Error: The type 'Shape' is not exhaustively matched by the switch cases since it doesn't match 'Triangle()'.
Try adding a default case or cases that match 'Triangle()'.

The error list is a to-do list of every switch to update. With _, both would have compiled and silently mishandled triangles.


11. A Real-World Example

Sara's bank app records three kinds of transaction, and Dart checks all three are handled:

dart
sealed class Transaction {
  final int amount;
  Transaction(this.amount);
}

class Deposit extends Transaction {
  Deposit(super.amount);
}

class Withdrawal extends Transaction {
  Withdrawal(super.amount);
}

class Transfer extends Transaction {
  final String to;
  Transfer(this.to, super.amount);
}

void main() {
  var history = [Deposit(5000), Withdrawal(1200), Transfer('Ali', 800)];
  var balance = 0;

  for (var t in history) {
    balance += switch (t) {
      Deposit(:var amount) => amount,
      Withdrawal(:var amount) || Transfer(:var amount) => -amount,
    };
    var line = switch (t) {
      Deposit(:var amount) => '+ $amount deposit',
      Withdrawal(:var amount) when amount > 1000 =>
        '- $amount large withdrawal',
      Withdrawal(:var amount) => '- $amount withdrawal',
      Transfer(:var to, :var amount) => '- $amount to $to',
    };
    print('$line  (balance $balance)');
  }
}
output
+ 5000 deposit  (balance 5000)
- 1200 large withdrawal  (balance 3800)
- 800 to Ali  (balance 3000)

amount lives on the parent and Transfer adds to. The guarded Withdrawal arm is backed by an unguarded one (Lecture 081).


Code Examples

Example 1 — Two Subtypes

dart
sealed class Pet {}

class Dog extends Pet {}

class Cat extends Pet {}

void main() {
  Pet pet = Cat();
  var sound = switch (pet) {
    Dog() => 'Woof',
    Cat() => 'Meow',
  };
  print(sound);
}
output
Meow

Example 2 — Reading Fields

dart
sealed class Shape {}

class Rectangle extends Shape {
  final int width, height;
  Rectangle(this.width, this.height);
}

class Square extends Shape {
  final int side;
  Square(this.side);
}

void main() {
  Shape shape = Rectangle(3, 5);
  var area = switch (shape) {
    Rectangle(:var width, :var height) => width * height,
    Square(:var side) => side * side,
  };
  print('Area: $area');
}
output
Area: 15

Example 3 — A switch Statement

dart
sealed class Vehicle {}

class Car extends Vehicle {}

class Bike extends Vehicle {}

void main() {
  Vehicle v = Bike();
  switch (v) {
    case Car():
      print('Four wheels');
    case Bike():
      print('Two wheels');
  }
}
output
Two wheels

Example 4 — A Shared Field on the Parent

dart
sealed class Message {
  final String from;
  Message(this.from);
}

class TextMessage extends Message {
  final String body;
  TextMessage(super.from, this.body);
}

class Photo extends Message {
  Photo(super.from);
}

void main() {
  Message m = TextMessage('Zayan', 'See you at 5');
  print('From: ${m.from}');
  print(switch (m) {
    TextMessage(:var body) => 'Says: $body',
    Photo() => 'Sent a photo',
  });
}
output
From: Zayan
Says: See you at 5

Example 5 — Guard Plus Fallback

dart
sealed class Delivery {}

class Packed extends Delivery {}

class Shipped extends Delivery {
  final int days;
  Shipped(this.days);
}

class Delivered extends Delivery {}

void main() {
  Delivery d = Shipped(1);
  print(switch (d) {
    Packed() => 'Leaving soon',
    Shipped(days: < 2) => 'Arrives tomorrow',
    Shipped(:var days) => 'Arrives in $days days',
    Delivered() => 'At your door',
  });
}
output
Arrives tomorrow

Example 6 — A Generic Sealed Family

dart
sealed class Lookup<T> {}

class Found<T> extends Lookup<T> {
  final T value;
  Found(this.value);
}

class Missing<T> extends Lookup<T> {}

Lookup<int> findAge(String name) => name == 'Sara' ? Found(21) : Missing();

void main() {
  for (var name in ['Sara', 'Omar']) {
    print(switch (findAge(name)) {
      Found(:var value) => '$name is $value',
      Missing() => 'No age for $name',
    });
  }
}
output
Sara is 21
No age for Omar

Example 7 — Student Marks

dart
sealed class ExamResult {}

class Sat extends ExamResult {
  final int marks;
  Sat(this.marks);
}

class Absent extends ExamResult {}

void main() {
  var results = {'Ali': Sat(82), 'Sara': Sat(45), 'Zayan': Absent()};
  results.forEach((name, result) {
    var verdict = switch (result) {
      Sat(marks: >= 50) => 'Pass',
      Sat() => 'Fail',
      Absent() => 'Absent',
    };
    print('$name: $verdict');
  });
}
output
Ali: Pass
Sara: Fail
Zayan: Absent

Example 8 — Shopping Total

dart
sealed class Discount {}

class Percent extends Discount {
  final int rate;
  Percent(this.rate);
}

class Flat extends Discount {
  final int amount;
  Flat(this.amount);
}

class NoDiscount extends Discount {}

int finalTotal(int total, Discount d) => switch (d) {
  Percent(:var rate) => total - total * rate ~/ 100,
  Flat(:var amount) => total - amount,
  NoDiscount() => total,
};

void main() {
  print(finalTotal(4000, Percent(10)));
  print(finalTotal(4000, Flat(500)));
  print(finalTotal(4000, NoDiscount()));
}
output
3600
3500
4000

Example 9 — Bank Withdrawal

dart
sealed class Withdraw {}

class Approved extends Withdraw {
  final double newBalance;
  Approved(this.newBalance);
}

class Declined extends Withdraw {
  final double shortBy;
  Declined(this.shortBy);
}

Withdraw withdraw(double balance, double amount) =>
    amount <= balance ? Approved(balance - amount) : Declined(amount - balance);

void main() {
  for (var amount in [700.0, 1500.0]) {
    print(switch (withdraw(1000, amount)) {
      Approved(:var newBalance) => 'Done. Balance: $newBalance',
      Declined(:var shortBy) => 'Declined. Short by $shortBy',
    });
  }
}
output
Done. Balance: 300.0
Declined. Short by 500.0

Example 10 — Temperature Readings

dart
sealed class Reading {
  final double value;
  Reading(this.value);
}

class Celsius extends Reading {
  Celsius(super.value);
}

class Fahrenheit extends Reading {
  Fahrenheit(super.value);
}

double toCelsius(Reading r) => switch (r) {
  Celsius(:var value) => value,
  Fahrenheit(:var value) => (value - 32) * 5 / 9,
};

void main() {
  print(toCelsius(Celsius(25)));
  print(toCelsius(Fahrenheit(212)));
}
output
25.0
100.0

Common Beginner Mistakes

1. Extending a sealed class from another file — gives The class 'Shape' can't be extended, implemented, or mixed in outside of its library because it's a sealed class. (section 4). Move the subclass into the sealed class's file.

2. Creating the sealed parent — Shape() gives The class 'Shape' is abstract and can't be instantiated. Create a subtype instead.

3. Forgetting the parentheses in a type case

dart
sealed class Shape {}

class Circle extends Shape {}

class Square extends Shape {}

void main() {
  Shape s = Circle();
  // var n = switch (s) {
  //   Circle => 'circle',
  //   Square => 'square',
  // }; // ✗
}
output
Error: The type 'Shape' is not exhaustively matched by the switch cases since it doesn't match 'Circle()'.

Without (), Circle is a constant of type Type, not a type test (dart analyze warns it can never match). Write Circle().

4. Covering only part of a subtype

dart
sealed class Shape {}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);
}

class Square extends Shape {}

void main() {
  Shape s = Circle(3);
  // var size = switch (s) {
  //   Circle(radius: > 10) => 'big circle',
  //   Square() => 'square',
  // }; // ✗
}
output
Error: The type 'Shape' is not exhaustively matched by the switch cases since it doesn't match 'Circle(radius: double())'.
Try adding a wildcard pattern or cases that match 'Circle()'.

Add a plain Circle() arm after the specific one.

5. Adding _ "just in case" — dart analyze warns This case is covered by the previous cases., and the _ would silently swallow future subtypes.


Best Practices

  • Use sealed when a type has a fixed set of kinds that carry different data; use an enum when the kinds carry none.
  • Keep the sealed parent and all its subtypes together in one file.
  • Don't add _ to a switch over a sealed type — let Dart point you to every switch when the family grows.
  • Prefer a sealed Success/Failure family over nullable fields plus an isOk flag.

Mini Challenge

A cinema sells three tickets: Adult (Rs 800), Child with an age (free under 3, otherwise Rs 400), and Senior (Rs 500). Make Ticket sealed and print the total for Adult(), Child(2), Child(9), Senior() — with no _ arm.

output
Total: Rs 1700

Try it yourself before opening the solution.

Show solution
dart
sealed class Ticket {}

class Adult extends Ticket {}

class Child extends Ticket {
  final int age;
  Child(this.age);
}

class Senior extends Ticket {}

void main() {
  var tickets = [Adult(), Child(2), Child(9), Senior()];
  var total = 0;
  for (var t in tickets) {
    total += switch (t) {
      Adult() => 800,
      Child(age: < 3) => 0,
      Child() => 400,
      Senior() => 500,
    };
  }
  print('Total: Rs $total');
}
output
Total: Rs 1700

Practice Questions

Work through these in order — they run from beginner to application to challenge.

Beginner

  1. Declare sealed class Drink with subclasses Tea and Coffee. Store a Coffee() in a Drink variable and print Coffee or Tea with a switch expression.
  2. What happens if you write var d = Drink();? Why?
  3. Can class Juice extends Drink {} be written in a different file from Drink? Explain.

Application

  1. Make sealed class Shape with Circle(radius) and Rectangle(width, height) (both double). Write double perimeter(Shape s) using object patterns and print perimeter(Rectangle(2, 3)). Use 3.14 for π.
  2. Make sealed class LoginResult with LoggedIn(name) and WrongPassword(triesLeft). Print Welcome, Sara for LoggedIn('Sara') and 2 tries left for WrongPassword(2).
  3. Using sealed class Light with Red, Yellow, Green, write a switch statement that prints Stop, Slow down, or Go for Yellow().
  4. Add a fourth subclass Flashing to question 6 without touching the switch. What does Dart report, and how do you fix it?

Challenge

  1. Using question 4's Shape, why does this switch fail to compile? Fix it so it prints small circle for Circle(2):

    dart
    String size(Shape s) => switch (s) {
      Circle(radius: >= 5) => 'big circle',
      Rectangle() => 'rectangle',
    };
  2. Write sealed class Result<T> with Ok<T>(value) and Fail<T>(error), and Result<int> safeDivide(int a, int b) that fails with Cannot divide by zero. Print the results of safeDivide(10, 2) and safeDivide(1, 0) as Result: 5 and Error: Cannot divide by zero.

  3. Zayan's game has sealed class Move with Step(steps), Jump(height), and Rest(). Starting at position = 0, a Step adds its steps, a Jump adds height * 2, and a Rest adds nothing. Process [Step(3), Jump(2), Rest(), Step(1)] and print Position: 8.


Practice Answers

Question 1 — Solution

dart
sealed class Drink {}

class Tea extends Drink {}

class Coffee extends Drink {}

void main() {
  Drink drink = Coffee();
  print(switch (drink) {
    Tea() => 'Tea',
    Coffee() => 'Coffee',
  });
}
output
Coffee

Question 2 — Solution

It does not compile: The class 'Drink' is abstract and can't be instantiated. A sealed class is implicitly abstract.

Question 3 — Solution

No. Subtypes must be in the same library (file); elsewhere Dart says Drink can't be extended outside of its library because it's a sealed class.

Question 4 — Solution

dart
sealed class Shape {}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);
}

class Rectangle extends Shape {
  final double width, height;
  Rectangle(this.width, this.height);
}

double perimeter(Shape s) => switch (s) {
  Circle(:var radius) => 2 * 3.14 * radius,
  Rectangle(:var width, :var height) => 2 * (width + height),
};

void main() {
  print(perimeter(Rectangle(2, 3)));
}
output
10.0

Question 5 — Solution

dart
sealed class LoginResult {}

class LoggedIn extends LoginResult {
  final String name;
  LoggedIn(this.name);
}

class WrongPassword extends LoginResult {
  final int triesLeft;
  WrongPassword(this.triesLeft);
}

String message(LoginResult r) => switch (r) {
  LoggedIn(:var name) => 'Welcome, $name',
  WrongPassword(:var triesLeft) => '$triesLeft tries left',
};

void main() {
  print(message(LoggedIn('Sara')));
  print(message(WrongPassword(2)));
}
output
Welcome, Sara
2 tries left

Question 6 — Solution

dart
sealed class Light {}

class Red extends Light {}

class Yellow extends Light {}

class Green extends Light {}

void main() {
  Light light = Yellow();
  switch (light) {
    case Red():
      print('Stop');
    case Yellow():
      print('Slow down');
    case Green():
      print('Go');
  }
}
output
Slow down

Question 7 — Solution

The program stops compiling:

output
Error: The type 'Light' is not exhaustively matched by the switch cases since it doesn't match 'Flashing()'.
Try adding a default case or cases that match 'Flashing()'.

Add a case Flashing(): arm.

Question 8 — Solution

Circles under radius 5 match no arm (doesn't match 'Circle(radius: double())'). Add a plain Circle() arm:

dart
sealed class Shape {}

class Circle extends Shape {
  final double radius;
  Circle(this.radius);
}

class Rectangle extends Shape {
  final double width, height;
  Rectangle(this.width, this.height);
}

String size(Shape s) => switch (s) {
  Circle(radius: >= 5) => 'big circle',
  Circle() => 'small circle',
  Rectangle() => 'rectangle',
};

void main() {
  print(size(Circle(2)));
}
output
small circle

Question 9 — Solution

dart
sealed class Result<T> {}

class Ok<T> extends Result<T> {
  final T value;
  Ok(this.value);
}

class Fail<T> extends Result<T> {
  final String error;
  Fail(this.error);
}

Result<int> safeDivide(int a, int b) =>
    b == 0 ? Fail('Cannot divide by zero') : Ok(a ~/ b);

void main() {
  for (var r in [safeDivide(10, 2), safeDivide(1, 0)]) {
    print(switch (r) {
      Ok(:var value) => 'Result: $value',
      Fail(:var error) => 'Error: $error',
    });
  }
}
output
Result: 5
Error: Cannot divide by zero

Question 10 — Solution

dart
sealed class Move {}

class Step extends Move {
  final int steps;
  Step(this.steps);
}

class Jump extends Move {
  final int height;
  Jump(this.height);
}

class Rest extends Move {}

void main() {
  var moves = [Step(3), Jump(2), Rest(), Step(1)];
  var position = 0;
  for (var move in moves) {
    position += switch (move) {
      Step(:var steps) => steps,
      Jump(:var height) => height * 2,
      Rest() => 0,
    };
  }
  print('Position: $position');
}
output
Position: 8

Quick Revision

  1. What does sealed tell Dart about a class family?
  2. Why can't you write Shape() when Shape is sealed?
  3. Where must a sealed class's subtypes be declared?
  4. Why does a switch over a sealed type not need _?
  5. What happens to your switches when you add a new subtype?
  6. When would you choose a sealed class over an enum?

Key Takeaways

  • sealed class declares a closed family: Dart knows every direct subtype.
  • A sealed class is implicitly abstract — only its subtypes can be created.
  • Subtypes must be in the same library (file); other files can use the family but not extend or implement it.
  • Switches over a sealed type are checked for exhaustiveness, with no _ needed; Dart names any missing subtype.
  • Object patterns like Circle(:var radius) read each subtype's own fields safely.
  • Sealed families model outcomes cleanly: Success/Failure, or a generic Result<T> with Ok and Fail.
  • Adding a subtype turns every incomplete switch into a compile error — a to-do list from the compiler.

What's Next?

Lecture 083 — Errors vs Exceptions. This closes Category 13. Result<T> returns failure as a value; Dart can also throw it. Category 14 begins by separating errors (bugs to fix) from exceptions (problems a program can expect and handle).


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.