Null safety prevents errors that result from unintentional access of variables set to null.
Nullable Types (?) :
To specify if the variable can be null, then you can use the nullable type operator.
String? carName; // initialized to null by default
int? marks = 36; // initialized to non-null
marks = null; // can be re-assigned to null
You don’t need to initialize a nullable variable before using it. It is initialized to null by default.
The Assertion Operator (!) :
Use the null assertion operator ( ! ) to make Dart treat a nullable expression as non-nullable if you’re certain it isn’t null.
Example:
int? someValue = 30;
int data = someValue!; // This is valid as value is non-nullable
Type Promotion :
Dart’s analyzer, which tells you what compile-time errors and warnings, is intelligent enough to determine whether a nullable variable is guaranteed to have values that are not null.
Dart uses Flow Analysis at runtime for type promotion (flow analysis is a mechanism that determines the control flow of a program).
Example:
int checkValue(int? someValue) {
if (someValue == null) {
return 0;
}
// At this point the value is not null.
return someValue.abs();
}
void main(){
print(checkValue(5));
print(checkValue(null));
}
In the above code, if statement checks if the value is null or not. After the if statement value cannot be null and is treated ( promoted) as a non-nullable value. This allows us to safely use someValue.abs(); instead of someValue?.abs();
Late :
The keyword late can be used to mark variables that will be initialized later, i.e. not when they are declared but when they are accessed. This also means that we can have non-nullable instance fields that are initialized later:
class ExampleState extends State {
late final String word; // non-nullable
@override
void initState() {
super.initState();
// print(word) here would throw a runtime error
word = 'Hello';
}
}
Accessing word before it is initialized will throw a runtime error.