preface

By the end of this chapter you will know:

  • What is air safety
  • Principles of air safety
  • How do I enable air security
  • The type of air safety
  • Empty assertion operator
  • Newest modifier

Video tutorial

Empty safety video tutorial address

Introduction to Air Safety

Null-safe Type System was introduced in Dart 2.12. If null-safe is enabled, types in code cannot be null by default, meaning that values cannot be null unless the type is declared nullable.

Air security is highly recommended by the government, and now many popular third-party libraries all support air security, so air security is the knowledge we must master.

Principles of air safety

  • ** Default non-nullable: ** Unless you explicitly declare a variable to be nullable, it must be of a non-null type.
  • ** Progressive migration: ** Freedom to choose when and how much code will be migrated, as well as the ability to use mixed-mode empty security, using both air-safe and non-air-safe code in a project.
  • ** Completely reliable: ** all the benefits of code soundness – fewer bugs, smaller binaries, and faster execution.

Enabling air Security

Air safety is available in Dart 2.12 and Flutter 2.0 and can be enabled by specifying Dart SDK version 2.12

environment:
  sdk: "> = 2.12.0 < 3.0.0"
Copy the code

Air safety type

Null security is divided into null and non-null. Null means that variables and parameters can be passed null, while non-null variables and parameters must not be null. In the following code demonstration, we use dart 2.12 to enable null security

  • An error is reported when the variable is null
  • An error is reported when passing parameters as empty
  • The method must return parameters if it needs to, or compile an error

Variables can be null and cannot be null

Declare an empty variable

Here I declare a string attribute named name, but I don’t assign a value, so the memory address for name is an empty string.

String name;
Copy the code

Error message

It prompts that non-null variables must be initialized

Indicates that variables can be null

We can add one after Stirng, okay? “, which means that the name variable can be null, and we don’t get an error when we use the name property, but we get an error when we use the name property, and it says String, okay? Cannot be assigned to a String as follows:

Empty assertion operator

We got an error when we used the name attribute above. We could have used the empty assertion operator instead! Dart does not report errors at compile time. This symbol should not be used in projects unless you know explicitly that it is not null, because our name property is still null, so you will get an error like this at runtime:

======== Exception caught by widgets library =======================================================
The following _CastError was thrown building MyHomePage(dirty, state: _MyHomePageState#5824d):
Null check operator used on a null value

The relevant error-causing widget was: 
  MyHomePage MyHomePage:file:///Users/jm/Desktop/Work/Git/my_project/flutter_null_safety/lib/main.dart:29:13
When the exception was thrown, this was the stack: 
..........
(package:flutter/src/rendering/binding.dart:319:5)
#123    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1143:15)
#124    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1080:9)
#125    SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:863:7)
(elided 4 frames from class _RawReceivePortImpl.class _Timer.and dart:async-patch) = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Parameter can be null and non – null use comparison

Declare a method that requires passing parameters

In the following code, we define a string with the optional parameter name. In the null-safe mechanism, we must ensure that the passed parameter cannot be empty, so we will receive the following error:

_upperCase({String name}) {
  setState(() {
    value.toUpperCase();
  });
}

floatingActionButton: FloatingActionButton(
  onPressed: _upperCase(),
  child: Icon(Icons.add),
), 
Copy the code

Error message

It prompts that the parameter name cannot be of type NULL, but is implicitly converted to NULL. There are two possible solutions to this problem

The solution

First: Add default values to optional parameters

_upperCase({String name = "Jimi"}) {
  setState(() {
    name.toUpperCase();
  });
}
Copy the code

Second: Add required to the optional parameter

_upperCase({required String name}) {
  setState(() {
    name.toUpperCase();
  });
}
Copy the code

If you add required to the optional parameter, name must be passed. If you add required to the optional parameter, name must be passed. If you add required to the optional parameter, name must be passed.

floatingActionButton: FloatingActionButton( onPressed: _upperCase(name: name!) , child: Icon(Icons.add), ),Copy the code

Here we use name! We said above that this is an empty assertion operator, which is used only if you know that the variable will have a value, so we will continue to optimize and declare name as a variable with a value as follows:

String name = "Jimi";

floatingActionButton: FloatingActionButton(
  onPressed: _upperCase(name: name),
  child: Icon(Icons.add),
), 
Copy the code

The return value of a method can be null versus non-null

Declare a method that needs to return a value

Here I declare a method to convert name to uppercase. This code will not report errors until we introduce null security, but after we introduce null security you will receive the following error:

String name = "Jimi";

String _upperCaseName(String name) {
}
Copy the code

Error message

It indicates that the return type may be a type that cannot be NULL, and you can try adding a return or throw statement at the end.

The solution

First: Add a return statement

String _upperCaseName(String name) {
  return name.toUpperCase();
}
Copy the code

Second: add a throw statement

String _upperCaseName(String name) {
  throw "no return";
}
Copy the code

Custom class fields can be null and cannot be null using contrast

Declare a User class

We often use custom classes in the development process, and the attributes in the custom class are not assigned to the first value, so if we introduce null safety, we will get an error, as shown in the following:

class User {
  String name;
  int age;

  setName(String name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }

  setAge(int age) {
    this.age = age;
  }

  getAge() {
    return this.age; }}Copy the code

An error prompt

It tells you that name and age must have an initial value or be marked late.

The solution

The first is to declare that a variable can be null

We declare variables nullable, and we use the null assertion operator! But it also implies that NULL is a useful value for a field, which is the opposite, so it’s not recommended. Let’s look at the second solution, the late modifier.

String? name;
int? age;
Copy the code

The second type: late modifier

Since late involves a lot of content, we will take a chapter to explain it.

Newest modifier

The late modifier is used to constrain variables at run time rather than compile time, which means that late is equivalent to when a constraint is enforced on a variable.

For example, the name field decorated with late in this example is not necessarily initialized; each time it is read, a runtime check is inserted to ensure that it has been assigned. If no value is assigned, an exception is thrown, so adding String to the variable says, “My value is definitely a String,” and adding the late modifier means, “EVERY time I run it, I check to see if it’s true.”

When using the late modifier, the following is summarized:

  • Don’t assign a value to the variable
  • Assign the variable later
  • Variables are assigned before being used
  • If no value is assigned before use, an error will be reported
late String name;
late int age;
Copy the code

The late modifier loads lazily

Lazy loading is also known as lazy initialization. When you modify a variable with late, it will be delayed until the field is first accessed, rather than initialized when the constructor is instantiated. And the initialization content of the instance field is not accessible to this because the new instance object cannot be accessed until all the initialization methods are complete. With late, however, you can access this, call methods, and access instance fields.

Do not use the late modifier

The name attribute calls getUserName() directly. Finally, when we instantiate a User object and get the value of name, let’s look at the console output:

void main() {
  print("Call constructor");
  var user = User();
  print("Get value");
  print("The value obtained is:${user.getName()}");

}

class User {
  String name = getUserName();

  setName(String name) {
    this.name = name;
  }

  getName() {
    return this.name; }}String getUserName() {
  print("Return user name");
  return "Jimi";
}
Copy the code

Console output

Constructor flutter: returns the user's name. Flutter: gets the value. Flutter: gets the valueCopy the code

Use the late modifier

The code is the same as above, except for the late modifier at the definition field. Let’s look at the console output:

void main() {
  print("Call constructor");
  var user = User();
  print("Get value");
  print("The value obtained is:${user.getName()}");

}

class User {
  late String name = getUserName();

  setName(String name) {
    this.name = name;
  }

  getName() {
    return this.name; }}String getUserName() {
  print("Return user name");
  return "Jimi";
}
Copy the code

Console output

Constructor flutter: gets the value. Flutter: returns the user's name. Flutter: gets the value JimiCopy the code

We can obviously see that the second and third lines are reversed, and without the late modifier (i.e., no lazy loading), we call the fetch method directly when we instantiate the constructor. After the late modifier is added, the fetch will only be performed when the field is used.

conclusion

The introduction of empty security has made my code more reliable

  • They all have to be nonempty in terms of type, but you can add them?Becomes nullable, using the null assertion operator!Use.
  • All optional parameters must be non-null and usablerequiredTo build a non-optional named parameter.
  • ListClasses are no longer allowed to contain uninitialized elements.
  • lateModifiers are checked at run time and can use non-null types andfinalIt also provides support for lazy initialization of fields.