Skip to content

Dart 基础

Dart 介绍

Dart 是 Google 开发的现代化编程语言,专为跨平台开发设计。其核心特性包括:

全场景覆盖:支持 Web 前端、服务端、移动端(Android/iOS)及 IoT 设备开发 高效开发体验:通过 AOT 编译实现原生级性能,JIT 编译支持热重载 现代语法体系:融合强类型系统、异步编程模型(async/await)和面向对象特性 该语言诞生于 2011 年,最初定位为 JavaScript 的替代方案。经过技术沉淀,Dart 在 2017 年随 Flutter 框架发布后迎来发展转折点。Flutter 凭借其创新的 UI 渲染引擎和声明式编程范式,使 Dart 成为构建跨平台应用的首选语言。

Dart 变量、常量及命名规则

变量

  1. 动态类型推导
    Dart 是强类型语言,但支持自动类型推断。使用 var 声明变量时,类型根据初始值自动确定:

    dart
    var str = '自动推断为 String'; // 类型:String
    var num = 100;             // 类型:int

    注意:变量类型一旦确定不可更改:

    dart
    var value = 'hello';
    value = 123; // ❌ 编译错误:int 不能赋值给 String
  2. 显式类型声明
    可直接指定类型(推荐用于成员变量或复杂场景):

    dart
    String 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 的主要区别

特性constfinal
初始化时机编译时运行时
赋值次数只能赋值一次(在声明时)允许先声明后赋值(一次)
值确定时间编译期间运行期间
使用场景编译时已知的常量值运行时确定的常量值
初始化特性立即初始化惰性初始化

常量构造函数

  1. 常量构造函数需以 const 关键字修饰
  2. 必须用于所有成员变量都是 final 的类
  3. 实例化时不加 const 修饰符,创建的是普通对象
  4. 实例化时加 const 修饰符:
    • 多个地方创建相同值的对象,只会保留一个实例
    • 传入相同参数时,返回相同对象
  5. 在 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 - 不同对象
}

命名规则

  1. 合法字符:字母、数字、下划线(_)、美元符($),不能以数字开头
    ✅ 正确:_user, totalAmount, $status
    ❌ 错误:1stPlace, user-name

  2. 区分大小写

    dart
    var name = 'Alice';
    var Name = 'Bob'; // 与 name 不同
  3. 禁止关键字
    不可使用保留字(如 if, class, for 等)作为标识符。

  4. 命名规范

    • 变量/属性:使用小驼峰(首个单词小写,后续单词首字母大写)
      studentName, isValid, totalCount
    • 类/类型:使用大驼峰(每个单词首字母大写)
      class UserProfile {}, enum StatusCode { ok }
    • 见名知意
      var a = 10;
      var itemCount = 10;

Dart 数据类型

  • 数值类型int(整数)和 double(浮点数)
  • 字符串类型:单引号、双引号、三引号
  • 布尔类型truefalse
  • 列表类型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(布尔类型)

布尔类型只有 truefalse 两个值:

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 + 518
-减法13 - 58
*乘法13 * 565
/除法13 / 52.6
%取余13 % 53
~/取整除法13 ~/ 52
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 == 3false
!=不等于5 != 3true
>大于5 > 3true
<小于5 < 3false
>=大于等于5 >= 5true
<=小于等于5 <= 3false
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
}

逻辑运算符

运算符描述示例结果
!取反!falsetrue
&&逻辑与true && falsefalse
||逻辑或true || falsetrue
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 += 10a = a + 10
-=减后赋值a -= 5a = a - 5
*=乘后赋值a *= 3a = a * 3
/=除后赋值a /= 2a = a / 2
%=取余后赋值a %= 4a = a % 4
~/=取整后赋值a ~/= 4a = 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);
}

注意事项:

  1. break 会立即终止当前循环
  2. continue 会跳过当前迭代的剩余代码
  3. 在多层循环中,break 只能跳出当前层循环
  4. 在 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遍历每个元素voidlist.forEach((v) => print(v))
map转换每个元素Iterablelist.map((v) => v*2).toList()
where过滤元素Iterablelist.where((v) => v>5).toList()
any检查是否至少一个满足条件boollist.any((v) => v>5)
every检查是否所有元素满足条件boollist.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 的三个基本特征:

  1. 封装:将数据和操作数据的方法绑定在一起
  2. 继承:子类复用父类的功能并扩展
  3. 多态:同一操作作用于不同对象产生不同行为

类定义与使用

基本类定义

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 最佳实践

  1. 使用@override注解明确表示覆写
  2. 优先使用级联操作符进行链式调用
  3. 对于可能为 null 的对象,使用空安全特性而非?.
  4. 在子类构造函数中始终调用父类构造函数
  5. 合理使用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 多态的优势

  1. 代码解耦:调用方只关心父类接口
  2. 扩展性强:添加新子类不影响现有代码
  3. 统一处理:可以统一处理不同子类对象
  4. 灵活性高:运行时决定具体执行哪个方法

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 当:
    1. 需要复用父类中的具体方法
    2. 需要建立"是一个"的关系
    3. 需要部分定制父类行为
  • 使用 implements 当:
    1. 需要遵循严格的接口规范
    2. 需要实现多接口
    3. 不需要复用已有实现

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定义严格规范任何类都可作接口,必须实现所有成员

最佳实践建议

  1. 抽象类设计

    • 使用抽象类定义领域模型的核心行为
    • 将通用实现放在抽象类的具体方法中
    • 使用抽象方法强制子类实现特定行为
  2. 多态应用

    • 在需要统一处理多种子类时使用多态
    • 通过父类类型声明变量/参数提高灵活性
    • 结合工厂模式创建不同子类实例
  3. 接口实现

    • 优先使用抽象类定义接口规范
    • 为不同实现提供统一的操作接口
    • 在需要实现多个不相关功能时使用接口
  4. 组合使用

    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(混入) 是在类中混入其他功能的机制,允许实现类似多继承的功能。需要注意以下使用条件:

  1. 作为 mixins 的类只能继承自Object,不能继承其他类
  2. 作为 mixins 的类不能有构造函数
  3. 一个类可以 mixins 多个 mixins 类
  4. 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)
}

混入顺序的重要性:当存在同名方法时,最后混入的类优先级最高。上例中AB之后混入,因此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的类型)
}

关键总结

  1. 非继承机制:mixins 不是传统继承,而是功能组合
  2. 方法覆盖顺序:从右向左覆盖(最后混入的类优先级最高)
  3. 类型系统:混入后实例同时属于原始类和所有混入类类型
  4. 构造函数限制:混入类不能有自己的构造函数
  5. 单继承扩展:配合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 包管理系统中的库

  1. 在项目根目录创建 pubspec.yaml 文件
yaml
name: my_project
description: 我的Dart项目
dependencies:
  http: ^0.13.4
  date_format: ^2.0.7
  1. 运行命令获取依赖
bash
dart pub get
  1. 在代码中引入
dart
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';

async 和 await

Dart 的异步编程模型使用 asyncawait

  • async:声明异步方法
  • await:等待异步操作完成
dart
void main() async {
  var result = await fetchData();
  print(result);
}

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 1));
  return '数据加载完成';
}

使用规则

  1. 只有 async 方法才能使用 await
  2. 调用 async 方法必须使用 await
  3. 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
}

部分导入

使用 showhide 控制导入的内容:

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 使用注意事项

  1. late变量必须在访问前初始化
  2. 适用于确定会被初始化的场景
  3. 避免在构造函数体之外使用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()安全调用★★★★★
latelate String id;延迟初始化★★★☆☆
required{required param}必需参数★★★★☆

6.2 推荐实践模式

  1. 优先使用安全调用操作符(?.)

    dart
    user?.profile?.address?.city ?? "未知"
  2. 合理使用空值合并操作符(??)

    dart
    String displayName = username ?? "匿名用户";
  3. 避免过度使用!断言

    dart
    // 不推荐
    print(user!.name!.length!);
    
    // 推荐
    if (user != null && user.name != null) {
      print(user.name.length);
    }
  4. 为 late 变量添加文档说明

    dart
    /// 用户唯一标识符,在首次登录时初始化
    late final String userId;
  5. 结合 required 和可空类型

    dart
    class Product {
      required String id;
      required String name;
      String? description; // 可选描述
    }

7. 空安全迁移策略

7.1 迁移步骤

  1. 升级 Dart SDK 到 2.12+版本
  2. 在 pubspec.yaml 中添加:
    yaml
    environment:
      sdk: ">=2.12.0 <3.0.0"
  3. 运行迁移工具:
    bash
    dart migrate
  4. 手动修复迁移工具未解决的问题

7.2 常见迁移问题及解决

问题类型错误示例解决方案
非空变量可能为 nullint i = null;改为int? i = null;
缺少必需参数func({param});添加required: func({required param});
未初始化 late 变量late String s; print(s);确保访问前初始化
不必要的 null 检查if (nonNullableVar != null)移除冗余检查

总结

Dart 的空安全特性通过以下方式提升代码质量:

  1. 可空类型(?):明确标识可能为 null 的变量
  2. 类型断言(!):在确保非空时进行转换(谨慎使用)
  3. late 关键字:延迟初始化非空变量
  4. required 关键字:强制必需参数的传递