# Sound Null Safety — Dart

Source: https://www.skillbyai.com/en/dart/n-null

> 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.](assets/figures/dart/section-2-map.svg) — Figure 2.1 — String versus String? in Dart's type system.

## Handling nullable values

Promotion, null-aware operators and required named parameters.

```dart
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.

**Quiz:** In Dart 3, what does the type String (without ?) guarantee?

- [ ] It may be null
- [ ] It is always empty
- [x] It can never be null
- [ ] It is dynamic

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