Null Safety
4.1 Non-Nullable by Default
Dart 3 enforces 100% sound null safety: if the type system says a value is non-nullable, it can never be null at runtime.
String name = 'Ana';
// name = null; // ❌ compile error
String? nickname; // nullable: defaults to null
nickname = 'Ace';4.2 Null-Aware Operators
String? input;
int length = input?.length ?? 0; // ?. null-aware access, ?? if-null
input ??= 'default'; // assign only if null
String forced = input!; // null assertion: throws if null (use sparingly)
List<int>? maybeList;
var first = maybeList?.first;
var copy = [...?maybeList]; // null-aware spread
maybeList?..add(1)..add(2); // null-aware cascade
var items = [1, ?input?.length]; // null-aware element: omitted if null (Dart 3.8)4.3 Type Promotion (Flow Analysis)
Promotion works for local variables and private final fields (Dart 3.2+). For public fields or getters, copy them into a local first:
void greet(String? name) {
if (name == null) {
print('Hello, stranger');
return;
}
print('Hello, ${name.toUpperCase()}'); // promoted to String
}
int lengthOf(Object value) {
if (value is String) return value.length; // promoted to String
if (value case List list) return list.length; // pattern promotion (Dart 3)
return 0;
}class User {
String? email;
void send() {
final email = this.email; // local copy enables promotion
if (email != null) sendTo(email);
}
}4.4 required and Null Safety in APIs
void createUser({required String name, String? bio, int age = 18}) {}
createUser(name: 'Ana');4.5 Best Practices
Prefer non-nullable types. Use ? only when "absence" is meaningful.
Avoid ! unless you can prove non-nullness. Prefer ??, early returns, and pattern checks.
Use late for dependency-injected or lifecycle-initialized fields, not to silence the compiler.