Dart Null Safety: Prevent Runtime Crashes with Compile-Time Guarantees
Dart's sound null safety prevents runtime crashes by distinguishing nullable (String?) from non-nullable (String) types at compile time. Use late for deferred initialization, but avoid excessive ! assertions.
15 Sept 2025, 05:11 UTC

The Problem: Null Reference Errors at Runtime
In pre-null-safe Dart, variables could unexpectedly be null, causing NoSuchMethodError crashes when methods were invoked on null values. Dart's sound null safety system eliminates this class of errors by enforcing non-null guarantees at compile time.
How It Works: Nullable vs Non-Nullable Types
Under null safety, every type is either nullable (marked with ?) or non-nullable by default. This distinction is enforced by the Dart analyzer during compilation:
// Non-nullable: must be initialized before use
late final String name;
void main() {
// Error: 'name' is not initialized
print(name.length); // Compile-time error
name = 'Dart'; // Initialize before access
print(name.length); // OK
}
The late Keyword
Use late for variables that need deferred initialization, such as circular dependencies or late-loaded configuration:
class Database {
late final Connection _connection;
void init() {
_connection = Connection('db.sqlite');
}
void query(String sql) {
// StateError if init() not called
_connection.execute(sql);
}
}
void main() {
final db = Database();
// Error: 'db._connection' is not initialized
db.query('SELECT * FROM users');
}
Practical Example: Safe User Input Handling
Here's a real-world pattern for handling potentially null user input:
class UserProfile {
final String name;
final String? email; // Explicitly nullable
UserProfile({required this.name, this.email});
String get contactInfo {
// Use null-aware operator to safely handle null
return email ?? 'No email provided';
}
}
void processUser(String? inputName, String? inputEmail) {
// Force-unwrap is safe here because we validate first
final name = inputName;
if (name == null || name.isEmpty) {
throw ArgumentError('Name is required');
}
final profile = UserProfile(name: name, email: inputEmail);
print(profile.contactInfo);
}
Common Mistakes and How to Avoid Them
Overusing the Assertion Operator !
The ! operator tells the analyzer to treat a nullable value as non-nullable. This defeats null safety if the value is actually null:
// Dangerous: can crash at runtime
String? maybeName = null;
print(maybeName!.toUpperCase()); // Runtime crash!
// Better: validate first
if (maybeName != null) {
print(maybeName.toUpperCase());
}
// Or use null-aware operators
print(maybeName?.toUpperCase() ?? 'Unknown');
Mixing Null-Safe and Non-Null-Safe Code
When depending on pre-null-safe packages, you'll need to handle the boundary carefully:
// In pubspec.yaml, ensure compatibility
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
http: ^0.13.5 # Null-safe version
Verification: Checking Your Project
Run these commands to verify null safety compliance:
- Analyze your project:
dart analyzeshows null safety warnings - Check dependencies:
dart pub outdated --mode=null-safetyreveals compatibility issues - Create new projects safely:
flutter create --platforms=ios,android my_appenables null safety by default
Limitations to Remember
- Null safety is opt-in per package; migrating requires careful dependency analysis
- The
latekeyword can throwStateErrorif accessed before initialization - Overusing
!reintroduces runtime null exceptions - Mixed-mode projects (null-safe + non-null-safe) require valid package upgrades
Quick Check: Is Your Code Null-Safe?
Run dart analyze in your project root. If you see no warnings related to null values, your code is properly null-safe. For a new Flutter project, run flutter create test_null_safety && cd test_null_safety && dart analyze to verify the template is null-safe.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.