Flutter2空安全,避免App空值崩溃问题

什么是空安全

从Flutter2开始,Flutter在配置中默认启用空安全,通过空检查合并到类型系统中,可以在开发过程中捕获这些错误,防止生产环境导致的崩溃问题。

目前在Kotlin、Swift、Rust等对空安全都有了对自己的支持,Dart从2.12版本开始支持空安全,通过空安全开发人员可以有效避免null错误崩溃,空安全性可以说是Dart语言的重要补充,它通过区分类型和非可空类型进一步增强类型系统。

空安全好处

  • 可以将原本运行时空值变为编辑时的分析错误。
  • 增强程序健壮性,有效避免由Null导致的崩溃。
  • 跟随Dart和Flutter的发展趋势,为程序的后续迭代不留坑。

空安全必备小知识

  • 空安全原则
  • 引入空安全前后Dart类型系统的变化
  • 可空(?)类型的使用
  • 延迟初始化(late)的使用
  • 空值断言操作符(!)的使用

空安全原则

Dart的空安全支持基于一下三条核心原则:

  • 默认不可空:除非将变量声明为可空,否则他一定是非空类型
  • 渐进迁移:可以自由选择何时进行迁移,多少代码进行迁移。
  • 完全可靠:Dart的空安全是非常可靠的,意味着编译期间包含了很多优化
    • 如果类型系统推出某个变量不为空,那么它永远不为空,当你将整个项目和其他依赖完全迁移至空安全后,你会享有健全性的所有优势——更少的Bug、更小的二进制文件以及更快的执行速度。

引入空安全前后Dart类型系统的变化

在引入空安全前Dart的类型系统是这样的:
在这里插入图片描述
这意味着在之前,所有的类型都可以为Null,也就是Null类型被看作是所有类型的子类。在引入空安全之后:
在这里插入图片描述
可以看出,最大的变化是将Null类型独立出来了,这意味着Null不在是其它类型的子类型,所以对于一个非Null类型的变量传递一个Null值时会报类型转换错误。提示:在使用了空安全的Flutter或Dart项目中你会经常看到?.、!、late的大量应用,那么他们分别是什么又改如何使用呢?请看下文的分析:

可空(?)类型的使用

我们可以通过将?跟在类型的后面来表示它后面的变量或参数可接受Null:

class CommonModel {
    
    
	String? firstName; //可空的成员变量
	int getNameLen(String? last/*可空的参数*/){
    
    
		int firstLen = firstName?.length ?? 0;
		int lastLen = lastName?.length ?? 0;
		return firstLen + lastLen;
	}
}

对于可空的变量或参数在使用的时候需要通过Dart的避空运算符?.来进行访问,否则会抛出编译错误。

当程序启用空安全后,类的成员变量默认是不可空的,所以对于一个非空的成员变量需要指定其初始化方式:

class CommonModel {
    
    
	List names=[];//定义时初始化
	final List colors;//在构造方法中初始化
	late List urls;//延时初始化
	CommonModel(this.colors);
{
    
    

延迟初始化(late)的使用

对于无法在定义时进行初始化,并且又想避免使用?.,那么延迟初始化可以帮到你。通过late修饰的变量,可以让开发者选择初始化的时机,并且在使用这个变量时可以不用?.。

late List urls;//延时初始化
setUrls(List urls){
    
    
	this.urls=urls;
}
int getUrlLen() {
    
    
	return urls.length;
}

延时初始化虽然能为我们编码带来一定便利,但如果使用不当会带来空异常的问题,所以在使用的时候一定保证赋值和访问的顺序,切莫颠倒。

延迟初始化(late)使用范式 :
在Flutter中State的initState方法中初始化的一些变量是比较适合使用late来进行延时初始化的,因为在Widget生命周期中initState方法是最先执行的,所以它里面初始化的变量通过late修饰后既能 保障使用时的便利,又能防止空异常。

空值断言操作符(!)的使用

当我们排除变量或参数的可空的可能后,可以通过!来告诉编译器这个可空的变量或参数不可空,这对我们进行方法传参或将可空参数传递给一个不可空的入参时特别有用:

Widget get _listView{
    
    
	return ListView(
		children: <Widget>[
		_banner,
		Padding(
			padding: EdgeInsets.formLTRB(7, 4, 7, 4),
			child: LocalNav(localNavList: localNavList),
		),
		if (gridNavModel != null)
			Padding(
				padding: EdgeInset.formLTRB(7, 0, 7, 4),
				child: GriNav(gridNavModel: gridNavModel!)),
			Padding(
				padding: EdgeInsets.formLTRB(7, 0, 7, 4),
				child: SubNav(subNavList: subNavList)),
			if (selesBoxModel != null) 
				Padding(
					padding: EdgeInsets.formLTRB(7, 0, 7, 4),
					child: SalesBox(selesBox: salesBoxModel!)),
		],
	);
}

猜你喜欢

转载自blog.csdn.net/qq_45455361/article/details/122665183