# Dart 3 Cheat Sheet: Null Safety, Records, Patterns and async

Source: https://srvscripts.com/guides/dart-cheat-sheet/
Updated: 2026-10-09
Publisher: srvScripts (https://srvscripts.com/)

**Short answer:** Create a command-line app with `dart create -t console app`, run it with `dart run` and build a native binary with `dart compile exe`. Types are non-nullable unless marked `?`; handle `null` with `??`, `?.` and `if (x != null)`. Use `final` for values set once and `const` for compile-time constants. Dart 3 adds records, pattern matching and sealed classes; Dart 3.13 adds primary constructors. Asynchronous code uses `Future`, `async`/`await` and `Stream`.

The examples in this Dart cheat sheet were run with the Dart SDK 3.13.5 (stable) on Linux on 9 October 2026, and checked with `dart analyze`.

## The dart command

| Command | What it does |
| --- | --- |
| dart create -t console app | New command-line project (bin/, lib/, test/, pubspec.yaml) |
| dart run / dart run bin/app.dart --flag | Run the package or one file with arguments |
| dart compile exe bin/app.dart -o app | Self-contained native binary for the current OS |
| dart pub add http | Add a dependency to pubspec.yaml and fetch it |
| dart pub get / dart pub upgrade | Fetch / upgrade dependencies |
| dart analyze | Static analysis and lints |
| dart format . | Format all Dart files |
| dart fix --apply | Apply automated fixes, e.g. after an SDK upgrade |
| dart test | Run tests in test/ (uses package:test) |
| dart --version | Show the SDK version |

New language features are enabled by the lower bound of the SDK constraint in `pubspec.yaml`. For example, `sdk: ^3.13.0` turns on primary constructors.

## Variables, types and null safety

| Syntax | What it does | Example |
| --- | --- | --- |
| var | Inferred, reassignable | var name = 'web01'; |
| final | Set once at run time | final port = 443; |
| const | Compile-time constant | const maxUsers = 1_000; |
| int, double, num, String, bool | Core types | double ratio = 0.75; |
| '$name:$port' / '${a + b}' | String interpolation |  |
| '''...''' | Multi-line string |  |
| int? maybe; | Nullable type; defaults to null |  |
| maybe ?? 0 | Default when null |  |
| maybe ??= 5; | Assign only if null |  |
| nick?.length | Null-aware access; result is nullable |  |
| maybe! | Assert non-null; throws if it is null |  |
| late String lazy; | Non-nullable, initialised later (error if read first) |  |
| int.tryParse('8080') | Parse, or null on failure (int.parse throws) |  |

## Control flow and functions

```
final msg = switch (code) {          // switch expression
  200 => 'ok',
  301 || 302 => 'redirect',
  >= 400 && < 500 => 'client error',
  _ => 'other',
};

String greet(String name, {String city = 'Leeds', required bool formal}) =>
    formal ? 'Good day $name from $city' : 'Hi $name';

int add(int a, [int b = 0]) => a + b;  // optional positional parameter

greet('Ali', formal: true);
greet('Sam', city: 'York', formal: false);
```

| Syntax | What it does |
| --- | --- |
| for (var i = 0; i < 3; i++) | Classic loop |
| for (final n in list) | Loop over any Iterable |
| while / do { } while | Loops |
| {required String x} | Named parameter that callers must pass |
| => expr | Arrow body for a single expression |
| (int x) => x * x | Anonymous function (closure) |

## Collections

| Syntax | What it does |
| --- | --- |
| final nums = [5, 3, 8]; | List; add, sort(), length, first, contains |
| nums.where((n) => n.isEven).toList() | Filter (lazy until toList()) |
| nums.map((n) => n * n).toList() | Transform |
| nums.fold(0, (a, b) => a + b) | Reduce to one value |
| final ports = {'ssh': 22}; | Map; ports['ssh'] returns int? |
| ports.remove('ssh'), containsKey | Remove / test a key |
| counts.update(w, (v) => v + 1, ifAbsent: () => 1) | Counting |
| final tags = {'a', 'b'}; | Set |
| [...a, ...b] | Spread |
| ['-v', if (debug) '--debug', for (final t in tags) '#$t'] | Collection if and for |
| ['-v', ?extra] | Null-aware element: added only when extra is not null (Dart 3.8+) |
| list..sort()..add(1) | Cascade: several calls on the same object |

## Classes, enums and extensions

```
// Dart 3.13 primary constructor: fields declared in the header
class Server(final String name, var int port) {
  String get address => '$name:$port';
}

class Host {
  final String name;
  final int port;
  Host(this.name, {this.port = 22});
  Host.web(String name) : this(name, port: 443);       // named constructor
  factory Host.fromJson(Map j) =>
      Host(j['name'] as String, port: j['port'] as int);
}

enum Level {
  low(1),
  high(5);

  const Level(this.weight);
  final int weight;
  bool get isHigh => weight > 3;
}

Level lv = .low;      // Dart 3.10 dot shorthand: type is inferred

extension on String {
  bool get isIp => RegExp(r'^\d{1,3}(\.\d{1,3}){3}$').hasMatch(this);
}
```

| Keyword | Meaning |
| --- | --- |
| extends | Inherit from one class |
| implements | Promise to provide another type’s interface |
| with Logger | Mix in a mixin |
| abstract interface class | Can only be implemented, not instantiated |
| sealed class | All subtypes are in the same library, so switch can be exhaustive |
| final class / base class | Restrict extending or implementing outside the library |
| @override | Mark an overriding member |

Dart 3.13 also reserves `final` and `var` on ordinary parameters for primary constructors, so code such as `void f(final int x)` no longer compiles with `sdk: ^3.13.0`.

## Records and pattern matching

```
(String, int) parseAddr(String s) {
  final parts = s.split(':');
  return (parts[0], int.parse(parts[1]));
}
final (host, port) = parseAddr('db1:5432');   // destructuring
final rec = (name: 'web', port: 80);          // named record fields
print(rec.port);

sealed class Event {}
class Login extends Event { Login(this.user, this.ok); final String user; final bool ok; }
class Restart extends Event { Restart(this.service); final String service; }
class Quit extends Event {}

String describe(Event e) => switch (e) {      // exhaustive: no default needed
      Login(user: var u, ok: false) => 'failed login $u',
      Login(:var user) => 'login $user',
      Restart(:var service) => 'restart $service',
      Quit() => 'quit',
    };

final json = {'user': 'bob', 'roles': ['admin']};
if (json case {'user': String u, 'roles': [String first, ...]}) {
  print('$u $first');                          // bob admin
}
```

## Errors and async code

```
class ConfigException implements Exception {
  ConfigException(this.message);
  final String message;
  @override
  String toString() => 'ConfigException: $message';
}

try {
  parsePort('70000');
} on ConfigException catch (e) {
  print(e);
} catch (e, st) {            // anything else, with stack trace
  print('$e\n$st');
} finally {
  print('done');
}

Future fetchStatus(String host) async {
  await Future.delayed(const Duration(milliseconds: 10));
  return host.length;
}

final results = await Future.wait([fetchStatus('a'), fetchStatus('bbb')]);
final v = await fetchStatus('db1').timeout(const Duration(seconds: 5));
```

| Syntax | What it does |
| --- | --- |
| Future + async / await | One value that arrives later |
| Future.wait([...]) | Run several futures and wait for all |
| .timeout(duration) | Throws TimeoutException if too slow |
| Stream + async* / yield | A sequence of values over time |
| await for (final x in stream) | Consume a stream |
| await Isolate.run(() => heavyWork()) | Run CPU-heavy work on another isolate (dart:isolate) |
| rethrow | Re-throw inside a catch block, keeping the stack trace |

## Files, processes and a log example

For server admins: a small `dart:io` tool that counts requests per client IP, compiled to a single binary with `dart compile exe`.

```
import 'dart:io';

Future main(List args) async {
  final path = args.isNotEmpty ? args.first : '/var/log/nginx/access.log';
  final file = File(path);
  if (!await file.exists()) {
    stderr.writeln('cannot read $path');
    exit(1);
  }
  final hits = {};
  for (final line in await file.readAsLines()) {
    final ip = line.split(' ').first;
    hits.update(ip, (n) => n + 1, ifAbsent: () => 1);
  }
  final top = hits.entries.toList()..sort((a, b) => b.value.compareTo(a.value));
  for (final e in top.take(10)) {
    print('${e.value.toString().padLeft(7)} ${e.key}');
  }
}
```

| Syntax | What it does |
| --- | --- |
| File(p).readAsString() / writeAsString(s) | Whole file in or out (async; ...Sync versions exist) |
| File(p).openRead() | Stream a large file instead of loading it |
| Directory(p).list() | List a directory as a stream |
| await Process.run('systemctl', ['is-active', 'nginx']) | Run a command; read exitCode and stdout |
| Platform.environment['HOME'] | Environment variable |
| jsonEncode(obj) / jsonDecode(text) | JSON from dart:convert; classes need a toJson() method |

**Official documentation (checked 9 October 2026):** [Dart language tour](https://dart.dev/language) · [Dart: Patterns](https://dart.dev/language/patterns) · [Dart: Sound null safety](https://dart.dev/null-safety) · [The dart command-line tool](https://dart.dev/tools/dart-tool) · [Dart API reference](https://api.dart.dev/) · [Dart changelog](https://dart.dev/changelog)

## Dart Cheat Sheet at a glance

In short: A compact Dart 3.13 reference covering the dart tool, null safety, collections, classes, records and patterns, errors, async code and dart:io.

Dart Cheat Sheet: what this guide covers.

**Related guides:** [Kotlin Cheat Sheet: Syntax, Null Safety and Collections](https://srvscripts.com/guides/kotlin-cheat-sheet/) · [Swift Cheat Sheet: Syntax, Optionals, Protocols and async](https://srvscripts.com/guides/swift-cheat-sheet/) · [TypeScript Cheat Sheet: Types, Generics and tsconfig](https://srvscripts.com/guides/typescript-cheat-sheet/).
