flutter
/

Dart Records – Lightweight, Immutable Data Structures

Last Sync: Today

On this page

9
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Dart Records – Lightweight, Immutable Data Structures

What are Records?

Introduced in Dart 3.0, records are anonymous, immutable data structures that let you bundle multiple values together without declaring a full class. They are similar to tuples in other languages. Records can have both positional fields (like in a tuple) and named fields. They are perfect for returning multiple values from a function or grouping related data temporarily.

Syntax of Records

Records are written using parentheses () containing a comma‑separated list of expressions. The type of a record is inferred, but you can also annotate it explicitly. There are two styles: positional fields (ordered) and named fields (named).

DARTRead-only
1
// Positional record (tuple-like)
var point = (10, 20);

// Named record
var person = (name: 'Alice', age: 30);

// Mixed (positional and named)
var mixed = (1, 'hello', flag: true);

Creating Records

You can create records using literal syntax. The type is automatically inferred. If you need to specify the type explicitly (e.g., for a variable declaration or function return), you can use the record type syntax: (int, String) for positional, or ({int age, String name}) for named. Named fields are enclosed in curly braces.

DARTRead-only
1
void main() {
  // Positional record
  (int, String) data = (42, 'answer');

  // Named record
  ({int x, int y}) point = (x: 10, y: 20);

  // Mixed
  (int, {String name}) mixed = (100, name: 'Alice');
}

Accessing Record Fields

Fields in a record are accessed differently depending on whether they are positional or named:

    • Positional fields are accessed using the $ operator followed by the field index (starting at 1). For example, record.$1 is the first field.
    • Named fields are accessed using dot notation (e.g., record.name).
DARTRead-only
1
void main() {
  var point = (10, 20);
  print(point.$1); // 10
  print(point.$2); // 20

  var person = (name: 'Alice', age: 30);
  print(person.name); // Alice
  print(person.age);  // 30

  var mixed = (1, 'hello', flag: true);
  print(mixed.$1);      // 1
  print(mixed.$2);      // hello
  print(mixed.flag);    // true
}

Record Types and Type Safety

Records are structural types. Two records are the same type if they have the same shape (same number of fields, same field types, same named field names). The field names matter for named fields; for positional fields, only the order matters.

DARTRead-only
1
void main() {
  (int, String) a = (1, 'hello');
  (int, String) b = (2, 'world');
  // a and b have the same type

  // Different shape → different type
  (int, String) c = (3, 'x');
  (String, int) d = ('x', 3); // Error: type mismatch

  // Named fields must match exactly
  ({int x, int y}) p1 = (x: 1, y: 2);
  ({int y, int x}) p2 = (x: 1, y: 2); // same shape, order doesn't matter
}

Pattern Matching with Records

One of the most powerful features of records is using them with pattern matching (also introduced in Dart 3.0). You can destructure records in variable declarations, switch statements, and more.

DARTRead-only
1
void main() {
  // Destructuring in variable declaration
  var (x, y) = (10, 20);
  print('x=$x, y=$y');

  // With named fields
  var (name: userName, age: userAge) = (name: 'Alice', age: 30);
  print('$userName is $userAge');

  // Destructuring in a loop
  var points = [(1, 2), (3, 4)];
  for (var (x, y) in points) {
    print('Point: $x, $y');
  }
}

Pattern matching also works in switch statements, allowing you to match and destructure records in a single go.

DARTRead-only
1
void describe(Object value) {
  switch (value) {
    case (int a, int b):
      print('Tuple of ints: ($a, $b)');
    case (String s,):
      print('Single string: $s');
    case (name: String n, age: int a):
      print('Person: $n is $a years old');
    default:
      print('Something else');
  }
}

void main() {
  describe((1, 2));
  describe(('hello',));
  describe((name: 'Bob', age: 25));
}

Records vs Classes

    • Records are lightweight, anonymous, and immutable. They are best for temporary grouping of data or returning multiple values from a function.
    • Classes provide names, methods, private fields, and can be extended. Use classes when you need behaviour, encapsulation, or identity beyond the data.

When to Use Records

    • Returning multiple values from a function without creating a wrapper class.
    • Lightweight grouping of related data that is only used locally.
    • As keys in maps (records are value‑based, so two records with same content are equal).
    • In pattern matching for concise data extraction.

Key Takeaways

    • Records are anonymous, immutable data structures introduced in Dart 3.0.
    • They can have positional fields (accessed with $1, $2, …) and named fields (accessed with dot).
    • Record types are structural; two records with the same shape are compatible.
    • Records shine when used with pattern matching (destructuring).
    • Use records for temporary data grouping; use classes for full‑fledged objects.

Try it yourself

void main() {
  // Create a record
  var point = (10, 20);
  print('Coordinates: (${point.$1}, ${point.$2})');

  // Destructure it
  var (x, y) = point;
  print('Destructured: x=$x, y=$y');

  // Named record
  var person = (name: 'Alice', age: 30);
  print('Person: ${person.name}, ${person.age}');
}

Test Your Knowledge

Q1
of 4

How do you access the first positional field of a record `r`?

A
r[0]
B
r.first
C
r.$1
D
r.1
Q2
of 4

What is the type of `var rec = (a: 1, b: 'two');`?

A
Map<String, dynamic>
B
({int a, String b})
C
(int, String)
D
Record
Q3
of 4

Can records contain both positional and named fields?

A
Yes
B
No
C
Only in Dart 3.1+
D
Only with a special syntax
Q4
of 4

How do you destructure a record `(x, y)` in a variable declaration?

A
var {x, y} = rec;
B
var (x, y) = rec;
C
var [x, y] = rec;
D
var x = rec.$1, y = rec.$2;

Frequently Asked Questions

Can records contain mutable fields?

No, records are immutable. Once created, you cannot change their fields. If you need mutability, use a class.

How do I compare two records for equality?

Records are compared by value using ==. Two records are equal if they have the same shape and each corresponding field is equal. This is a value‑based comparison, not reference equality.

Can records have methods?

No, records are pure data containers; they cannot have methods. For behaviour, you should use a class.

What is the maximum number of positional fields in a record?

Dart doesn't enforce a hard limit, but for practical reasons, you should keep the number reasonable (e.g., ≤ 5). If you need many fields, consider using a class with named parameters.

Can I use records as keys in a map?

Yes, because records implement == and hashCode based on their fields, they work well as keys in maps and sets.

Are records available in older Dart versions?

No, records were introduced in Dart 3.0. If you're using an older version, you need to upgrade to Dart 3+.

Previous

dart metadata

Next

dart patterns

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.