乐趣区

关于flutter:Flutter-空安全精讲

前言

看完本章节你将晓得:

  • 什么是空平安
  • 空平安的准则
  • 如何启用空平安
  • 空平安的类型
  • 空断言运算符
  • late 修饰符

视频教程

空平安视频教程地址

空平安介绍

空平安 null-safe type system 是在 Dart 2.12 中引入的,如果开启空平安,默认状况下代码中的类型不能为空,也就是说除非申明该类型是能够为空的,否则值不能为空。

空平安是官网极力推荐的,当初很多风行的第三方库全部都是反对了空平安,所以空平安是咱们必须要把握的常识。

空平安的准则

  • 默认不可空:除非你将变量显式申明为可空,否则它肯定是非空的类型。
  • 渐进迁徙:能够自在地抉择何时进行迁徙,多少代码会进行迁徙,还能够应用混合模式的空平安,在一个我的项目中同时应用空平安和非空平安的代码。
  • 齐全牢靠:对代码的健全性带来的所有劣势——更少的 BUG、更小的二进制文件以及更快的执行速度。

启用空平安

空平安在 Dart 2.12 和 Flutter 2.0 中可用,可通过指定 Dart SDK 版本为 2.12 那么就会开启空平安

environment:
  sdk: ">=2.12.0 <3.0.0"

空平安类型

空平安分可为空和不可为空,可为空就是变量、形参都能够传 null 值,不可为空变量、形参肯定不能为空,咱们在应用空平安的时候会碰到上面三种状况,接下来的代码演示咱们都是 dart 2.12 开启空平安为准

  • 变量为空编译时报错
  • 传递参数时为空编译时报错
  • 办法须要返回参数时必须返回,否则编译时报错

变量可为空和不可为空的应用比照

申明一个空变量

这里我申明了一个变量为 name 的字符串属性,但并没有赋值,所以 name 的内存地址存的是一个空的字符串。

String name;

谬误提醒

它提醒说不可为空的变量肯定要进行初始化

表明变量可为空

咱们能够在 Stirng 前面加一个 ? 号,该符号表明 name 这个变量能够为空,这个时候咱们发现定义时不会呈现报错,然而咱们在应用 name 属性的时候会发现有一个报错,它报错的信息是 String? 不能调配给一个String,如下:

空断言运算符

在下面咱们应用 name 这个属性的时候会呈现一个报错,咱们能够应用空断言运算符 ! 来表明该值不会为空,所以 Dart 在编译时不会报错,该符号在我的项目中尽量不要应用,除非你明确晓得它是不为空的,因为咱们 name 属性还是空的,所以在运行时将会收到如下报错:

======== 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)
====================================================================================================

参数可为空和不可为空的应用比照

申明一个须要传参的办法

上面这段代码咱们定义了一个可选参数为 name 的字符串,在空平安的机制下,咱们必须保障传入的参数不能为空,那么将会收到如下报错:

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

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

谬误提醒

它提醒参数 name 的类型不能为 null 值,然而被隐式转换成了null,咱们碰到这种状况有两种解决方案,咱们接下来看看如果解决

解决办法

第一种:给可选参数增加默认值

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

第二种:给可选参数减少 required

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

当给可选参数减少 required 后,阐明可选参数 name 必传,这样会导致调用处会报错,咱们来在调用时把 name 传进去,如下:

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

这里应用 name! 来进行传入,在下面咱们说过这个是空断言运算符,只有你明确晓得该变量会有值时才应用,那么咱们这里这样应用还是会报错,所以咱们来持续优化,把 name 申明一个有值的变量,如下:

String name = "Jimi";

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

办法的返回值可为空和不可为空的应用比照

申明一个须要有返回值的办法

上面我申明了一个办法用来将 name` 的转换为大写,在咱们没有引入空平安之前下来代码是不会报错的,然而引入空平安机制后你会收到如下报错:

String name = "Jimi";

String _upperCaseName(String name) {}

谬误提醒

它提醒返回类型可能是不能为 null 的类型,能够尝试在最初增加 returnthrow语句。

解决办法

第一种:增加 return 语句

String _upperCaseName(String name) {return name.toUpperCase();
}

第二种:增加 throw 语句

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

自定义类字段可为空和不可为空应用比照

申明一个 User 类

咱们在平时的开发过程中经常会自定义类,而自定义类中的属性一开始都不会赋值,那么咱们引入空平安的状况下将会报错,如下所示:

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;}
}

报错提醒

它这里提醒 name 以及 age 必须要有初始化值,或者标记为late

解决办法

第一种:申明变量可为空

咱们采纳申明变量可为空的形式,在应用的时候咱们应用空断言运算符 ! 来进行写入和读取操作,然而这也暗示着 null 对于字段来说是有用的值,这样就南辕北辙了,所以倡议不要采纳这种形式,接下来咱们来看看第二种解决方案 late 修饰符。

String? name;
int? age;

第二种:late 修饰符

因为 late 波及内容较多,咱们拿一个章节来解说。

late 修饰符

late修饰符是在运行时而非编译时对变量进行束缚,这也就是说 late 相当于何时执行对变量的强制束缚。

比方本示例中 name 字段用 late 润饰后并不一定曾经被初始化,每次它被读取时,都会插入一个运行时的查看,以确保它曾经被赋值。如果并未赋值,就会抛出一个异样,给变量加上 String 类型就是说:“我的值相对是字符串”,而加上 late 修饰符意味着:” 每次运行我都要查看是不是真的 ”。

当应用 late 修饰符总结如下:

  • 先不给变量赋值
  • 稍后再给变量赋值
  • 在应用前会给变量赋值
  • 在应用前不赋值,将会报错
late String name;
late int age;

late 修饰符懒加载

懒加载也有一种说法是初始化提早执行,当你用 late 润饰变量后,那么它将会被提早到字段首次被拜访时才会执行,而不是在实例化结构器时就初始化了。而且实例字段的初始化内容是无法访问 this 的,因为在所有的初始化办法实现前,是无法访问到新的实例对象。然而,应用了 late 的话就能够拜访到this、调用办法以及拜访实例的字段。

不应用 late 修饰符

咱们这里就是创立一个 User 类,name属性间接调用 getUserName() 办法,最初当咱们实例化一个 User 对象并获取 name 的值,咱们来看下控制台输入:

void main() {print("调用构造函数");
  var user = User();
  print("获取值");
  print("获取的值为: ${user.getName()}");

}

class User {String name = getUserName();

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

  getName() {return this.name;}
}

String getUserName() {print("返回用户的名称");
  return "Jimi";
}

控制台输入

flutter: 调用构造函数
flutter: 返回用户的名称
flutter: 获取值
flutter: 获取的值为: Jimi

应用 late 修饰符

代码和下面一样,只是在定义字段处加了一个 late 修饰符,咱们来看一下控制台输入:

void main() {print("调用构造函数");
  var user = User();
  print("获取值");
  print("获取的值为: ${user.getName()}");

}

class User {late String name = getUserName();

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

  getName() {return this.name;}
}

String getUserName() {print("返回用户的名称");
  return "Jimi";
}

控制台输入

flutter: 调用构造函数
flutter: 获取值
flutter: 返回用户的名称
flutter: 获取的值为: Jimi

咱们显著能够看到第二行和第三行反过来了,而在不应用 late 修饰符也就是没有懒加载的状况下,当咱们实例化结构器时就间接调用了获取值的办法。而加了 late 修饰符后是在应用该字段的时候才会去进行获取。

总结

空平安的引入让我的代码变得更加牢靠

  • 在类型上都必须是非空的,当然你也能够增加 ? 变成可空的,用空断言运算符 ! 进行应用。
  • 可选参数都必须是非空的,能够应用 required 来构建一个非可选命名参数。
  • List 类当初不再容许蕴含未初始化的元素。
  • late修饰符在运行时查看,可能应用非空类型和 final,它同时提供了对字段提早初始化的反对。
退出移动版