Dart 空安全:
- 空类型操作符 (?)
- 空值合并操作符 (??)
- 空值断言操作符 (!)
- 延迟初始化 (late)
1、空类型操作符 (?)
- 当你想要根据一个表达式是否为 null 来执行某个操作时,你可以使用 (?)
- 语法:expression1?.expression2
- 如果 expression1 不是 null,则执行 expression2 并返回其结果。如果 expression1 是 null,则直接返回 null,并且不会执行 expression2。
void main(){
String? name = null;
int? length = name?.length;
print("length: $length"); //length: null
}
// 如果 name 是 null,length 也会是 null
// 如果 name 不是 null,length 将会是 name 的长度
2、空值合并操作符 (??)
- 当你想要为一个可能为 null 的表达式提供一个默认值时,你可以使用 (??)
- 语法:expression1 ?? expression2
- 如果 expression1 不是 null,则返回 expression1 的值。如果 expression1 是 null,则返回 expression2 的值。
void main() {
String? name = null;
String fullName = name ?? "Unknown";
print("fullName: $fullName"); //fullName: Unknown
}
// 如果 name 是 null,则 fullName 为 "Unknown"
// 如果 name 不是 null,则 fullName 为 name 的值
3、空值断言操作符 (!)
- 当你确定一个表达式不应该为 null,但编译器无法确定时,你可以使用!来告诉编译器你确信该表达式不是 null
- 语法:expression!
- 使用此操作符时,你应确保表达式确实不是 null,否则在运行时会出现 NullPointerException
void main() {
String? name = null;
int length = name!.length;
print("length: $length"); //Unhandled exception:Null check operator used on a null value
}
// 确信 name 不是 null,并获取其长度
// 如果 name 是 null,则会报错 NullPointerException
补充,(!) 取反用法
void main(){
String name = "leon";
if(name is! String){
print("其他类型");
}else{
print("String类型");
}
}
//输出:String类型
4、延迟初始化 (late)
- 当你在声明变量时使用 late 关键字,你告诉 Dart 编译器该变量将在稍后的某个时间点被初始化,而不是在声明时立即初始化
void main() {
late String name; // 声明一个late变量,此时不需要初始化
if (someCondition) {
name = 'Alice'; // 在某个条件满足时进行初始化
} else {
name = 'Bob'; // 在另一个条件满足时进行初始化
}
print(name); // 使用已经初始化的变量
}
bool someCondition = true; // 假设这是某个条件,实际情况中可能根据逻辑判断来设置
- 在 Flutter 中 State 的 initState 方法中初始化的一些变量,是比较适合使用 late 来进行延时初始化的
- 因为在 Widget 生命周期中 initState 方法是最先执行的,所以它里面初始化的变量通过 late 修饰后既能保障使用时的便利,又能防止空异常
class _TravelPgeState extends State<TravelPge> with TickerProviderStateMixin {
late TabController _controller;
void initState() {
super.initState();
_controller = TabController(length: 0, vsync: this);
}
...
5、补充:Widget 生命周期
- 在 Flutter 中,Widget 并不直接具有生命周期,因为 Widget 是不可变的(immutable)
- 然而,Flutter 中的 StatefulWidget 和 State 对象确实具有生命周期,因为 StatefulWidget 可以创建和管理一个可变的状态对象(State)
当你插入一个 StatefulWidget 到 Flutter 的 widget 树时,Flutter 会进行以下操作:
创建(Create):
- StatefulWidget 的实例被创建
- createState() 方法被调用以创建一个新的 State 对象
- initState() 方法在 State 对象上被调用,你可以在这里进行初始化操作
插入(Insert):
- StatefulWidget 和它的 State 对象被插入到 widget 树中
- build() 方法在 State 对象上被调用,以构建 widget 树
更新(Update):
- 当 StatefulWidget 的依赖项发生变化时,Flutter 会重新构建 widget 树
- build() 方法会再次被调用,但 State 对象不会改变
- 如果你想根据新的依赖项更新 State,你可以调用 setState(() {}),这将导致 build() 方法再次被调用,但 initState() 不会被调用
移除(Remove):
- 当 StatefulWidget 从 widget 树中移除时,Flutter 会调用 dispose() 方法。你可以在这里释放任何资源或执行任何必要的清理操作
注意,build() 方法在整个生命周期中可能会被多次调用,而 initState() 和 dispose() 方法每个 State 对象只会被调用一次。
class LifecycleWidget extends StatefulWidget {
_LifecycleWidgetState createState() => _LifecycleWidgetState();
}
class _LifecycleWidgetState extends State<LifecycleWidget> {
void initState() {
super.initState();
print('initState called');
// 初始化代码
}
Widget build(BuildContext context) {
print('build called');
return Container(
padding: const EdgeInsets.all(16.0),
child: Text('Lifecycle Widget'),
);
}
void dispose() {
super.dispose();
print('dispose called');
// 清理代码
}
}
- 在这个例子中,当你插入 LifecycleWidget 到 Flutter 应用中时,你会看到 “initState called” 和 “build called” 打印出来
- 如果依赖项发生变化并触发重建,只有 “build called” 会再次打印
- 当你从 widget 树中移除 LifecycleWidget 时,“dispose called” 会打印出来。
6、空安全适配示例
(1)示例1
- 空类型:(?) 修饰
- 非空类型:1)定义时初始化 2)(late) 延迟初始化
非空类型需要在构造函数中指定 required
void main() {
Person person = Person(name: "leon", sex: "man");
person.display();
person.setAge(27);
person.display();
}
class Person {
String name = ""; //非空类型,定义时初始化
int? age; //空类型,(?) 修饰
late String sex; //非空类型,(late) 延迟初始化
//非空类型需要在构造函数中指定 required
Person({required this.name, required this.sex});
void setAge(int age) {
this.age = age;
}
void display() {
print("name: $name age: $age sex: $sex");
}
}
(2)示例2
- State中延迟初始化的属性,可以在 initState() 中赋值
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'muke flutter',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: const MyHomePage(title: 'muke learning'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({super.key, required this.title});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late String data; //延迟初始化
//State中延迟初始化的属性,可以在 initState() 中赋值
void initState() {
super.initState();
data = "延迟初始化";
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(child: Text("data: $data")));
}
}