Mobile development: Flutter

Sefism.

Chapter 01

Dart essentials

The parts of Dart 3 that Flutter code leans on every day: null safety, immutable classes, pattern matching, collections, Futures, Streams and isolates.

Sound null safety

Also called null safety

What
A type like String can never hold null; only String? can. The compiler forces you to handle the null case with ?., ??, a null check or late before you use the value.
Why
Null crashes are the most common runtime error in apps. Sound null safety moves them to compile time, so a missing value is handled on purpose instead of crashing a screen.
Dart
String? nickname;
final shown = nickname ?? 'Guest';
final length = nickname?.length;

if (nickname != null) {
  print(nickname.toUpperCase());
}

late final String token;

Watch out

Sprinkling ! to silence the compiler just moves the crash to runtime; handle the null case instead.

var, final and const

What
var can be reassigned, final is set once at runtime, and const is a compile-time constant that is created once and shared.
Why
Prefer final for most variables. const widgets are built once and Flutter can skip rebuilding them, which is a cheap performance win.
Dart
var count = 0;
count++;
final startedAt = DateTime.now();
const padding = EdgeInsets.all(16);
const title = Text('Settings');

Watch out

A final list can still be mutated with add; only const or an unmodifiable list is truly frozen.

Classes and immutable models

Also called immutable model, mixin

What
Dart classes have fields, constructors with named and required parameters, and factory constructors. App models are usually immutable, with a copyWith method to create changed copies.
Why
Immutable models make state changes explicit, which is what Flutter state management libraries expect and what makes bugs easy to trace.
Dart
class User {
  const User({required this.id, required this.name});
  final String id;
  final String name;

  User copyWith({String? name}) =>
      User(id: id, name: name ?? this.name);

  factory User.fromJson(Map<String, dynamic> j) =>
      User(id: j['id'] as String, name: j['name'] as String);
}

Watch out

Two instances with the same fields are not == unless you override equality or use a package such as freezed or equatable.

Sealed classes, records and patterns

Also called switch expression, pattern matching, sealed class

What
Dart 3 adds sealed class families, records such as (int, String) and patterns that destructure values. A switch over a sealed type must cover every subtype.
Why
Modelling states like loading, success and error as a sealed family means the compiler tells you when a screen forgets to handle one.
Dart
sealed class Shape {}
class Circle extends Shape { Circle(this.r); final double r; }
class Square extends Shape { Square(this.s); final double s; }

double area(Shape shape) => switch (shape) {
  Circle(:final r) => pi * r * r,
  Square(:final s) => s * s,
};

final (name, age) = ('Ana', 21);

List, Map and Set

What
The core collections: ordered List, key-value Map and unique Set. Collection if, collection for and the spread operator ... build them inline.
Why
Widget trees are built from lists, so collection if and for let you show or repeat children without temporary variables.
Dart
final scores = <String, int>{'ana': 90, 'li': 72};
final passed = [
  for (final e in scores.entries)
    if (e.value >= 75) e.key,
];
final everyone = {...passed, 'guest'};
final top = scores.values.reduce(max);

Futures, async and await

Also called event loop

What
A Future is a value that arrives later, such as an HTTP response. async marks a function that returns a Future, and await pauses it until the value is ready without blocking the UI.
Why
Every network call, file read and database query in Flutter is a Future, so you need to await them and handle their errors with try and catch.
Dart
Future<String> fetchName() async {
  await Future.delayed(const Duration(seconds: 1));
  return 'Ana';
}

Future<void> main() async {
  try {
    final (a, b) = await (fetchName(), fetchName()).wait;
    print('$a and $b');
  } catch (e) { print('Failed: $e'); }
}

Watch out

Calling an async function without await silently drops its errors; the unawaited_futures lint catches this.

Streams

What
A Stream delivers many values over time, like location updates or database changes. You read it with listen, await for or a StreamBuilder widget, and create one with an async* function.
Why
Anything that changes repeatedly, from a chat feed to a Firestore query, is modelled as a Stream so the UI can update as values arrive.
Dart
Stream<int> countdown(int from) async* {
  for (var i = from; i >= 0; i--) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

final sub = countdown(3).listen(print);
await sub.cancel();

Watch out

Forgetting to cancel a subscription in dispose keeps the listener alive and calls setState on a widget that is gone.

Isolates

What
Dart runs your code on one thread per isolate, and isolates share no memory. Isolate.run executes a function on a separate isolate and returns its result.
Why
Heavy work on the main isolate, like parsing a large JSON file or resizing images, blocks frames and makes the app stutter. Moving it to another isolate keeps scrolling smooth.
Dart
final items = await Isolate.run(() {
  final data = jsonDecode(hugeJson) as List<dynamic>;
  return data.map((e) => Item.fromJson(e)).toList();
});

Watch out

Async code is not parallel: await frees the isolate while waiting for I/O, but CPU-heavy work still blocks it.

Packages and pubspec.yaml

Also called pub.dev, pubspec

What
pubspec.yaml lists your app's dependencies, assets and SDK constraints. Packages come from pub.dev and are added with flutter pub add.
Why
Most features, such as HTTP, storage and notifications, come from packages, so you need to pick well-maintained ones and keep them up to date.
Shell
flutter pub add http
flutter pub add dev:build_runner
flutter pub get
flutter pub outdated

Watch out

Choosing a package by popularity alone; check its pub points, platform support and last release before depending on it.

01 · Dart essentials1 / 8