Records & Destructuring
Records — Dart 3's Structural Tuples
Records are anonymous, immutable, structural types. They let you bundle multiple values together without defining a full class. Think of them as lightweight, type-safe tuples.
Records were introduced in Dart 3.0 (May 2023). They are one of the most impactful additions to the language since null safety.
Record Syntax
dart// Positional record (int, String) pair = (42, 'hello'); print(pair.$1); // 42 print(pair.$2); // 'hello' // Named fields (much more readable) ({String name, int age}) person = (name: 'JavaShark', age: 25); print(person.name); // 'JavaShark' print(person.age); // 25 // Mixed positional and named (int, {String label}) tagged = (1, label: 'first');
Records Are Value Types
Records have structural equality — two records are equal if all their fields are equal:
dartfinal a = (x: 1, y: 2); final b = (x: 1, y: 2); print(a == b); // true — value equality! // This is unlike most Dart objects, which use reference equality by default
Using Records as Multiple Return Values
This is the killer use case — return multiple values from a function without creating a class:
dart// Before Dart 3: had to use a class, Map, or List // After Dart 3: just use a record! ({String name, int age, bool isActive}) parseUser(Map<String, dynamic> json) { return ( name: json['name'] as String, age: json['age'] as int, isActive: json['is_active'] as bool? ?? false, ); } final user = parseUser({'name': 'Alice', 'age': 30, 'is_active': true}); print(user.name); // Alice print(user.isActive); // true
Destructuring — Pattern Assignment
Dart 3 also added destructuring — extracting values from objects into variables in a single statement:
dart// Record destructuring final (name, age) = ('JavaShark', 25); print(name); // JavaShark print(age); // 25 // Named field destructuring final (:name, :age) = (name: 'Alice', age: 30); // List destructuring final [first, second, ...rest] = [1, 2, 3, 4, 5]; print(first); // 1 print(second); // 2 print(rest); // [3, 4, 5] // Map destructuring final {'name': String userName, 'age': int userAge} = {'name': 'Bob', 'age': 22}; print(userName); // Bob // Object destructuring (using getters) final Point(:x, :y) = Point(3.0, 4.0); print('x=$x, y=$y'); // x=3.0, y=4.0
Practical Pattern — Swap Variables
dartint a = 1; int b = 2; (a, b) = (b, a); // Swap without a temp variable! print('a=$a, b=$b'); // a=2, b=1
Summary
Records solve the "I need to return two things" problem elegantly, without the boilerplate of a dedicated class. Destructuring makes consuming structured data dramatically cleaner. Together, they move Dart closer to the expressiveness of languages like Rust and Swift without sacrificing the familiar OOP model.