Dart - Null Safety, Flow Analysis, Variables And Keywords
Day 4 of my 100-day Flutter learning journey. This blog will cover my understanding of Null Safety and Flow Analysis and other topics under Dart. Null Safety
Dart Null Safety is a feature that helps developers write better, more reliable code in the Dart programming language. It provides tools that prevent errors caused by missing values or null references.
A Null or Nullable Variable is a variable with no value assigned to it, and could be expressed in Dart as ;
int? b;
int? b = 2;
Non-Nullable Variables are variables that have values assigned to them and could be expressed as;
int b;
var b;
Variables without values assigned to them can cause problems during runtime. Hence null safety checks to see if a variable has a value assigned or not during compilation to prevent you from running the program, i.e., preventing you from having a null exception error. Darts' types are all non-nullable by default.
Nullable And Non-Nullable Values
The only way to set a variable to null is by declaring the type as nullable, which is achieved by inserting ?
after the type i.e. int? b;
or int? b = 2;
.
Flow Analysis
Flow Analysis is the ability of Darts' analyzer to scan through the running flow of an app in many ways.
Let's say you want to add our nullable variable b int? b;
to a non-nullable variable x int x = 2;
This won't be possible since b has no value assigned to it. But with flow analysis, we can perform this task by promoting our operation into an else branch.
int? b;
int x = 2;
print(b + x) //this will throw a compile time error
if(b == null){
print('b has no value assigned')
} else {
print(b + x) //This is called promotion under flow analysis
}
// No errors will be generated since that will only get to the else branch to add b and x if b isn't null.
Declaring a variable without initializing it will result in an error in pre-null safe Dart. But with flow analysis' Definite Assignment, Dart knows when a variable is assigned after its declaration before being used, i.e. you can declare a variable and give it a value later. Attempting to use the variable before it's initiated will throw a compile-time error.
int y = 10;
int sign;
print(sign) // this will throw a compile time error
if (y >= 0) {
sign = 1;
} else {
sign = -1;
}
print(sign);
//Dart is able to tell the value of sign is 1 and can go ahead and perform the print operation
Null Assertion Operator(!)
The assertion operator allows you to assign a nullable value to a non-nullable variable. For example
int c = 34;
int? mayBe;
if (c = 34) {
//nullable variable = 34
mayBe = c;
}
//if c is not equal to 34 there will be a runtime error
int value = mayBe!;
We use the assertion operator to tell Dart that we are confident mayBe
will not be null before we assign it to value
. Should we be wrong, we'll generate a runtime error.
If-Null Operator (??)
The if-null operator allows you to assign another value to your non-nullable variable if your nullable value is null.
int c = 34;
int? mayBe;
if (c = 34) {
//nullable variable = 34
mayBe = c;
}
//if c is not equal to 34 there will be a runtime error
int value = mayBe!;
//if c is not equal to 34 value will be assigned to 0, preventing a runtime err
int value = maybeValue ?? 0; // Assign 0 as value if maybeValue is null.
print(value);
When to use Assertion(!) Vs If-Null(??)
Use if-null when you have a default value you want to assign should your nullable variable be null.
Use the assertion operator if you are sure your value won’t be null at runtime.
To write better code, think if your variables should be null or not before the declaration.
Variables And Keywords
The are four types of Dart variables, top-level, static, instance, and local. A variable's type depends on where you declare it in your code.
A Top-level variable is not linked to any class or object and can be accessed anywhere in your program.
Static and Instance variables are tied to an object or class. They are often referred to as the fields & properties of a class
A Local variable can only be accessed in a local method or function.
How To Declare a Variable
Top-level nullable variables can be left unassigned at declaration and will be assigned null, so you can allocate them later, before or after using them, but this can lead to a runtime error.
Top-level non-nullable variables must be declared with a value of the same type. Without initialization, Dart will throw an error, preventing compilation. If you’re sure of assigning it a value before using it, you can use the Late modifier, which permits the variable to hold no value till it gets initialized.
You can use the late modifier to declare a variable and still assign it a value instantly. The late modifier will lazily initialize the variable in this case. Marking a variable as late and initializing it at declaration means the initializer will only run right after the variable is accessed and used, not when it is declared.
Static Vs Dynamic Languages
Static languages are checked at compile time, while dynamic languages are checked during execution. Dart and other statically typed languages have a type checker, in this case, Darts' static analyzer, that ensures all types are correct or else compilation fails.
Dynamic languages rely on runtime checks, and type-related errors can only be checked when the program is running.
Advantages Of Static Languages
Discover type-related bugs at compile time. This saves time since bugs are hard to discover in large apps.
Readable Code; static languages rely on variables having the type they were declared as.
Maintainable Code; the type system warns you about breakables if you make changes.
Dart Keywords
Keywords are reserved words with specific meanings and purposes within the Dart language. They cannot be used as identifiers (such as variable names) because they are reserved for peculiar uses. Dart has specific keywords with a superscript from 1 to 3.
Keywords with superscript 1
are called contextual keywords, meaning they only have meaning in specific places.
Superscript 2 keywords
are dubbed as built-in identifiers and can’t be used as classes, type names, or import prefixes.
Superscript 3 keywords
are a few reserved words that are primarily used for asynchronous tasks.
Final Keyword
The
final
keyword permits you to declare a variable and never change its value again.Final
is more restrictive thanvar
and limits the number of possible states, which leads to fewer bugs.Final
is used for read-only variables only.It is best to use
final
instead ofvar
whenever possible since Dart will alert you at compile time if you try to change its values.Using
final
still allows you to state the variable type explicitly.
Const Keyword
Const
is more restrictive thanfinal
.Const
defines a compile-time constant.Const
variables are good for performance enhancement since Dart can optimize the generated code.Use
const
to declare variables initialised with a string literal since all literals are compile-time constants.Const
can’t be used to declare variables with values expressed only at runtime.Const variables can only be set once, too.
In terms of best practices always prefer
const
overfinal
andvar
.
Dynamic Keyword
The dynamic
keyword is used to opt out of type safety. Variables declared dynamic
can take values of any type and can be changed at any time. Because these values can be of various types. dynamic
is best used when working with data in JSON format.
With this info we can tell dynamic
is the least restrictive keyword, followed by var
, final
, then const
.