DEV Community

kanta13jp1
kanta13jp1

Posted on

Dart 3 New Features — Sealed Classes, Pattern Matching, and Records

Dart 3 New Features — Sealed Classes, Pattern Matching, and Records

Dart 3 changed how we write Dart. Master the three flagship features with real examples.

Records: Return Multiple Values Type-Safely

// Before Dart 3: needed a Map or a dedicated class
Map<String, dynamic> getUser() => {'name': 'Alice', 'age': 30};

// Dart 3: Records are type-safe
(String name, int age) getUser() => ('Alice', 30);

// Named fields
({String name, int age}) getUserNamed() => (name: 'Alice', age: 30);

// Usage
final user = getUser();
print(user.$1);  // 'Alice'
print(user.$2);  // 30

final named = getUserNamed();
print(named.name);  // 'Alice'

// Destructuring
final (name, age) = getUser();
print('$name is $age years old');
Enter fullscreen mode Exit fullscreen mode

Patterns: Structural Matching

// switch expression (returns a value)
String describe(Object value) => switch (value) {
  int n when n < 0  => 'negative',
  int n when n == 0 => 'zero',
  int _             => 'positive',
  String s          => 'string: $s',
  _                 => 'unknown',
};

// List pattern
final [first, second, ...rest] = [1, 2, 3, 4, 5];
print(first);  // 1
print(rest);   // [3, 4, 5]

// Map pattern
final {'name': String name, 'age': int age} = {'name': 'Bob', 'age': 25};
print('$name: $age');

// Object pattern
class Point { final double x, y; const Point(this.x, this.y); }

String describePoint(Point p) => switch (p) {
  Point(x: 0, y: 0)         => 'origin',
  Point(x: var x, y: 0)     => 'x-axis at $x',
  Point(x: 0, y: var y)     => 'y-axis at $y',
  Point(x: var x, y: var y) => '($x, $y)',
};
Enter fullscreen mode Exit fullscreen mode

Sealed Classes: Exhaustive Pattern Matching

// sealed class = subclassable only within the same library
sealed class Shape {}
class Circle    extends Shape { final double radius;        Circle(this.radius); }
class Rectangle extends Shape { final double w, h;          Rectangle(this.w, this.h); }
class Triangle  extends Shape { final double base, height;  Triangle(this.base, this.height); }

// The compiler verifies exhaustiveness — no else needed
double area(Shape shape) => switch (shape) {
  Circle(:final radius)              => 3.14 * radius * radius,
  Rectangle(:final w, :final h)     => w * h,
  Triangle(:final base, :final height) => base * height / 2,
};
// Forget Triangle → compile error (exhaustiveness check)
Enter fullscreen mode Exit fullscreen mode

In Practice: Type-Safe API Response Modeling

sealed class ApiResult<T> {}
class Success<T> extends ApiResult<T> { final T data;    Success(this.data); }
class Failure<T> extends ApiResult<T> { final String message; Failure(this.message); }
class Loading<T> extends ApiResult<T> {}

// Pattern-match in the UI
Widget buildWidget(ApiResult<User> result) => switch (result) {
  Loading()              => const CircularProgressIndicator(),
  Success(:final data)   => UserCard(user: data),
  Failure(:final message) => ErrorText(message),
};
Enter fullscreen mode Exit fullscreen mode

Summary

Records  → type-safe multiple return values: (T1, T2) or ({name: T1, age: T2})
Patterns → switch expression returns a value / List, Map, Object patterns
Sealed   → restrict inheritance + compile-time exhaustiveness / ideal for API state
Enter fullscreen mode Exit fullscreen mode

Combining all three Dart 3 features catches the majority of runtime errors at compile time.

Top comments (0)