Menu
Coddy logo textTech

Records (Dart 3)

Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 86 of 110.

Dart 3 introduced records - a lightweight way to bundle multiple values together without creating a full class. Records are immutable, have built-in equality, and are perfect for returning multiple values from a function or grouping related data temporarily.

Create a record by wrapping values in parentheses:

void main() {
  // A simple record with two values
  var point = (10, 20);
  print(point.$1);  // 10 - access first element
  print(point.$2);  // 20 - access second element
  
  // Records with named fields
  var person = (name: 'Alice', age: 30);
  print(person.name);  // Alice
  print(person.age);   // 30
}

Positional fields are accessed with $1, $2, etc., while named fields use their names directly. You can mix both styles in a single record.

Records shine when returning multiple values from functions - no need to create a class just to return two pieces of data:

(int, int) divideWithRemainder(int a, int b) {
  return (a ~/ b, a % b);
}

(String name, int age) getUser() {
  return ('Bob', 25);
}

void main() {
  var (quotient, remainder) = divideWithRemainder(17, 5);
  print('$quotient remainder $remainder');  // 3 remainder 2
  
  var (name, age) = getUser();
  print('$name is $age');  // Bob is 25
}

Records automatically support equality based on their values, making them convenient for comparisons without implementing == yourself. Use records when you need simple, immutable data groupings; use classes when you need methods, mutability, or more complex behavior.

challenge icon

Challenge

Easy

Let's build a coordinate system using records! You'll create functions that work with 2D points represented as records, demonstrating how records make it easy to return and work with multiple related values without creating full classes.

You'll organize your code into two files:

  • geometry.dart: Create functions that use records to work with coordinate data:
    • A function createPoint(int x, int y) that returns a record with named fields x and y. The return type should be ({int x, int y})
    • A function midpoint(({int x, int y}) p1, ({int x, int y}) p2) that calculates and returns the midpoint between two points as a record with named fields. Use integer division (~/) for the calculation
    • A function distance(({int x, int y}) p1, ({int x, int y}) p2) that returns a record with positional fields: the horizontal distance (difference in x) and vertical distance (difference in y). The return type should be (int, int). Calculate as p2.x - p1.x and p2.y - p1.y
  • main.dart: Import your geometry file and demonstrate records in action:
    • Create two points using createPoint: point A at (2, 4) and point B at (10, 8)
    • Print Point A: ([x], [y]) using the named fields
    • Print Point B: ([x], [y]) using the named fields
    • Calculate the midpoint and print Midpoint: ([x], [y])
    • Calculate the distance and use destructuring to extract the values into variables dx and dy, then print Distance: dx=[dx], dy=[dy]

Notice how records let you return multiple values cleanly - named fields for clarity when the meaning matters, and positional fields when the context is obvious!

Expected output:

Point A: (2, 4)
Point B: (10, 8)
Midpoint: (6, 6)
Distance: dx=8, dy=4

Cheat sheet

Dart 3 introduced records - a lightweight way to bundle multiple values together without creating a full class. Records are immutable and perfect for returning multiple values from a function.

Create a record by wrapping values in parentheses:

// Simple record with positional fields
var point = (10, 20);
print(point.$1);  // 10 - access first element
print(point.$2);  // 20 - access second element

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

Positional fields are accessed with $1, $2, etc., while named fields use their names directly.

Records are ideal for returning multiple values from functions:

// Return type specifies record structure
(int, int) divideWithRemainder(int a, int b) {
  return (a ~/ b, a % b);
}

// Named fields in return type
(String name, int age) getUser() {
  return ('Bob', 25);
}

// Destructuring records
var (quotient, remainder) = divideWithRemainder(17, 5);
print('$quotient remainder $remainder');  // 3 remainder 2

var (name, age) = getUser();
print('$name is $age');  // Bob is 25

Record return types with named fields use curly braces:

({int x, int y}) createPoint(int x, int y) {
  return (x: x, y: y);
}

Records automatically support equality based on their values.

Try it yourself

import 'geometry.dart';

void main() {
  // TODO: Create point A at (2, 4) using createPoint
  
  // TODO: Create point B at (10, 8) using createPoint
  
  // TODO: Print Point A using named fields: Point A: ([x], [y])
  
  // TODO: Print Point B using named fields: Point B: ([x], [y])
  
  // TODO: Calculate the midpoint and print: Midpoint: ([x], [y])
  
  // TODO: Calculate distance and use destructuring to extract into dx and dy
  // Then print: Distance: dx=[dx], dy=[dy]
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming