๐ŸŽฏ

A language tour

The Versatility of Dart

Mobile, web, desktop, server โ€” one language. Sound null safety, fast compilation, and Flutter's beating heart.

scroll

01 โ€” Sound Null Safety

Null is no longer a surprise

Dart has sound null safety โ€” the type system distinguishes between nullable and non-nullable types, and the compiler enforces the difference. Null pointer exceptions are caught at compile time, not in production.

"Sound null safety is one of the most impactful features we've ever added to Dart. It eliminated an entire class of production crashes that Dart developers used to encounter regularly."

โ€” Dart team, Google
null_safety.dart
// Non-nullable by default
String name = "Alice";
// name = null;  โ† compile error

// Nullable requires the ? suffix
String? maybeNull = null;

// Safe access with ?.
int? len = maybeNull?.length;

// Null-coalescing
String display = maybeNull ?? "unknown";

// Late initialisation โ€” set once, use freely
late String config;
// config used before init โ†’ runtime error, not null crash

// Flow analysis โ€” compiler tracks null checks
void process(String? s) {
  if (s != null) {
    print(s.toUpperCase());  // safe โ€” s is String here
  }
}

Flow analysis means the compiler knows s is non-null inside the if block. No explicit cast needed โ€” the type narrows automatically based on the control flow.


02 โ€” async/await

Asynchronous code that breathes

Dart was designed for UI programming, where blocking the main thread is catastrophic. Its async/await model makes writing non-blocking code as natural as synchronous code โ€” and streams provide first-class support for sequences of async events.

async.dart
import 'dart:convert';
import 'package:http/http.dart' as http;

// Future<T> = a value that will be available asynchronously
Future<String> fetchUser(int id) async {
  final response = await http.get(
    Uri.parse('https://api.example.com/users/$id'),
  );
  final data = jsonDecode(response.body) as Map<String, dynamic>;
  return data['name'] as String;
}

// Stream โ€” sequence of async events
Stream<int> countUp(int max) async* {
  for (var i = 0; i <= max; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

async* and yield create a generator โ€” a function that produces a stream of values lazily. Each yield emits a value and suspends until the next listener requests one.


03 โ€” Extensions

Add methods to any existing type

Dart's extension methods let you add methods to existing types โ€” even types you don't own โ€” without subclassing or modifying the original. This is how Flutter's widget APIs stay clean while remaining extensible.

extensions.dart
// Add methods to String without subclassing
extension StringUtils on String {
  String capitalize() =>
    isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}';

  bool get isEmail => contains('@') && contains('.');

  List<String> words() => split(RegExp(r'\s+'));
}

// Now you can call these on any String
print('hello world'.capitalize());       // 'Hello world'
print('[email protected]'.isEmail);     // true
print('hello world dart'.words());     // ['hello', 'world', 'dart']

// Extend int with duration helpers
extension IntDuration on int {
  Duration get seconds => Duration(seconds: this);
  Duration get minutes => Duration(minutes: this);
}
await Future.delayed(5.seconds);

Extension methods are static โ€” no virtual dispatch, no subclassing. They're resolved at compile time, making them zero-cost abstractions that read like instance methods.


04 โ€” Cascade Notation

Chain operations on a single object

The cascade operator .. lets you chain multiple operations on the same object without a builder pattern or repeated variable references. It's especially valuable in Flutter widget construction.

cascade.dart
// Without cascade โ€” noisy
var buffer = StringBuffer();
buffer.write('Hello');
buffer.write(', ');
buffer.write('world');
buffer.write('!');

// With cascade โ€” clean
var result = StringBuffer()
  ..write('Hello')
  ..write(', ')
  ..write('world')
  ..write('!');

print(result.toString());  // "Hello, world!"

// Null-aware cascade โ€” only if non-null
StringBuffer? maybe;
maybe?..write('safe')..write(' cascade');

The cascade .. returns the original receiver, not the method's return value. This means you can chain void methods โ€” something impossible with regular method chaining.


05 โ€” Records & Patterns

Structured data with destructuring

Dart 3 introduced records โ€” lightweight, anonymous, immutable composite types โ€” and pattern matching. Together they bring modern functional programming idioms to Dart without the ceremony of defining a class for every data shape.

records.dart
// Record type โ€” multiple return values without a class
(int, String) getUser() => (42, 'Alice');

final (id, name) = getUser();  // destructure
print('$name has id $id');

// Named record fields
var point = (x: 3.0, y: 4.0);
print(point.x);  // 3.0

// Pattern matching in switch
switch (getUser()) {
  case (final id, final name) when id > 0:
    print('Valid user: $name');
  default:
    print('Invalid');
}

Records are structurally typed โ€” (int, String) and (int, String) from different libraries are the same type. No named classes required for simple data aggregation.


06 โ€” The Whole Picture

Why Dart is worth knowing

๐Ÿ“ฑ

Flutter

Flutter builds native iOS, Android, web, and desktop apps from a single Dart codebase. Dart is why Flutter is fast.

โšก

AOT & JIT

Debug with hot reload via JIT. Ship with AOT native compilation. One language, two modes, best of both worlds.

๐Ÿ”’

Sound Type System

Not just null safety โ€” the entire type system is sound. If the compiler accepts it, the types are correct.

๐ŸŒ

Dart for Web

Compile to JavaScript or WebAssembly. Server-side with Dart CLI. One language truly across every platform.

๐ŸŽจ

UI-Optimised

Isolates for true parallelism, Streams for reactive UI, async/await for I/O โ€” designed for the UI programming model.

๐Ÿ› ๏ธ

Great Tooling

dart format, dart analyze, dart test โ€” a complete, opinionated toolchain built into the language distribution.