Explain Dart records

Medium PriorityAsked in ~50% of Flutter interviews (growing)

3 min read

Dart 3

AspectRecordClass
Has a name❌ Anonymous
Mutable❌ Always immutableConfigurable
Equality✅ Structural (auto)❌ Manual override
Can have methods❌ Just data
Inheritance
Best forMulti-return, light tuples, map keysDomain objects, behaviour

Two records with the same shape and same values are ==. That's the killer feature.


Code in action

// Positional
final pt = (3, 4);
pt.$1;                      // 3
pt.$2;                      // 4

// Named
final user = (name: 'Alice', age: 30);
user.name;                  // 'Alice'
user.age;                   // 30

// Mixed
final mixed = (1, 2, label: 'pair');

// Types
(int, int) point() => (3, 4);
({double lat, double lng}) coord() => (lat: 12.97, lng: 77.59);

// Destructuring
final (x, y) = point();
final (:lat, :lng) = coord();
final (first, _, third) = (1, 2, 3);   // skip middle with _
// Free structural equality
(1, 'a') == (1, 'a');                  // true 🎉
final s = <(int, String)>{(1, 'a'), (1, 'a')};
s.length;                              // 1 — deduped automatically

Killer use case: multi-value returns

// Before — boilerplate class or out-params
class _MinMax { final int min, max; _MinMax(this.min, this.max); }

// Dart 3 — clean and self-documenting
(int min, int max) findMinMax(List<int> xs) =>
    (xs.reduce(min), xs.reduce(max));

final (min, max) = findMinMax([3, 1, 4, 1, 5, 9]);    // (1, 9)

Combined with pattern matching:

String describe((int, int) p) => switch (p) {
  (0, 0)                       => 'Origin',
  (0, _)                       => 'On Y-axis',
  (_, 0)                       => 'On X-axis',
  (var x, var y) when x == y   => 'On diagonal',
  (var x, var y)               => 'Point ($x, $y)',
};

When to reach for records vs classes

You need…Use
Return more than one value from a functionRecord
A composite key for a Map or SetRecord (structural equality)
Throw-away local grouping ("zip" of two lists)Record
Reusable domain concept with behaviour (User.greeting())Class
Mutability or inheritanceClass
Stable JSON schema your team will keep evolvingClass (you'll add methods/validation)

Rule of thumb: records for transient shapes, classes for first-class concepts.


Common mistakes to avoid

// ❌ Treating records like full-blown data classes
final user = (name: 'Alice', age: 30);
user.greet();                          // ❌ records have no methods

// ❌ Mutating fields
user.name = 'Bob';                     // ❌ records are immutable

// ❌ Overusing records and leaking implementation details
({String token, int expiresIn}) login() => ...;
// any rename breaks every caller. For long-lived API surfaces, use a class.

// ❌ Forgetting positional vs named field access
final r = (1, 2);
r.first;                               // ❌ no such getter — use r.$1

// ❌ Assuming type compatibility by shape only with named records
({int a, int b}) one = (a: 1, b: 2);
({int b, int a}) two = one;            // ❌ types differ even if shape matches
// Names matter; order of names does not, but the *set* of names is part of the type.

// ❌ Using a record where a sealed class makes more sense
// "result" with success/failure variants → sealed class, not a record

Interview follow-ups

  1. How is a record different from a tuple in other languages? It's structurally typed with both positional and named fields, and equality / hashCode are built in. It's not a wrapper around a List — $1, $2 access is just sugar over fields, not list indexing.

  2. What does "structural equality" mean for records? Two records are equal if they have the same shape (same positional arity and same named fields) and all corresponding values are ==. No need to write operator == or hashCode.

  3. Can a record be used as a Map key? Yes, and it's one of the best uses — (x, y) → cell for grids, (userId, date) → score for compound keys. Because hashCode is structural, you get the behaviour you want for free.

  4. When would you choose a class over a record? When you need methods, named identity (User vs (String, int)), mutability, inheritance, or a stable API surface. Records shine inside a function or as a return value; classes shine as domain concepts.

How helpful was this content?

Please sign in to rate this article.