Quick start, flying safety

Introduction

In Flutter 2.0, an important upgrade is that Dart supports empty security. Alex has carefully translated many articles on air security for us: migration guide, in-depth understanding of air security, etc. through the migration guide, I will also use fps_monitor migration empty security. But after adapting the project, how should we use it in daily development? What is air safety? Now let's do a few exercises to get started quickly and safely.

1, What problem does air safety solve?

To understand what air safety is, we first need to know what air safety has solved for us?

Let's start with an example

void main() {
  String stringNullException;
  print(stringNullException.length);
}

This code will not be prompted at compile time until null security is adapted. But obviously this is a piece of problematic code. In the Debug mode, an empty exception will be thrown and the screen will be red.

I/flutter (31305): When the exception was thrown, this was the stack:
I/flutter (31305): #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)

In release mode, this exception turns the entire screen gray.

This is a typical example. stringNullException is empty without assignment, but we call it length method, resulting in program exception.

After the same code adapts to empty security, an error prompt is given during compilation, and developers can repair it in time.

image.png

So simply put, null security helps us find possible null exceptions in advance in the code editing stage, but this does not mean that the program will not have null exceptions.

2, How to use empty security?

So what is the content of empty security and how should we use it in our daily development? Let's learn through several exercises in Null safety codelab.

1. Non null and nullable types

In null security, all types are non null by default. For example, if you have a variable of type String, it should always contain a String.

If you want a variable of type String to accept any String or null, add a question mark (?) after the type name Indicates that the variable can be empty. For example, a type is String? Any String can be null or empty.

Exercise A: non null and nullable types

void main() {
  int a;
  a = null; // Prompt error, because int a means a cannot be empty
  print('a is $a.');
}

This code declares that variable a is a non empty variable through int, and an error is reported when a = null is executed. Can be modified to int? Type, allowed a to be empty:

void main() {
  int? a; // Indicates that a is allowed to be empty
  a = null; 
  print('a is $a.');
}

Exercise B: nullable types of generic types

void main() {
  List<String> aListOfStrings = ['one', 'two', 'three'];
  List<String> aNullableListOfStrings = [];
  // Error prompt, because the generic String represents non null
  List<String> aListOfNullableStrings = ['one', null, 'three']; 

  print('aListOfStrings is $aListOfStrings.');
  print('aNullableListOfStrings is $aNullableListOfStrings.');
  print('aListOfNullableStrings is $aListOfNullableStrings.');
}

In this exercise, because the type of the aListOfNullableStrings variable is List, which represents a non empty String array, but a null element is provided in the later creation process, resulting in an error. Therefore, null can be changed to another String or represented as an nullable String in a generic type.

void main() {
  List<String> aListOfStrings = ['one', 'two', 'three'];
  List<String> aNullableListOfStrings = [];
  // Array elements are allowed to be empty, so no error will be reported
  List<String?> aListOfNullableStrings = ['one', null, 'three'];

  print('aListOfStrings is $aListOfStrings.');
  print('aNullableListOfStrings is $aNullableListOfStrings.');
  print('aListOfNullableStrings is $aListOfNullableStrings.');
}

2. Empty assertion operator (!)

If you determine that an expression that can be null is not null, you can use the null assertion operator! Make Dart treat it as non empty. By adding! After the expression, it can be assigned to a non empty variable.

Exercise A: null assertion

///The return value of this method may be null
int? couldReturnNullButDoesnt() => -3;

void main() {
  int? couldBeNullButIsnt = 1;
  List<int?> listThatCouldHoldNulls = [2, null, 4];

  // Although the couldBeNullButIsnt variable can be null, it has been given an initial value, so no error will be reported
  int a = couldBeNullButIsnt;
  // The declared element in the list generic type can be empty, which does not match the int b type, and an error is reported
  int b = listThatCouldHoldNulls.first; // first item in the list
  // The above statement indicates that this method may return null, while int c indicates non null, so an error is reported
  int c = couldReturnNullButDoesnt().abs(); // absolute value

  print('a is $a.');
  print('b is $b.');
  print('c is $c.');
}

In this exercise, both the method couldReturnNullButDoesnt and the array listThatCouldHoldNulls are declared through nullable types, but the following variables b and c are declared through non empty types, so an error is reported. You can add at the end of the expression! Indicates that the operation is not null (you must confirm that this expression will not be null, otherwise it may still cause null pointer exception). Modify as follows:

int? couldReturnNullButDoesnt() => -3;

void main() {
  int? couldBeNullButIsnt = 1;
  List<int?> listThatCouldHoldNulls = [2, null, 4];

  int a = couldBeNullButIsnt;
  // add to! The assertion indicates that it is not empty, and the assignment is successful
  int b = listThatCouldHoldNulls.first!; // first item in the list
  int c = couldReturnNullButDoesnt()!.abs(); // absolute value

  print('a is $a.');
  print('b is $b.');
  print('c is $c.');
}

3. Type promotion

Dart's process analysis has been extended to consider zero value. Nullable variables that cannot be empty are treated as non empty variables. This behavior is called type promotion.

bool isEmptyList(Object object) {
  if (object is! List) return false;
  // An error will be reported before null security, because the Object object does not contain isEmpty method
  // After the List statement is analyzed, it will be judged that the List variable is not safe according to the above error type.
  return object.isEmpty; 
}

This code will report an error before null security, because the object variable is of type object and does not contain isEmpty method.

No error will be reported after empty security, because the process analysis will promote the object variable to List type according to the above judgment statement.

Exercise A: assign values explicitly

void main() {
  String? text;
  //if (DateTime.now().hour < 12) {
  //  text = "It's morning! Let's make aloo paratha!";
  //} else {
  //  text = "It's afternoon! Let's make biryani!";
  //}
  print(text);
  // Error message: the text variable may be empty
  print(text.length);
}

In this code, we use String? An nullable variable text is declared, and text is directly used later length. Dart will think this is unsafe, so it will give an error prompt.

However, when we remove the code annotated above, we will not report an error. Because Dart judges the place where text is assigned and thinks that text will not be empty, he promotes text to non empty type (String) and no error will be reported.

Exercise B: empty check

int getLength(String? str) {
  // An error is reported here because str may be empty
  return str.length;
}

void main() {
  print(getLength('This is a string!'));
}

In this example, because STR may be empty, using str.length will prompt an error. Through type promotion, we can modify it as follows:

int getLength(String? str) {
  // Judge that the scene str with empty str is promoted to non empty type
  if (str == null) return 0;
  return str.length;
}

void main() {
  print(getLength('This is a string!'));
}

Judge the scenario where str is empty in advance, so that the type of subsequent str is determined by String? (can be null) is promoted to String (not null), and no error is reported.

3. late keyword

Sometimes variables (such as fields in a class or top-level variables) should be non empty, but they cannot be assigned immediately. In this case, use the late keyword.

When you put late in front of the variable declaration, you will tell Dart the following information:

  • Don't assign values to variables first.

  • It will be assigned a value later

  • You will assign a value to this variable before using it.

  • If the variable is read before it is assigned a value, an error is thrown.

Exercise A: using late

class Meal {
  // If the description variable is not given an initial value directly or in the constructor, an error is reported
  String description;

  void setDescription(String str) {
    description = str;
  }
}
void main() {
  final myMeal = Meal();
  myMeal.setDescription('Feijoada!');
  print(myMeal.description);
}

In this example, the Meal class contains a non empty variable description, but the variable is not given an initial value directly or in the constructor, so an error is reported. In this case, we can use the late keyword to indicate that the variable is a deferred declaration:

class Meal {
  // late statement no error
  late String description;
  void setDescription(String str) {
    description = str;
  }
}
void main() {
  final myMeal = Meal();
  myMeal.setDescription('Feijoada!');
  print(myMeal.description);
}

Exercise B: using late under circular references

class Team {
  // If the non null variable has no initial value, an error is reported
  final Coach coach;
}

class Coach {
  // If the non null variable has no initial value, an error is reported
  final Team team;
}

void main() {
  final myTeam = Team();
  final myCoach = Coach();
  myTeam.coach = myCoach;
  myCoach.team = myTeam;

  print('All done!');
}

Solve the error reporting by adding the late keyword. Note that we do not need to delete final. The variables declared by late final mean that they only need to be set once, and then they become read-only variables.

class Team {
  late final Coach coach;
}

class Coach {
  late final Team team;
}

void main() {
  final myTeam = Team();
  final myCoach = Coach();
  myTeam.coach = myCoach;
  myCoach.team = myTeam;
  print('All done!');
}

Exercise C: late keyword and lazy loading

int _computeValue() {
  print('In _computeValue...');
  return 3;
}

class CachedValueProvider {
  final _cache = _computeValue();
  int get value => _cache;
}

void main() {
  print('Calling constructor...');
  var provider = CachedValueProvider();
  print('Getting value...');
  print('The value is ${provider.value}!');
}

This exercise does not report errors, but you can see the output of running this Code:

Calling constructor...
In _computeValue...
Getting value...
The value is 3!

After printing the first sentence Calling constructor After that, the CachedValueProvider() object is generated. The build process initializes its variable final_ cache = _ Computevalue() so print the second sentence in_ computeValue..., Then print the subsequent statements.

When we're right_ After adding the late keyword to the cache variable, what is the result?

int _computeValue() {
  print('In _computeValue...');
  return 3;
}

class CachedValueProvider {
  // late keyword, which will not be initialized during construction
  late final _cache = _computeValue();
  int get value => _cache;
}

void main() {
  print('Calling constructor...');
  var provider = CachedValueProvider();
  print('Getting value...');
  print('The value is ${provider.value}!');
}

The log is as follows:

Calling constructor...
Getting value...
In _computeValue...
The value is 3!

In log_ computeValue... The execution of the was delayed. In fact_ cache variables are not initialized at the time of construction, but are delayed until they are used.

4, Empty security does not mean that there are no empty exceptions

These exercises also reflect the role of security: empty security helps us find possible empty exceptions in advance in the code editing stage. Note, however, that this does not mean that there are no null exceptions. For example, the following example

void main() {
  String? text;
  print(text);
  // No error will be reported because it is used! The assertion indicates that the text variable cannot be empty
  print(text!.length);
}

Because text Length indicates that the variable text cannot be empty. But in fact, text may be empty for various reasons (for example, json resolves to null), resulting in program exceptions.

The scenario of late keyword above also exists:

class Meal {
  // late states that no error will be reported during editing
  late String description;
  void setDescription(String str) {
    description = str;
  }
}
void main() {
  final myMeal = Meal();
  // Read the uninitialized variable first, resulting in an exception
  print(myMeal.description);
  myMeal.setDescription('Feijoada!');
}

We read in advance before assigning a value to the description, which will also lead to program exceptions.

So it's the same sentence: null security only helps us find possible null exceptions in advance in the code editing stage, but it doesn't mean that the program won't have null exceptions. Developers still need to make perfect boundary judgment on the code to ensure the robust operation of the program!

See here to leave an assignment for you, how to write a single example of the factory in the empty safety, welcome to leave your answer in the comment area, and I will announce the answer next week ~.

If you want to know more about air safety, we recommend:

  • Deep understanding of air safety

  • Null safety codelab

  • Sound air safety

5, Finally, I would like to thank Wu Yanzu and Peng Yuyan for their praise and attention

Thank Alex for his contribution to the empty security document.

image.png

I will also translate in the near future: Null safety codelab welcome to pay attention.

If you are interested in other contents of Flutter, it is recommended to read the wonderful articles in the past:

Double the fluency of ListView!! Flutter Caton analysis and general optimization scheme
Open source will be carried out within this month. Welcome to pay attention

How are Widget, Element and Render trees formed?

Construction process and performance analysis of ListView

In depth analysis · differences in the life cycle of Flutter in different versions

Welcome to search official account: Flutter or runflutter, which is in progress, collects the most detailed guide to Flutter's advance and optimization. Follow me, discuss your questions and get my latest articles~

Keywords: Android Flutter

Added by DrFishNips on Sat, 12 Feb 2022 07:13:06 +0200