34 null
Guideline
Optional parameters must be nullable or have a default value. You can use required to make named parameters non-optional. Non-nullable top-level variables and static fields must have initializers. Non-nullable instance fields must be initialized before the constructor body begins.
Let me break down this Dart documentation into clear sections with examples:
34.1 Optional Parameters Must Be Nullable or Have Default Values
Nullable optional parameters:
void greetUser(String name, [String? title]) {
'Hello ${title ?? ''} $name');
print(}
// Usage
'John'); // title is null
greetUser('John', 'Dr.'); // title is 'Dr.' greetUser(
Optional parameters with default values:
void greetUser(String name, [String title = 'Mr.']) {
'Hello $title $name');
print(}
// Usage
'John'); // Uses default 'Mr.'
greetUser('John', 'Dr.'); // Uses 'Dr.' greetUser(
❌ This won’t work (non-nullable without default):
void greetUser(String name, [String title]) { // ERROR!
'Hello $title $name');
print(}
34.2 Using required
for Named Parameters
Without required
(optional and nullable):
class User {
String name;
String? email; // Optional, can be null
{required this.name, this.email});
User(}
// Usage
= User(name: 'John'); // email is null
User user1 = User(name: 'John', email: 'john@example.com'); User user2
With required
(mandatory named parameters):
class User {
String name;
String email;
{required this.name, required this.email});
User(}
// Usage
= User(name: 'John', email: 'john@example.com'); // Both required
User user // User user = User(name: 'John'); // ERROR! email is required
34.3 Non-nullable Top-level Variables and Static Fields Must Have Initializers
Top-level variables:
// ✅ Correct - initialized
String appName = 'MyApp';
int version = 1;
// ❌ Error - not initialized
String appName; // ERROR!
Static fields:
class AppConfig {
// ✅ Correct - initialized
static String appName = 'MyApp';
static int maxUsers = 100;
// ❌ Error - not initialized
static String version; // ERROR!
}
Late initialization (when you can’t initialize immediately):
late String configPath; // Will be set before first use
void main() {
= '/path/to/config'; // Must set before using
configPath // Safe to use now
print(configPath); }
34.4 Non-nullable Instance Fields Must Be Initialized Before Constructor Body
Direct initialization:
class User {
String name = 'Unknown'; // Initialized at declaration
int age = 0;
}
Constructor parameter initialization:
class User {
String name;
int age;
// ✅ Correct - initialized in constructor signature
this.name, this.age);
User(}
Initializer list:
class User {
String name;
String email;
String displayName;
// ✅ Correct - initialized before constructor body
this.name, this.email) : displayName = name.toUpperCase();
User(}
❌ This won’t work (initialized in constructor body):
class User {
String name; // ERROR! Not initialized
String inputName) {
User(= inputName; // Too late! Must be initialized before body
name }
}
Using late
for complex initialization:
class DatabaseConnection {
late String connectionString;
String host, int port) {
DatabaseConnection(// Complex logic that can't be done in initializer list
= 'postgresql://$host:$port/mydb';
connectionString }
}
34.5 Real-world Example Combining All Concepts
// Top-level variable - must be initialized
String appVersion = '1.0.0';
class ApiService {
// Static field - must be initialized
static String baseUrl = 'https://api.example.com';
// Instance fields - must be initialized before constructor body
String apiKey;
String? userAgent; // Nullable, so no initializer required
late HttpClient client; // Will be initialized in constructor body
// Constructor with required and optional parameters
{
ApiService(this.apiKey, // Required named parameter
required this.userAgent, // Optional nullable parameter
String timeout = '30s', // Optional with default value
}) {
// Complex initialization that couldn't be done in initializer list
= HttpClient()
client ..connectionTimeout = Duration(seconds: int.parse(timeout.replaceAll('s', '')))
..userAgent = userAgent ?? 'MyApp/$appVersion';
}
// Method with optional parameters
Future<String> fetchData(String endpoint, [Map<String, String>? headers]) async {
// Implementation here
return 'data';
}
}
The core principle is that Dart’s null safety system ensures all non-nullable variables have values before they can be used, preventing null reference errors at compile time rather than runtime.