Dart Fundamentals: The Language Behind Flutter · Dart Basics: Variables, Null Safety, Functions and Collections

Sound Null Safety

In Dart, a String can never be null; a String? can. Learn nullable types, the ?. ?? and ??= operators, flow analysis that promotes types after checks, late variables, and why the ! operator should be rare.

Create a free account to save this page, take notes and track your progress. Sign in

What you'll build

A user profile formatter that deals safely with optional data (missing nickname, missing phone number) without a single null crash.

The idea

Dart has sound null safety: types are non-nullable by default.

  • String name can never hold null.
  • String? nickname can be a String or null, and the compiler forces you to handle the null case before using it as a String.

Tools for nullable values:

Syntax Meaning
a?.length null if a is null, otherwise a.length
a ?? 'default' a if not null, else the default
a ??= 'value' assign only if a is null
if (a != null) { a.length } type promotion: inside the check a is a String
a! "trust me, it's not null"; throws if it is
late String token; non-nullable, but assigned later (checked at runtime)

"Sound" means that if the type says non-null, it really is non-null at runtime, so the compiler can optimise and you can't get a null-reference error from those values.

The code

Nullable fields, ?. ?? ??= and type promotion
class Profile {
  final String name;
  final String? nickname;
  final String? phone;
  Profile(this.name, {this.nickname, this.phone});
}

String displayName(Profile p) => p.nickname ?? p.name;

String contactLine(Profile p) {
  final phone = p.phone;
  if (phone == null) return '${p.name}: no phone';
  return '${p.name}: ${phone.replaceAll(' ', '')}'; // promoted to String inside the check
}

late final String greeting; // non-nullable, initialised later

void main() {
  final people = [
    Profile('Ana Silva', nickname: 'Ana', phone: '+351 912 345 678'),
    Profile('Benjamin Okafor'),
  ];

  for (final p in people) {
    print('${displayName(p)} -> ${contactLine(p)}');
    print('  nickname length: ${p.nickname?.length ?? 0}');
  }

  String? theme;
  theme ??= 'dark'; // only assigns when null
  theme ??= 'light'; // ignored: already set
  print('theme: $theme');

  greeting = 'Welcome back';
  print(greeting);

  String? missing;
  try {
    print(missing!.length); // ! throws if the value is null
  } on TypeError {
    print('the ! operator threw a TypeError: the value was null');
  }
}

Output

Ana -> Ana Silva: +351912345678
  nickname length: 3
Benjamin Okafor -> Benjamin Okafor: no phone
  nickname length: 0
theme: dark
Welcome back
the ! operator threw a TypeError: the value was null

Run this on your computer — see “Get Started” for setup.

Common mistakes

  • Sprinkling ! to silence errors. Each one is a potential crash; prefer ??, ?. or an if check.
  • Expecting promotion on fields. Promotion works on local variables (and private final fields); copy a field to a local first, as contactLine does.
  • Overusing late. If the value is never assigned before use, you get a runtime error instead of a compile-time one.
  • Making everything nullable "just in case". Non-null types give you guarantees; use ? only when absence is meaningful.

Key points

  • Types are non-nullable by default; T? allows null.
  • ?., ??, ??= handle nulls concisely; if (x != null) promotes x.
  • ! asserts non-null and throws if wrong: use rarely.
  • late defers initialisation of non-nullable variables.

Further reading: Sound null safety, Understanding null safety.

Practice questions

What does 'sound null safety' mean in Dart?

Answer

Types are non-nullable unless marked with ?, and the compiler guarantees that a non-nullable variable can never contain null at runtime, so null-reference errors can only come from nullable values you explicitly handle (or from ! / late misuse).

Why doesn't type promotion work on a nullable class field?

Answer

Another piece of code (or a getter override) could change the field between the null check and its use, so the compiler can't prove it's still non-null. Copy it into a local variable first (promotion also works on private final fields).

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?