Appearance
Dart 基础
Dart 介绍
Dart 是 Google 开发的现代化编程语言,专为跨平台开发设计。其核心特性包括:
全场景覆盖:支持 Web 前端、服务端、移动端(Android/iOS)及 IoT 设备开发 高效开发体验:通过 AOT 编译实现原生级性能,JIT 编译支持热重载 现代语法体系:融合强类型系统、异步编程模型(async/await)和面向对象特性 该语言诞生于 2011 年,最初定位为 JavaScript 的替代方案。经过技术沉淀,Dart 在 2017 年随 Flutter 框架发布后迎来发展转折点。Flutter 凭借其创新的 UI 渲染引擎和声明式编程范式,使 Dart 成为构建跨平台应用的首选语言。
Dart 变量、常量及命名规则
变量
动态类型推导
Dart 是强类型语言,但支持自动类型推断。使用var
声明变量时,类型根据初始值自动确定:dartvar str = '自动推断为 String'; // 类型:String var num = 100; // 类型:int
注意:变量类型一旦确定不可更改:
dartvar value = 'hello'; value = 123; // ❌ 编译错误:int 不能赋值给 String
显式类型声明
可直接指定类型(推荐用于成员变量或复杂场景):dartString name = 'Alice'; int age = 30; List<int> scores = [90, 85];
禁止混合声明:
var int number = 5;
// ❌ 语法错误:不能同时使用 var 和类型
常量
const 常量
- 编译时常量:在编译时确定值,永远不会改变
- 必须声明时立即赋值
- 值在编译期间就已固定
dart
const PI = 3.14;
// PI = 3.14159; // 错误:const定义的常量无法更改
final 常量
- 运行时常量:在运行时确定值,赋值后不可改变
- 允许先声明后赋值(但只能赋值一次)
- 具有惰性初始化特性:在运行时第一次使用前才初始化
dart
final pi = 3.14;
// pi = 3.14159; // 错误:final常量只能赋值一次
final a;
a = 13; // 允许先声明后赋值
// a = 14; // 错误:只能赋值一次
final d = DateTime.now(); // 运行时获取当前时间
final 和 const 的主要区别
特性 | const | final |
---|---|---|
初始化时机 | 编译时 | 运行时 |
赋值次数 | 只能赋值一次(在声明时) | 允许先声明后赋值(一次) |
值确定时间 | 编译期间 | 运行期间 |
使用场景 | 编译时已知的常量值 | 运行时确定的常量值 |
初始化特性 | 立即初始化 | 惰性初始化 |
常量构造函数
- 常量构造函数需以
const
关键字修饰 - 必须用于所有成员变量都是
final
的类 - 实例化时不加
const
修饰符,创建的是普通对象 - 实例化时加
const
修饰符:- 多个地方创建相同值的对象,只会保留一个实例
- 传入相同参数时,返回相同对象
- 在 Flutter 中,使用
const
组件可以避免不必要的重建
示例代码
dart
class Container {
final int width;
final int height;
// 常量构造函数
const Container({required this.width, required this.height});
}
void main() {
// 不使用 const - 创建普通对象
var c1 = Container(width: 100, height: 100);
var c2 = Container(width: 100, height: 100);
print(identical(c1, c2)); // false - 不同对象
// 使用 const - 创建编译时常量对象
var c3 = const Container(width: 100, height: 100);
var c4 = const Container(width: 100, height: 100);
print(identical(c3, c4)); // true - 相同对象
// 不同参数创建的对象不同
var c5 = const Container(width: 100, height: 110);
var c6 = const Container(width: 120, height: 100);
print(identical(c5, c6)); // false - 不同对象
}
命名规则
合法字符:字母、数字、下划线(
_
)、美元符($
),不能以数字开头
✅ 正确:_user
,totalAmount
,$status
❌ 错误:1stPlace
,user-name
区分大小写:
dartvar name = 'Alice'; var Name = 'Bob'; // 与 name 不同
禁止关键字:
不可使用保留字(如if
,class
,for
等)作为标识符。命名规范:
- 变量/属性:使用小驼峰(首个单词小写,后续单词首字母大写)
✅studentName
,isValid
,totalCount
- 类/类型:使用大驼峰(每个单词首字母大写)
✅class UserProfile {}
,enum StatusCode { ok }
- 见名知意:
❌var a = 10;
✅var itemCount = 10;
- 变量/属性:使用小驼峰(首个单词小写,后续单词首字母大写)
Dart 数据类型
- 数值类型:
int
(整数)和double
(浮点数) - 字符串类型:单引号、双引号、三引号
- 布尔类型:
true
和false
- 列表类型:
List
- 字典类型:
Map
- 运行时类型:
dynamic
(很少用) - 符号类型:
Symbol
(很少用)
1. Numbers(数值类型)
Dart 提供两种数值类型:
int
:整数类型(64 位)double
:双精度浮点数(64 位)
dart
void main() {
int a = 123; // 整型
double b = 23.5; // 浮点型
b = 24; // double 可接受整数值
// 运算符示例
var c = a + b; // + - * / %
print(c); // 输出: 147.0
}
2. Strings(字符串类型)
字符串定义方式:
- 单引号
'...'
- 双引号
"..."
- 三引号
'''...'''
或"""..."""
(多行字符串)
dart
void main() {
// 定义方式
String str1 = '单引号字符串';
String str2 = "双引号字符串";
String str3 = '''多行
字符串''';
// 字符串拼接
String name = '张三';
String lang = 'Dart';
print('$name 正在学习 $lang'); // 输出: 张三 正在学习 Dart
print(name + " " + lang); // 输出: 张三 Dart
}
3. Booleans(布尔类型)
布尔类型只有 true
和 false
两个值:
dart
void main() {
bool flag1 = true;
bool flag2 = false;
// 条件判断
var condition = true;
if (condition) {
print('条件为真');
} else {
print('条件为假');
}
// 值比较
var a = 123;
var b = 123;
if (a == b) {
print('a等于b'); // 输出: a等于b
}
}
4. List(列表/数组)
Dart 中数组称为 List:
dart
void main() {
// 1. 动态类型列表
var list1 = ["张三", 20, true];
print(list1[0]); // 输出: 张三
// 2. 指定类型列表
var list2 = <String>["苹果", "香蕉"];
var list3 = <int>[10, 20];
// 3. 可扩展列表
var list4 = [];
list4.add("北京");
list4.add("上海");
list4.add(100); // 可添加任意类型
// 4. 固定长度列表
var list5 = List<String>.filled(2, "");
list5[0] = "第一项";
list5[1] = "第二项";
// list5.add("新项"); // 错误:固定长度列表无法添加元素
}
5. Maps(字典/映射)
键值对集合,键和值可以是任意类型:
dart
void main() {
// 1. 字面量创建
var person = {
"name": "张三",
"age": 25,
"skills": ["编程", "设计"]
};
print(person["name"]); // 输出: 张三
// 2. 构造函数创建
var company = Map();
company["name"] = "ABC公司";
company["employees"] = 200;
company["isTech"] = true;
}
类型检查与判断
使用 is
关键字进行类型检查:
dart
void main() {
var data = "Dart语言";
if (data is String) {
print('字符串类型');
} else if (data is int) {
print('整型');
} else if (data is List) {
print('列表类型');
} else {
print('其他类型');
}
}
Dart 运算符与类型转换详解
算术运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
+ | 加法 | 13 + 5 | 18 |
- | 减法 | 13 - 5 | 8 |
* | 乘法 | 13 * 5 | 65 |
/ | 除法 | 13 / 5 | 2.6 |
% | 取余 | 13 % 5 | 3 |
~/ | 取整除法 | 13 ~/ 5 | 2 |
dart
void main() {
int a = 13;
int b = 5;
print(a + b); // 18
print(a - b); // 8
print(a * b); // 65
print(a / b); // 2.6
print(a % b); // 3
print(a ~/ b); // 2
}
关系运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
== | 等于 | 5 == 3 | false |
!= | 不等于 | 5 != 3 | true |
> | 大于 | 5 > 3 | true |
< | 小于 | 5 < 3 | false |
>= | 大于等于 | 5 >= 5 | true |
<= | 小于等于 | 5 <= 3 | false |
dart
void main() {
int a = 5;
int b = 3;
print(a == b); // false
print(a != b); // true
print(a > b); // true
print(a < b); // false
print(a >= b); // true
print(a <= b); // false
}
逻辑运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
! | 取反 | !false | true |
&& | 逻辑与 | true && false | false |
|| | 逻辑或 | true || false | true |
dart
void main() {
bool a = true;
bool b = false;
print(!b); // true
print(a && b); // false
print(a || b); // true
// 实际应用
int age = 23;
String sex = "女";
if(age == 20 || sex == "女") {
print("$age --- $sex"); // 23 --- 女
}
}
赋值运算符
运算符 | 描述 | 示例 | 结果 |
---|---|---|---|
= | 赋值 | int a = 10; | a=10 |
??= | 空安全赋值(值为空时赋值) | b ??= 23; | 若 b 为 null 则赋值 23 |
dart
void main() {
int a = 10;
print(a); // 10
int? b;
b ??= 23; // b为空,赋值为23
print(b); // 23
int c = 6;
c ??= 23; // c不为空,保持不变
print(c); // 6
}
复合赋值运算符
运算符 | 描述 | 示例 | 等价于 |
---|---|---|---|
+= | 加后赋值 | a += 10 | a = a + 10 |
-= | 减后赋值 | a -= 5 | a = a - 5 |
*= | 乘后赋值 | a *= 3 | a = a * 3 |
/= | 除后赋值 | a /= 2 | a = a / 2 |
%= | 取余后赋值 | a %= 4 | a = a % 4 |
~/= | 取整后赋值 | a ~/= 4 | a = a ~/ 4 |
dart
void main() {
var a = 12;
a += 10; // a = 22
print(a);
a -= 5; // a = 17
print(a);
a *= 3; // a = 51
print(a);
a ~/= 4; // a = 12
print(a);
}
条件表达式
1. if-else 语句
dart
void main() {
var score = 85;
if(score > 90) {
print('优秀');
} else if(score > 70) {
print('良好'); // 输出:良好
} else if(score >= 60) {
print('及格');
} else {
print('不及格');
}
}
2. switch-case 语句
dart
void main() {
String sex = "女";
switch(sex) {
case "男":
print('性别是男');
break;
case "女":
print('性别是女'); // 输出:性别是女
break;
default:
print('传入参数错误');
break;
}
}
3. 三目运算符
dart
void main() {
bool flag = false;
String result = flag ? '我是true' : '我是false';
print(result); // 输出:我是false
}
4. ?? 运算符
dart
void main() {
var a;
var b = a ?? 10; // a为空,b赋值为10
print(b); // 10
var c = 22;
var d = c ?? 10; // c不为空,d赋值为c的值
print(d); // 22
}
类型转换
String → Number
dart
void main() {
// 使用 int.parse() 或 double.parse()
String intStr = '123';
int myInt = int.parse(intStr);
print(myInt is int); // true
String doubleStr = '123.45';
double myDouble = double.parse(doubleStr);
print(myDouble is double); // true
// 错误处理
String invalid = '';
try {
double.parse(invalid);
} catch (err) {
print('转换失败: $err'); // 处理转换错误
}
}
Number → String
dart
void main() {
int myInt = 123;
String intStr = myInt.toString();
print(intStr is String); // true
double myDouble = 123.45;
String doubleStr = myDouble.toString();
print(doubleStr); // "123.45"
}
2. 其他类型转布尔类型
dart
void main() {
// 字符串空检查
String str = '';
if (str.isEmpty) {
print('字符串为空'); // 输出
}
// 数值检查
int num = 0;
if (num == 0) {
print('数值为0'); // 输出
}
// null 检查
dynamic value;
if (value == null) {
print('值为空'); // 输出
}
// NaN 检查
double result = 0 / 0;
if (result.isNaN) {
print('结果为NaN'); // 输出
}
}
Dart 循环语法详解
1. for 循环基本语法
dart
for (int i = 1; i <= 100; i++) {
print(i);
}
2. 常用循环模式示例
示例 1:打印 0-50 的偶数
dart
for (int i = 0; i <= 50; i++) {
if (i % 2 == 0) {
print(i);
}
}
简单列表遍历
dart
List list = ['张三', '李四', '王五'];
for (var i = 0; i < list.length; i++) {
print(list[i]);
}
字典列表遍历
dart
List newsList = [
{"title": "新闻111"},
{"title": "新闻222"},
{"title": "新闻333"}
];
for (var i = 0; i < newsList.length; i++) {
print(newsList[i]['title']);
}
二维数据遍历
dart
List data = [
{
"cate": '国内',
"news": [
{"title": "国内新闻1"},
{"title": "国内新闻2"},
{"title": "国内新闻3"}
]
},
{
"cate": '国际',
"news": [
{"title": "国际新闻1"},
{"title": "国际新闻2"},
{"title": "国际新闻3"}
]
}
];
for (var i = 0; i < data.length; i++) {
print(data[i]["cate"]);
print('-------------');
for (var j = 0; j < data[i]["news"].length; j++) {
print(data[i]["news"][j]["title"]);
}
}
4. while 循环
dart
// 基本语法
int i = 1;
while (i <= 10) {
print(i);
i++;
}
5. do-while 循环
dart
// 基本语法
int k = 1;
do {
print(k);
k++;
} while (k <= 10);
// 与while的区别:至少执行一次
var m = 10;
do {
print('执行代码'); // 会执行一次
} while (m < 2);
break 语句
dart
// 跳出当前循环
for (var i = 1; i <= 10; i++) {
if (i == 4) {
break; // 当i=4时跳出循环
}
print(i);
}
// 跳出多层循环(只能跳出一层)
for (var i = 0; i < 5; i++) {
print('外层---$i');
for (var j = 0; j < 3; j++) {
if (j == 1) {
break; // 跳出内层循环
}
print('里层$j');
}
}
continue 语句
dart
// 跳过当前迭代
for (var i = 1; i <= 10; i++) {
if (i == 4) {
continue; // 跳过i=4的迭代
}
print(i);
}
注意事项:
break
会立即终止当前循环continue
会跳过当前迭代的剩余代码- 在多层循环中,
break
只能跳出当前层循环 - 在 while 循环中使用
continue
要小心避免死循环
List 常用属性和方法
常用属性
属性 | 说明 | 示例 |
---|---|---|
length | 列表长度 | list.length |
reversed | 翻转列表 | list.reversed.toList() |
isEmpty | 是否为空 | list.isEmpty |
isNotEmpty | 是否不为空 | list.isNotEmpty |
常用方法
dart
// 创建List
List myList = ['香蕉', '苹果', '西瓜'];
// 添加元素
myList.add('桃子'); // 添加单个元素
myList.addAll(['葡萄', '芒果']); // 添加多个元素
// 查找元素
int index = myList.indexOf('苹果'); // 返回索引,找不到返回-1
// 删除元素
myList.remove('西瓜'); // 删除指定元素
myList.removeAt(1); // 删除指定索引元素
// 修改元素
myList.fillRange(1, 3, '新水果'); // 修改索引1-3的元素
// 插入元素
myList.insert(1, '插入水果'); // 指定位置插入单个元素
myList.insertAll(1, ['插入1', '插入2']); // 指定位置插入多个元素
// 类型转换
String str = myList.join('-'); // List转字符串 ['a','b'] → "a-b"
List newList = str.split('-'); // 字符串转List "a-b" → ['a','b']
Set 操作
dart
// 创建Set
var s = Set();
s.add('香蕉');
s.add('苹果');
s.add('苹果'); // 重复添加无效
// List转Set去重
List myList = ['香蕉','苹果','西瓜','香蕉','苹果'];
var s = Set.from(myList);
print(s); // {香蕉, 苹果, 西瓜}
// Set转List
List uniqueList = s.toList();
Map 常用属性和方法
常用属性
属性 | 说明 | 示例 |
---|---|---|
keys | 所有 key 值 | map.keys.toList() |
values | 所有 value 值 | map.values.toList() |
isEmpty | 是否为空 | map.isEmpty |
isNotEmpty | 是否不为空 | map.isNotEmpty |
常用方法
dart
// 创建Map
Map person = {
"name": "张三",
"age": 20,
"sex": "男"
};
// 添加/合并
person.addAll({
"work": ['敲代码','送外卖'],
"height": 160
});
// 删除元素
person.remove("sex");
// 检查值是否存在
bool exists = person.containsValue('张三');
集合遍历方法
forEach 遍历
dart
List myList = ['香蕉','苹果','西瓜'];
// List遍历
myList.forEach((value) {
print(value);
});
// Set遍历
Set fruitSet = {'苹果', '香蕉', '橙子'};
fruitSet.forEach((value) => print(value));
// Map遍历
Map person = {"name": "张三", "age": 20};
person.forEach((key, value) {
print("$key---$value");
});
map 转换
dart
List myList = [1, 3, 4];
var newList = myList.map((value) {
return value * 2;
}).toList();
print(newList); // [2, 6, 8]
where 过滤
dart
List numbers = [1, 3, 4, 5, 7, 8, 9];
var filtered = numbers.where((value) {
return value > 5;
}).toList();
print(filtered); // [7, 8, 9]
any 检查(任意元素满足)
dart
List numbers = [1, 3, 4, 5, 7, 8, 9];
bool hasLarge = numbers.any((value) {
return value > 5;
});
print(hasLarge); // true
every 检查(所有元素满足)
dart
List numbers = [1, 3, 4, 5, 7, 8, 9];
bool allLarge = numbers.every((value) {
return value > 5;
});
print(allLarge); // false
总结表格:集合高阶函数
方法 | 功能 | 返回值类型 | 示例 |
---|---|---|---|
forEach | 遍历每个元素 | void | list.forEach((v) => print(v)) |
map | 转换每个元素 | Iterable | list.map((v) => v*2).toList() |
where | 过滤元素 | Iterable | list.where((v) => v>5).toList() |
any | 检查是否至少一个满足条件 | bool | list.any((v) => v>5) |
every | 检查是否所有元素满足条件 | bool | list.every((v) => v>0) |
Dart 函数详解
函数定义基础
Dart 中的函数定义基本格式如下:
dart
返回类型 函数名(参数1, 参数2, ...) {
// 函数体
return 返回值;
}
示例代码
dart
// 无返回值函数
void printInfo() {
print('我是一个自定义方法');
}
// 返回整型函数
int getNum() {
return 123;
}
// 返回字符串函数
String printUserInfo() {
return 'this is str';
}
// 返回列表函数
List getList() {
return ['111','2222','333'];
}
void main() {
printInfo(); // 调用无返回值函数
print(getNum()); // 调用返回整型函数
print(printUserInfo()); // 调用返回字符串函数
print(getList()); // 调用返回列表函数
}
函数作用域
函数内部定义的函数只能在外部函数内部调用:
dart
void main() {
void outerFunction() {
void innerFunction() {
print('内部函数被调用');
}
innerFunction(); // 正确:在外部函数内部调用
}
outerFunction(); // 调用外部函数
// innerFunction(); // 错误:不能在外部函数外部调用
}
函数参数类型
1. 必需参数
dart
// 计算1到n的和
int sumNum(int n) {
int sum = 0;
for(int i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
void main() {
print(sumNum(5)); // 15 (1+2+3+4+5)
print(sumNum(10)); // 55
}
2. 可选位置参数
使用 []
包裹可选参数,需提供默认值: 可以省略默认值,但参数类型必须是可空的(使用 ?),如果不提供默认值,参数默认值为 null
dart
String printUserInfo(String username, [int age = 0]) {
return age != 0
? "姓名:$username---年龄:$age"
: "姓名:$username---年龄保密";
}
// 可以省略默认值,但参数类型必须是可空的(使用 ?),如果不提供默认值,参数默认值为 null
void printInfo(String name, [int? age]) {
print('$name, ${age ?? "年龄未知"}');
}
void main() {
print(printUserInfo('张三', 25)); // 姓名:张三---年龄:25
print(printUserInfo('李四')); // 姓名:李四---年龄保密
}
3. 命名参数
使用 {}
包裹命名参数,需提供默认值: 命名参数可以不传,不传时使用默认值:
dart
String printUserDetails(String username,
{int age = 0, String gender = '未知'}) {
return "姓名:$username | 年龄:${age != 0 ? age : '保密'} | 性别:$gender";
}
void main() {
print(printUserDetails('王五')); // 姓名:王五 | 年龄:保密 | 性别:未知
print(printUserDetails('赵六', age: 30, gender: '男'));
// 姓名:赵六 | 年龄:30 | 性别:男
}
函数作为参数
Dart 中函数是一等公民,可以作为参数传递:
dart
void main() {
// 定义普通函数
void sayHello() {
print('你好,世界!');
}
// 定义接收函数作为参数的函数
void executeFunction(Function fn) {
print('开始执行函数...');
fn();
print('函数执行完成!');
}
// 传递函数作为参数
executeFunction(sayHello);
// 匿名函数作为参数
executeFunction(() {
print('这是匿名函数');
});
}
箭头函数
对于单行函数,可以使用箭头语法简化:
dart
void main() {
// 传统函数
int doubleValue(int x) {
return x * 2;
}
// 箭头函数
int tripleValue(int x) => x * 3;
print(doubleValue(5)); // 10
print(tripleValue(5)); // 15
// 在列表操作中使用箭头函数
var numbers = [1, 2, 3, 4, 5];
var squared = numbers.map((n) => n * n).toList();
print(squared); // [1, 4, 9, 16, 25]
}
递归函数
函数可以调用自身实现递归:
dart
void main() {
// 计算阶乘
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
print(factorial(5)); // 120 (5*4*3*2*1)
// 斐波那契数列
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
for (int i = 0; i < 10; i++) {
print(fibonacci(i)); // 0,1,1,2,3,5,8,13,21,34
}
}
闭包(Closure)
Dart 支持闭包,函数可以记住并访问其词法作用域中的变量:
dart
void main() {
// 创建计数器闭包
Function makeCounter() {
int count = 0;
return () {
count++;
print(count);
};
}
var counter = makeCounter();
counter(); // 1
counter(); // 2
counter(); // 3
var anotherCounter = makeCounter();
anotherCounter(); // 1 (独立的计数器)
}
Dart 面向对象编程详解
基本概念
Dart 是纯面向对象语言,所有对象都是类的实例,所有类都继承自 Object 类。OOP 的三个基本特征:
- 封装:将数据和操作数据的方法绑定在一起
- 继承:子类复用父类的功能并扩展
- 多态:同一操作作用于不同对象产生不同行为
类定义与使用
基本类定义
dart
class Person {
String name = "张三";
int age = 23;
void getInfo() {
print("${this.name}----${this.age}");
}
void setInfo(int age) {
this.age = age;
}
}
void main() {
Person p1 = Person();
p1.setInfo(28);
p1.getInfo(); // 输出:张三----28
}
构造函数
默认构造函数
dart
class Person {
late String name;
late int age;
// 默认构造函数简写
Person(this.name, this.age);
void printInfo() {
print("$name----$age");
}
}
void main() {
Person p = Person("李四", 25);
p.printInfo(); // 输出:李四----25
}
命名构造函数
dart
class Person {
late String name;
late int age;
Person(this.name, this.age);
// 命名构造函数
Person.now() {
print('我是命名构造函数');
}
Person.setInfo(String name, int age) {
this.name = name;
this.age = age;
}
}
void main() {
Person p1 = Person.now(); // 输出:我是命名构造函数
Person p2 = Person.setInfo("王五", 30);
p2.printInfo(); // 输出:王五----30
}
封装与访问控制
Dart 使用_
前缀表示私有成员(库级私有)
私有成员示例
animal.dart
dart
class Animal {
String _name; // 私有属性
int age;
Animal(this._name, this.age);
// 公有方法访问私有属性
String getName() {
return _name;
}
// 私有方法
void _run() {
print('私有方法被调用');
}
// 公有方法调用私有方法
void execRun() {
_run();
}
}
main.dart
dart
import 'animal.dart';
void main() {
Animal a = Animal('小狗', 3);
print(a.getName()); // 输出:小狗
a.execRun(); // 输出:私有方法被调用
// a._name; // 错误:无法访问私有属性
// a._run(); // 错误:无法访问私有方法
}
Getter 与 Setter
dart
class Rect {
late num height;
late num width;
Rect(this.height, this.width);
// Getter
num get area => height * width;
// Setter
set areaHeight(num value) => height = value;
}
void main() {
Rect r = Rect(10, 4);
print(r.area); // 输出:40
r.areaHeight = 6;
print(r.area); // 输出:24
}
初始化列表
在构造函数体执行前初始化实例变量
dart
class Rect {
int height;
int width;
Rect(): height = 2, width = 10 {
print("$height---$width"); // 输出:2---10
}
int getArea() => height * width;
}
void main() {
Rect r = Rect();
print(r.getArea()); // 输出:20
}
继承与多态
继承示例
dart
class Animal {
String name;
Animal(this.name);
void speak() {
print("动物叫声");
}
}
class Dog extends Animal {
Dog(String name) : super(name);
@override
void speak() {
print("$name 汪汪叫");
}
}
class Cat extends Animal {
Cat(String name) : super(name);
@override
void speak() {
print("$name 喵喵叫");
}
}
void main() {
Animal dog = Dog("大黄");
Animal cat = Cat("小花");
dog.speak(); // 输出:大黄 汪汪叫
cat.speak(); // 输出:小花 喵喵叫
}
抽象类与接口
dart
// 抽象类
abstract class Shape {
// 抽象方法
double area();
// 普通方法
void printArea() {
print("面积: ${area()}");
}
}
// 实现接口
class Circle implements Shape {
double radius;
Circle(this.radius);
@override
double area() {
return 3.14 * radius * radius;
}
@override
void printArea() {
print("圆的面积: ${area()}");
}
}
void main() {
Shape circle = Circle(5);
circle.printArea(); // 输出:圆的面积: 78.5
}
Dart 面向对象高级特性详解
1. 静态成员
1.1 静态属性和方法
使用static
关键字定义类级别的变量和函数
dart
class Person {
static String name = '张三'; // 静态属性
int age = 20; // 实例属性
// 静态方法
static void show() {
print(name);
}
// 实例方法
void printInfo() {
print("$name - $age"); // 实例方法可以访问静态成员
}
}
void main() {
// 访问静态成员
print(Person.name); // 输出:张三
Person.show(); // 输出:张三
// 实例化对象
Person p = Person();
p.printInfo(); // 输出:张三 - 20
}
1.2 访问规则
方法类型 | 访问静态成员 | 访问实例成员 |
---|---|---|
静态方法 | ✅ | ❌ |
实例方法 | ✅ | ✅ |
dart
class Calculator {
static const double pi = 3.14159;
late double radius;
// 静态方法只能访问静态成员
static double calculateArea(double r) {
return pi * r * r;
}
// 实例方法可以访问静态成员和实例成员
void printArea() {
print("面积: ${pi * radius * radius}");
}
}
2. 对象操作符
2.1 类型检查与转换
操作符 | 描述 | 示例 |
---|---|---|
is | 类型检查 | if (obj is Person) {...} |
as | 类型转换 | (obj as Person).printInfo() |
dart
class Animal {
void speak() => print("动物叫声");
}
class Dog extends Animal {
@override
void speak() => print("汪汪叫");
}
void main() {
Animal dog = Dog();
// 类型检查
if (dog is Dog) {
print("这是一只狗");
}
// 类型转换
(dog as Dog).speak();
}
2.2 级联操作符 (..
)
允许在同一对象上执行多个操作
dart
class Person {
String name;
int age;
Person(this.name, this.age);
void printInfo() => print("$name - $age");
}
void main() {
Person p = Person('张三', 20)
..name = "李四" // 修改属性
..age = 25 // 修改属性
..printInfo(); // 调用方法
// 输出:李四 - 25
}
2.3 条件运算符 (?
) - 已废弃
注意:Dart 的最新版本已废弃
?.
操作符,使用空安全特性替代
3. 类的继承
3.1 基本继承
dart
class Animal {
String name;
Animal(this.name);
void speak() => print("动物叫声");
}
class Dog extends Animal {
// 子类构造函数必须调用父类构造函数
Dog(String name) : super(name);
void run() => print("$name 在奔跑");
}
void main() {
Dog dog = Dog("大黄");
dog.speak(); // 输出:动物叫声
dog.run(); // 输出:大黄 在奔跑
}
3.2 构造函数继承
dart
class Person {
String name;
int age;
Person(this.name, this.age);
// 命名构造函数
Person.onlyName(this.name) : age = 0;
}
class Employee extends Person {
String position;
// 调用父类默认构造函数
Employee(String name, int age, this.position) : super(name, age);
// 调用父类命名构造函数
Employee.onlyName(String name, this.position) : super.onlyName(name);
}
3.3 方法覆写 (@override
)
dart
class Animal {
void speak() => print("动物叫声");
}
class Cat extends Animal {
@override
void speak() => print("喵喵叫"); // 覆写父类方法
}
class Dog extends Animal {
@override
void speak() {
super.speak(); // 调用父类方法
print("汪汪叫");
}
}
3.4 使用super
关键字
dart
class Vehicle {
void start() => print("引擎启动");
}
class Car extends Vehicle {
@override
void start() {
super.start(); // 调用父类方法
print("汽车启动");
}
}
4. 注意事项
4.1 不可为 null 的实例字段
Dart 最新版本要求初始化不可为 null 的实例字段:
dart
class Person {
late String name; // 使用late关键字延迟初始化
int age = 0; // 或直接初始化
Person(this.name);
}
4.2 继承限制
特性 | 是否继承 | 说明 |
---|---|---|
属性和方法 | ✅ | 可见的成员会被继承 |
构造函数 | ❌ | 必须显式调用父类构造函数 |
静态成员 | ❌ | 属于类本身,不被继承 |
私有成员 | ❌ | 仅在定义它的库中可见 |
4.3 最佳实践
- 使用
@override
注解明确表示覆写 - 优先使用级联操作符进行链式调用
- 对于可能为 null 的对象,使用空安全特性而非
?.
- 在子类构造函数中始终调用父类构造函数
- 合理使用
late
关键字处理延迟初始化
1. 抽象类(Abstract Classes)
1.1 定义与特点
- 使用
abstract
关键字定义 - 不能被直接实例化
- 可以包含抽象方法(没有方法体的方法)和具体方法
- 主要作用是为子类定义标准和约束
dart
abstract class Animal {
// 抽象方法 - 没有方法体
void eat();
void run();
// 具体方法 - 有实现
void printInfo() {
print('我是一个抽象类里面的普通方法');
}
}
1.2 实现抽象类
子类继承抽象类时,必须实现所有抽象方法:
dart
class Dog extends Animal {
@override
void eat() {
print('小狗在吃骨头');
}
@override
void run() {
print('小狗在跑');
}
}
1.3 抽象类 vs 普通类
特性 | 抽象类 | 普通类 |
---|---|---|
实例化 | ❌ | ✅ |
抽象方法 | ✅ | ❌ |
具体方法 | ✅ | ✅ |
继承要求 | 必须实现抽象方法 | 无特殊要求 |
2. 多态(Polymorphism)
2.1 多态概念
- 父类引用指向子类对象:
Animal d = Dog();
- 同一方法调用产生不同行为
- 基于继承和方法覆写实现
2.2 多态实现
dart
abstract class Animal {
void eat();
}
class Dog extends Animal {
@override
void eat() => print('小狗在吃骨头');
}
class Cat extends Animal {
@override
void eat() => print('小猫在吃老鼠');
}
void main() {
Animal dog = Dog(); // 父类引用指向子类对象
Animal cat = Cat(); // 父类引用指向子类对象
dog.eat(); // 输出:小狗在吃骨头
cat.eat(); // 输出:小猫在吃老鼠
}
2.3 多态的优势
- 代码解耦:调用方只关心父类接口
- 扩展性强:添加新子类不影响现有代码
- 统一处理:可以统一处理不同子类对象
- 灵活性高:运行时决定具体执行哪个方法
3. 接口(Interfaces)
3.1 Dart 接口特性
- Dart 没有专门的
interface
关键字 - 任何类都可以作为接口使用
- 使用
implements
关键字实现接口 - 实现接口时必须覆写所有属性和方法
3.2 接口实现示例
dart
// 抽象类作为接口
abstract class Db {
late String uri; // 数据库连接地址
void add(String data);
void save();
void delete();
}
// 实现接口
class Mysql implements Db {
@override
String uri;
Mysql(this.uri);
@override
void add(String data) {
print('Mysql添加数据: $data');
}
@override
void save() => print('Mysql保存数据');
@override
void delete() => print('Mysql删除数据');
}
class MsSql implements Db {
@override
late String uri;
@override
void add(String data) => print('MsSql添加数据: $data');
@override
void save() => print('MsSql保存数据');
@override
void delete() => print('MsSql删除数据');
}
3.3 接口实现要求
实现方式 | 要求 | 示例 |
---|---|---|
普通类作为接口 | 必须覆写所有属性和方法 | class A implements B {...} |
抽象类作为接口 | 必须实现所有抽象成员 | class C implements D {...} |
多接口实现 | 用逗号分隔多个接口 | class E implements F, G {...} |
3.4 抽象类作为接口的最佳实践
dart
// 定义数据库接口标准
abstract class Database {
void connect(String url);
void query(String sql);
void close();
}
// MySQL实现
class MySQL implements Database {
@override
void connect(String url) => print('MySQL连接到: $url');
@override
void query(String sql) => print('执行MySQL查询: $sql');
@override
void close() => print('关闭MySQL连接');
}
// PostgreSQL实现
class PostgreSQL implements Database {
@override
void connect(String url) => print('PostgreSQL连接到: $url');
@override
void query(String sql) => print('执行PostgreSQL查询: $sql');
@override
void close() => print('关闭PostgreSQL连接');
}
4. 继承 vs 实现
4.1 extends 抽象类 vs implements 抽象类
特性 | extends 抽象类 | implements 抽象类 |
---|---|---|
复用代码 | ✅ 继承具体方法 | ❌ 必须重新实现所有方法 |
方法约束 | ✅ 必须实现抽象方法 | ✅ 必须实现所有方法 |
使用场景 | 复用代码并扩展功能 | 严格遵循接口规范 |
4.2 选择指南
- 使用
extends
当:- 需要复用父类中的具体方法
- 需要建立"是一个"的关系
- 需要部分定制父类行为
- 使用
implements
当:- 需要遵循严格的接口规范
- 需要实现多接口
- 不需要复用已有实现
5. 综合应用案例
5.1 支付系统设计
dart
// 支付接口
abstract class PaymentGateway {
void processPayment(double amount);
String getPaymentName();
}
// 信用卡支付实现
class CreditCardPayment implements PaymentGateway {
@override
void processPayment(double amount) {
print('信用卡支付: \$${amount.toStringAsFixed(2)}');
}
@override
String getPaymentName() => '信用卡支付';
}
// PayPal支付实现
class PayPalPayment implements PaymentGateway {
@override
void processPayment(double amount) {
print('PayPal支付: \$${amount.toStringAsFixed(2)}');
}
@override
String getPaymentName() => 'PayPal支付';
}
// 支付处理器(利用多态)
class PaymentProcessor {
void executePayment(PaymentGateway gateway, double amount) {
print('开始${gateway.getPaymentName()}...');
gateway.processPayment(amount);
print('支付完成');
}
}
void main() {
PaymentProcessor processor = PaymentProcessor();
PaymentGateway creditCard = CreditCardPayment();
PaymentGateway payPal = PayPalPayment();
processor.executePayment(creditCard, 99.99);
processor.executePayment(payPal, 49.95);
}
5.2 输出结果
开始信用卡支付...
信用卡支付: $99.99
支付完成
开始PayPal支付...
PayPal支付: $49.95
支付完成
总结
关键概念对比表
概念 | 关键字 | 主要用途 | 特点 |
---|---|---|---|
抽象类 | abstract | 定义标准/约束 | 包含抽象方法,不能被实例化 |
多态 | extends + @override | 统一接口不同实现 | 父类引用指向子类对象 |
接口 | implements | 定义严格规范 | 任何类都可作接口,必须实现所有成员 |
最佳实践建议
抽象类设计:
- 使用抽象类定义领域模型的核心行为
- 将通用实现放在抽象类的具体方法中
- 使用抽象方法强制子类实现特定行为
多态应用:
- 在需要统一处理多种子类时使用多态
- 通过父类类型声明变量/参数提高灵活性
- 结合工厂模式创建不同子类实例
接口实现:
- 优先使用抽象类定义接口规范
- 为不同实现提供统一的操作接口
- 在需要实现多个不相关功能时使用接口
组合使用:
dart// 继承复用 + 接口规范 abstract class Vehicle { void startEngine(); } class Car implements Vehicle { @override void startEngine() => print('汽车引擎启动'); } class ElectricCar extends Car { @override void startEngine() { super.startEngine(); print('电力系统就绪'); } }
Dart 中的 Mixins
概念
Mixins(混入) 是在类中混入其他功能的机制,允许实现类似多继承的功能。需要注意以下使用条件:
- 作为 mixins 的类只能继承自
Object
,不能继承其他类 - 作为 mixins 的类不能有构造函数
- 一个类可以 mixins 多个 mixins 类
- mixins 既不是继承也不是接口,而是 Dart 独有的特性
mixins 的类只能继承自Object
dart
// ✅ 合法的 mixin 类(隐式继承 Object)
class Printer {
void print() => print('Printing...');
}
// ❌ 非法的 mixin 类(继承了其他类)
class Base {}
class AdvancedPrinter extends Base { // 继承非 Object 类
void print() => print('Advanced printing...');
}
mixins 的类不能有构造函数
dart
// ✅ 合法的 mixin 类(无构造函数)
class Logger {
void log(String msg) => print('[LOG] $msg');
}
// ✅ 允许工厂构造函数
class Network {
factory Network() => Network._internal();
Network._internal(); // 私有构造仍被允许
void fetch() => print('Fetching data...');
}
// ❌ 非法的 mixin 类(包含生成构造函数)
class Authenticator {
Authenticator(this.token); // 显式构造函数
final String token;
void auth() => print('Authenticating...');
}
基础用法示例
dart
class A {
String info = "this is A";
void printA() {
print("A");
}
}
class B {
void printB() {
print("B");
}
}
class C with A, B {}
void main() {
var c = C();
c.printA(); // 输出: A
c.printB(); // 输出: B
print(c.info); // 输出: this is A
}
与继承结合使用
dart
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print('${this.name}----${this.age}');
}
void run() {
print("Person Run");
}
}
class A {
void run() {
print("A Run");
}
}
class B {
void run() {
print("B Run");
}
}
class C extends Person with B, A {
C(String name, num age) : super(name, age);
}
void main() {
var c = C('张三', 20);
c.printInfo(); // 输出: 张三----20
c.run(); // 输出: A Run (A中的方法覆盖了B和Person)
}
混入顺序的重要性:当存在同名方法时,最后混入的类优先级最高。上例中
A
在B
之后混入,因此A.run()
生效
Mixins 的类型特性
dart
class A {
String info = "this is A";
void printA() {
print("A");
}
}
class B {
void printB() {
print("B");
}
}
class C with A, B {}
void main() {
var c = C();
print(c is C); // true
print(c is A); // true (混入A的类型)
print(c is B); // true (混入B的类型)
}
关键总结
- 非继承机制:mixins 不是传统继承,而是功能组合
- 方法覆盖顺序:从右向左覆盖(最后混入的类优先级最高)
- 类型系统:混入后实例同时属于原始类和所有混入类类型
- 构造函数限制:混入类不能有自己的构造函数
- 单继承扩展:配合
extends
使用可实现功能增强
设计建议:优先使用 mixins 而非多重继承,它提供了更清晰、更安全的代码复用方式,同时避免了钻石继承问题
Dart 泛型
没有泛型的问题
dart
// 只能返回string类型
String getData(String value) {
return value;
}
// 需要为不同类型创建冗余方法
String getData1(String value) => value;
int getData2(int value) => value;
放弃类型检查的问题
dart
// 不指定类型放弃类型检查
getData(value) {
return value;
}
// 可以返回任意类型,但失去类型安全
泛型方法
解决类型安全和复用性问题:
dart
// 泛型方法定义
T getData<T>(T value) {
return value;
}
void main() {
print(getData<int>(12)); // 明确指定int类型
print(getData<String>('Hello')); // 明确指定String类型
}
泛型类
dart
class MyList<T> {
List<T> list = <T>[];
void add(T value) {
this.list.add(value);
}
List<T> getList() {
return list;
}
}
void main() {
// 存储字符串
MyList<String> stringList = MyList<String>();
stringList.add("Apple");
stringList.add("Banana");
print(stringList.getList()); // [Apple, Banana]
// 存储整数
MyList<int> intList = MyList<int>();
intList.add(10);
intList.add(20);
print(intList.getList()); // [10, 20]
}
Dart 内置 List 泛型
dart
void main() {
// 创建固定长度的字符串列表
List<String> fruits = List<String>.filled(2, "");
fruits[0] = "Apple";
fruits[1] = "Orange";
print(fruits); // [Apple, Orange]
// 创建固定长度的整数列表
List<int> numbers = List<int>.filled(3, 0);
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
print(numbers); // [1, 2, 3]
}
泛型接口
定义并实现泛型接口:
dart
// 定义泛型缓存接口
abstract class Cache<T> {
T? getByKey(String key);
void setByKey(String key, T value);
}
// 文件缓存实现
class FileCache<T> implements Cache<T> {
@override
T? getByKey(String key) {
// 实际从文件读取
return null;
}
@override
void setByKey(String key, T value) {
print("文件缓存: key=$key, value=$value");
}
}
// 内存缓存实现
class MemoryCache<T> implements Cache<T> {
@override
T? getByKey(String key) {
// 实际从内存读取
return null;
}
@override
void setByKey(String key, T value) {
print("内存缓存: key=$key, value=$value");
}
}
void main() {
// 字符串缓存
Cache<String> stringCache = MemoryCache<String>();
stringCache.setByKey('name', '张三');
// Map类型缓存
Cache<Map> mapCache = FileCache<Map>();
mapCache.setByKey('user', {'name': '李四', 'age': 25});
}
Dart 模块化与库系统详解
Dart 库的概念与类型
在 Dart 中,库(library) 是实现模块化的核心机制。每个 Dart 文件都是一个库,使用 import
关键字引入其他库。Dart 主要有三种库类型:
1. 自定义库
dart
import 'lib/my_library.dart';
2. 系统内置库
dart
import 'dart:math'; // 数学计算
import 'dart:io'; // I/O操作
import 'dart:convert'; // 编码转换
3. Pub 包管理系统中的库
- 在项目根目录创建
pubspec.yaml
文件
yaml
name: my_project
description: 我的Dart项目
dependencies:
http: ^0.13.4
date_format: ^2.0.7
- 运行命令获取依赖
bash
dart pub get
- 在代码中引入
dart
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';
async 和 await
Dart 的异步编程模型使用 async
和 await
:
- async:声明异步方法
- await:等待异步操作完成
dart
void main() async {
var result = await fetchData();
print(result);
}
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 1));
return '数据加载完成';
}
使用规则:
- 只有 async 方法才能使用 await
- 调用 async 方法必须使用 await
- async 方法返回 Future 对象
库冲突解决
当引入多个库有同名标识符时,使用 as
关键字创建别名:
dart
import 'lib/person1.dart';
import 'lib/person2.dart' as lib;
void main() {
Person p1 = Person(); // 使用 person1.dart 的 Person
lib.Person p2 = lib.Person(); // 使用 person2.dart 的 Person
}
部分导入
使用 show
和 hide
控制导入的内容:
1. 只导入特定部分 (show)
dart
import 'lib/my_math.dart' show add, multiply;
void main() {
print(add(2, 3)); // 可用
// print(subtract(5, 3)); // 错误:未导入
}
2. 隐藏特定部分 (hide)
dart
import 'lib/my_math.dart' hide divide;
void main() {
print(add(2, 3)); // 可用
// print(divide(6, 2)); // 错误:已隐藏
}
延迟加载(懒加载)
延迟加载减少初始启动时间,在需要时加载库:
dart
import 'package:heavy_library/heavy.dart' deferred as heavyLib;
void main() {
loadHeavyLibrary();
}
Future<void> loadHeavyLibrary() async {
await heavyLib.loadLibrary();
heavyLib.runHeavyOperation();
}
Pub 包管理实战
使用 http 包请求数据
dart
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main() async {
var url = Uri.https('jsonplaceholder.typicode.com', '/todos/1');
var response = await http.get(url);
if (response.statusCode == 200) {
var json = convert.jsonDecode(response.body);
print('标题: ${json['title']}');
} else {
print('请求失败: ${response.statusCode}');
}
}
使用 date_format 包格式化日期
dart
import 'package:date_format/date_format.dart';
void main() {
final now = DateTime.now();
print(formatDate(now, [yyyy, '-', mm, '-', dd])); // 2023-10-15
print(formatDate(now, [HH, ':', nn, ':', ss])); // 14:30:45
}
Dart 空安全特性详解
1. 空安全概述
Null safety(空安全)是 Dart 的一项重要特性,主要目的:
- 避免空引用错误(Null Pointer Exceptions)
- 提高代码健壮性和可维护性
- 改善运行时性能
Flutter 2.2.0(2021 年 5 月 19 日发布)及更高版本要求使用空安全。
2. 可空类型(?)
2.1 基本用法
使用?
声明可空类型:
dart
// 非空类型
int a = 123;
String username = "张三";
List<String> l1 = ["张三", "李四"];
// 可空类型
int? nullableInt = null;
String? nullableString = null;
List<String>? nullableList = null;
2.2 函数中的可空类型
dart
String? getData(String? apiUrl) {
if (apiUrl != null) {
return "this is server data";
}
return null;
}
void main() {
print(getData("https://api.example.com")); // 正常数据
print(getData(null)); // 返回null
}
3. 类型断言(!)
3.1 安全使用断言
dart
void printLength(String? str) {
if (str != null) {
print(str.length); // 安全访问
}
}
3.2 带异常处理的断言
dart
void printLength(String? str) {
try {
print(str!.length); // 断言非空
} catch (e) {
print("str is null");
}
}
4. late 关键字
4.1 延迟初始化
dart
class Person {
late String name; // 延迟初始化
late int age;
void setName(String name, int age) {
this.name = name;
this.age = age;
}
String getName() => "$name---$age";
}
void main() {
Person p = Person();
p.setName("张三", 20);
print(p.getName()); // 张三---20
}
4.2 使用注意事项
late
变量必须在访问前初始化- 适用于确定会被初始化的场景
- 避免在构造函数体之外使用
late
+可空类型
5. required 关键字
5.1 函数参数中的 required
dart
// 可选参数
String printUserInfo(String username, {int age = 10, String sex = "男"}) {
return "姓名:$username---性别:$sex--年龄:$age";
}
// 必需命名参数
String printInfo(String username, {required int age, required String sex}) {
return "姓名:$username---性别:$sex--年龄:$age";
}
void main() {
print(printUserInfo('张三')); // 姓名:张三---性别:男--年龄:10
print(printInfo('张三', age: 22, sex: "女")); // 姓名:张三---性别:女--年龄:22
}
5.2 类构造函数中的 required
dart
class Person {
final String name;
final int age;
Person({required this.name, required this.age});
String getInfo() => "$name---$age";
}
class UserProfile {
String? nickname; // 可空属性
required int loginCount; // 必需属性
UserProfile({this.nickname, required this.loginCount});
}
5.3 混合使用示例
dart
class User {
late final String id; // 延迟初始化
String? email; // 可空属性
required DateTime createdAt; // 必需属性
User({required this.id, this.email, required this.createdAt});
String get displayId => id;
void updateEmail(String? newEmail) {
if (newEmail != null) {
email = newEmail;
}
}
}
6. 空安全最佳实践
6.1 空安全操作符对比
操作符 | 示例 | 用途 | 安全等级 |
---|---|---|---|
? | String? name; | 声明可空类型 | ★★★★☆ |
! | name!.length | 类型断言 | ★★☆☆☆ |
?? | name ?? "默认" | 空值合并 | ★★★★★ |
?. | user?.getName() | 安全调用 | ★★★★★ |
late | late String id; | 延迟初始化 | ★★★☆☆ |
required | {required param} | 必需参数 | ★★★★☆ |
6.2 推荐实践模式
优先使用安全调用操作符(?.)
dartuser?.profile?.address?.city ?? "未知"
合理使用空值合并操作符(??)
dartString displayName = username ?? "匿名用户";
避免过度使用!断言
dart// 不推荐 print(user!.name!.length!); // 推荐 if (user != null && user.name != null) { print(user.name.length); }
为 late 变量添加文档说明
dart/// 用户唯一标识符,在首次登录时初始化 late final String userId;
结合 required 和可空类型
dartclass Product { required String id; required String name; String? description; // 可选描述 }
7. 空安全迁移策略
7.1 迁移步骤
- 升级 Dart SDK 到 2.12+版本
- 在 pubspec.yaml 中添加:yaml
environment: sdk: ">=2.12.0 <3.0.0"
- 运行迁移工具:bash
dart migrate
- 手动修复迁移工具未解决的问题
7.2 常见迁移问题及解决
问题类型 | 错误示例 | 解决方案 |
---|---|---|
非空变量可能为 null | int i = null; | 改为int? i = null; |
缺少必需参数 | func({param}); | 添加required : func({required param}); |
未初始化 late 变量 | late String s; print(s); | 确保访问前初始化 |
不必要的 null 检查 | if (nonNullableVar != null) | 移除冗余检查 |
总结
Dart 的空安全特性通过以下方式提升代码质量:
- 可空类型(?):明确标识可能为 null 的变量
- 类型断言(!):在确保非空时进行转换(谨慎使用)
- late 关键字:延迟初始化非空变量
- required 关键字:强制必需参数的传递