Records and patterns are Dart 3's biggest additions and make JSON handling and state matching concise and safe.
A record is an anonymous, immutable, fixed-size aggregate: (1, 'a') or with named fields (name: 'Ana', age: 31). Its type is written the same way: (int, String) or ({String name, int age}). Positional fields are read with $1, $2; named ones by name. Records have structural == and hashCode, so they're great for returning several values:
(double, double) minMax(List<double> xs) => (xs.reduce(min), xs.reduce(max));
final (lo, hi) = minMax(data);Patterns describe the shape of a value and destructure it. They appear in:
- Variable declarations:
final (lo, hi) = ...;,var {'name': name} = json;. switchstatements and expressions.if (value case pattern)— match and bind in a condition.forloops:for (final (i, x) in list.indexed).
Pattern kinds include constants, relational (> 0), logical (||, &&), records, lists ([first, ...rest]), maps ({'id': int id}), objects (Point(x: 0, :var y)), wildcards _, casts and null checks (var x?).
Map and list patterns check types as they bind, which makes validating untyped JSON clean: if (json case {'name': String name, 'age': int age}) ... only matches when both keys exist with the right types. A when clause adds a guard: case (var a, var b) when a > b:.
Example
({int ok, int failed}) summarize(List<int> codes) {
var ok = 0, failed = 0;
for (final c in codes) {
if (c case >= 200 && < 300) ok++; else failed++;
}
return (ok: ok, failed: failed);
}
String describe(Object? json) => switch (json) {
{'type': 'user', 'name': String name, 'age': int age} when age >= 18 =>
'adult $name',
{'type': 'user', 'name': String name} => 'user $name',
[int first, ...] => 'list starting with $first',
null => 'nothing',
_ => 'unknown',
};
void main() {
final (:ok, :failed) = summarize([200, 404, 201, 500]);
print('$ok ok, $failed failed'); // 2 ok, 2 failed
print(describe({'type': 'user', 'name': 'Ana', 'age': 31})); // adult Ana
print(describe({'type': 'user', 'name': 'Bo'})); // user Bo
print(describe([7, 8])); // list starting with 7
print((1, 'a') == (1, 'a')); // true
}Remember
- Records
(a, b)/(name: x)bundle values with structural equality - Patterns destructure in declarations, switch, if-case and for
- Map/list patterns type-check while binding — ideal for JSON
Watch out for
- Matching JSON numbers with
intwhen the server may send1.0; usenumand convert.