Null Object Pattern in Dart: Kill Null Checks

Dart's ?. and ?? treat the symptoms. The Null Object pattern removes the disease: replace scattered null checks with a do-nothing object that just works.

Delete half your if (x != null) branches and ! operators this afternoon — and end up with *fewer* crashes, not more. That's the promise of the Null Object pattern, and it's the most practical answer to how to reduce null checks in Dart: instead of guarding every call site against null, hand every call site an object that is never null but safely does nothing. This guide implements it in Dart 3, compares it against sealed classes, default values, and Option types, and shows the Flutter Theme.of fallback that kills a whole category of null checks. What you'll learn What the Null Object pattern is and the exact problem it solves How to reduce null checks in Dart with a four-step implementation A decision table: null object vs. sealed classes vs. default values vs. Option types A Flutter example: a theme fallback that never nulls Four mistakes that turn the pattern into a bug-hider (and how to avoid them) How to reduce null checks in Dart: the short answer The before-state everyone recognizes — null checks sprayed across the codebase: // ❌ BEFORE: every call site defends itself (Dart 3.13) Logger? logger; void handleOrder(Order order) { if (logger != null) { logger!.log('Order ${order.id} received'); } // ... 14 more `if (logger != null)` scattered through the file } The Null Object pattern replaces the nullable reference with a real object whose methods do nothing: // ✅ AFTER: the null object (Dart 3.13) abstract interface class Logger { void log(String message); } final class ConsoleLogger implements Logger { @override void log(String message) => print('[log] $message'); } final class NullLogger implements Logger { const NullLogger(); @override void log(String message) {} // safe no-op } Logger logger = const NullLogger(); // never null…

Explore