पाठ 4 / 25

Sound Null Safety

Use nullable and non-nullable types, null-aware operators, late and required correctly.

Null errors caught at compile time

Since Dart 3, sound null safety is always on: a type like String can never be null, and you add ? to allow null (String?). The analyser tracks nullability and refuses to let you call methods on a possibly-null value until you handle the null case, and type promotion means that after if (name != null) the variable is treated as non-nullable inside the block (for local variables). Null-aware operators: ?. (call only if not null), ?? (default value), ??= (assign if null), and the spread ...? for nullable collections. The ! operator asserts a value is not null and throws at run time if it is, so use it sparingly. late marks a non-nullable variable or field that is initialised later, for example in initState in Flutter, with a run-time check on first read. required makes named parameters mandatory. Because the system is sound, the compiler can trust non-nullable types and generate faster code, and a whole class of crashes disappears.

Nullable and non-nullable types

A non-nullable type always holds a value; a nullable type adds the possibility of null that must be handled.

Two boxes: one always filled, another with a dashed outline that may be empty, connected by a question-mark symbol.
Figure 2.1 — String versus String? in Dart's type system.

Handling nullable values

Promotion, null-aware operators and required named parameters.

class Customer {
  Customer({required this.name, this.phone, this.address});
  final String name;
  final String? phone;            // may be null
  final Address? address;
}

class Address {
  Address(this.city);
  final String city;
}

String contactLine(Customer c) {
  final city = c.address?.city ?? 'City not set';   // null-aware access + default
  final phone = c.phone;
  if (phone != null) {
    return '${c.name}, $city, ${phone.replaceAll(' ', '')}';   // promoted to String
  }
  return '${c.name}, $city';
}

void main() {
  final c = Customer(name: 'Asha', address: Address('Pune'));
  print(contactLine(c));

  Map<String, int> stock = {'pen': 10};
  final pens = stock['pen'] ?? 0;                   // map lookups return int?
  stock['ink'] ??= 5;                                // assign only if absent
}

Every ! is a promise you might break

value! throws if value is null at run time. Before reaching for !, try a local variable with a null check (which enables promotion), ?? or an early return.

त्वरित जाँच: In Dart 3, what does the type String (without ?) guarantee?

  • It may be null
  • It is always empty
  • It can never be null
  • It is dynamic
Answer

It can never be null — With sound null safety, non-nullable types cannot hold null.