flutter可以用来制作跨平台应用,配套语言是dart

配置环境

​ image-20260415155821048

先下载dartsdk

下载完成后终端输入检测是否下载成功

text
12
dart --version
flutter

我习惯用VScode,所以你们要是用VScode的话去下载flutter和dart相关的插件(Trae和cursor还有windsurf本质上也是VScode,插件一般都一样)

dart语言

因为我学过很多语言,而这些语言基本上差不多,dart给我的感觉很像java和C#,包括C#里的很多概念dart里都有(估计是开发dart的照抄C#这种的)

和C#一样,这个必须在main函数中运行

变量

关键字 / 类型是否可重新赋值是否可变类型是否必须初始化是否编译时常量说明
var✅❌✅❌自动推断类型(最常用)
int / String / num / double / bool✅❌✅❌显式类型(推荐规范写法)
dynamic✅✅❌❌动态类型(不安全,慎用)
Object✅❌(需强转)❌❌所有类型父类(更安全)
final❌(只能一次)❌❌❌运行时常量
const❌(完全不可变)❌✅✅编译时常量
late✅❌❌(可延迟)❌延迟初始化(常用于Flutter)
late final❌(只能一次)❌❌❌延迟 + 单次赋值

其中var int String double bool Object const这些我们已经相当熟悉了,其中num表示整数或小数

但是这里面的其他的一些我们是第一次见

数字类型

num (父类)
├── int (整型)
└── double (浮点型)

赋值方向是否允许原因
int → num✅ 允许子类赋值给父类(向上转型)
double → num✅ 允许子类赋值给父类(向上转型)
num → int❌ 不允许父类不能隐式转为子类
num → double❌ 不允许父类不能隐式转为子类
int → double❌ 不允许不同类型,不能隐式转换
double → int❌ 不允许不同类型,不能隐式转换

这里我们知道隐式转换和显示转换

对于隐式转换

dart
1234567891011121314151617
void main() {
  int i = 10;
  double d = 3.14;

  // int 可以赋值给 num
  num n1 = i;
  print('n1: $n1, 类型: ${n1.runtimeType}'); // n1: 10, 类型: int

  // double 可以赋值给 num
  num n2 = d;
  print('n2: $n2, 类型: ${n2.runtimeType}'); // n2: 3.14, 类型: double

  // num 变量可以接受任意数字
  num n3 = 100; // 存 int
  n3 = 2.718; // 改存 double
  print('n3: $n3'); // n3: 2.718
}

对于显示转换(其实就是调用方法比如to...()这种方法)

dart
12345678910111213141516171819202122232425
void main() {
  num n = 10;
  
  // ✅ 显式转 int
  int i = n.toInt();
  print('i: $i');  // i: 10
  
  // ✅ 显式转 double
  double d = n.toDouble();
  print('d: $d');  // d: 10.0
  
  // int 转 double
  int i2 = 10;
  double d2 = i2.toDouble();  // ✅ 10 → 10.0
  print('d2: $d2');
  
  // double 转 int(截断小数)
  double d3 = 3.99;
  int i3 = d3.toInt();  // ✅ 3.99 → 3(不是四舍五入!)
  print('i3: $i3');     // i3: 3
  
  // double 四舍五入
  int i4 = d3.round();  // ✅ 3.99 → 4
  print('i4: $i4');
}

final

当需要存储一个不变的数据,但是在运时才确定,需要使final声明常量

编译时常量:当需要存储一个不变的数据,且在编译时就确定,需要使const声明常量

image-20260415165147420
dart
1234567891011121314151617
class Circle {
  // final 实例变量必须在声明时或构造函数中初始化
  final double radius;
  final double pi = 3.14159; // 声明时初始化

  // 构造函数初始化列表
  Circle(this.radius);

  double get area => pi * radius * radius;
}

void main() {
  final circle = Circle(5.0);
  print('面积: ${circle.area}'); // 78.53975

  // circle.radius = 10.0;  // ❌ 错误:不能修改 final 字段
}

这个也是和const常量一样赋值之后不可以再次修改

String

String 基础

dart
123456789101112
void main() {
  // 三种创建方式
  String s1 = '单引号';
  String s2 = "双引号";
  String s3 = '''多行
  字符串
  支持换行''';
  
  print(s1);
  print(s2); 
  print(s3);
}

字符串插值

dart
1234567891011
void main() {
  String name = 'Dart';
  int version = 3;
  
  // $变量名
  print('语言: $name');
  
  // ${表达式}
  print('版本: ${version + 1}');
  print('大写: ${name.toUpperCase()}');
}

String常用方法

toString()方法将其他类型转换为字符串

dart
12345
void main() {
  num a = 5555;
  a.toString();
  print(a);
}

{% hideToggle 其他方法 %}

一、长度 / 判空
text
1
String s = "hello";
方法作用示例
length字符长度s.length → 5
isEmpty是否为空"".isEmpty → true
isNotEmpty是否不为空"hi".isNotEmpty → true

二、查找 / 判断
方法作用示例
contains()是否包含"hello".contains("he") → true
startsWith()是否以…开头"hello".startsWith("h")
endsWith()是否以…结尾"hello".endsWith("o")
indexOf()第一次出现位置"hello".indexOf("l") → 2
lastIndexOf()最后一次出现"hello".lastIndexOf("l") → 3

三、截取 / 分割
方法作用示例
substring(start, end)截取字符串"hello".substring(1,3) → "el"
split()分割字符串"a,b,c".split(",") → ["a","b","c"]

四、替换
方法作用示例
replaceAll()替换全部"aabb".replaceAll("a","x") → "xxbb"
replaceFirst()替换第一个"aabb".replaceFirst("a","x") → "xabb"

五、大小写转换
方法作用示例
toLowerCase()转小写"HELLO".toLowerCase()
toUpperCase()转大写"hello".toUpperCase()

六、去空格
方法作用示例
trim()去两边空格" hi ".trim() → "hi"
trimLeft()去左边" hi".trimLeft()
trimRight()去右边"hi ".trimRight()

{% endhideToggle %}

List列表类型

这个和C#里的几乎一样,C#里面也有list,不过list和数组差不多,包括访问数组里的元素方法也是一样的

dart
123456789
void main() {
  // 三种创建方式
  List<int> numbers = [1, 2, 3];           // 字面量
  var fruits = <String>['apple', 'banana']; // 类型推断
  List empty = List.empty();                // 空列表(growable: false)
  List growable = List.empty(growable: true); // 可增长的空列表
  
  print(numbers);  // [1, 2, 3]
}

常用的方法:

在尾部添加------------add(内容)

在尾部添加一个列表------------addAll (列表)

删除满足内容的第一个----------remove(内容)

删除最后一个-----------removeLast()

删除索引范围内数据----------removeRange(start,end)

循环-------------forEach((item) {});

是否都满足条件---------------every((item){return 布尔值 });

筛选出满足条件的数据---------------where((item){return 布尔值 });

列表的长度(属性)-length

最后一个元素(属性)--------------last

第一个元素(属性)-first是否为空(属性)-----------isEmpty

dart
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
void main() {
  List<String> colors = ['red', 'green', 'blue'];
  List<String> color1 = ['skyblue'];

  // 尾部添加
  colors.add("yellow");
  print(colors);

  // 尾部添加一个列表
  colors.addAll(color1);
  print(colors);

  // 删除某一个(删除第一个匹配的)
  colors.remove('blue');
  print(colors);

  // 删除最后一个
  colors.removeLast();
  print(colors);

  // 删除索引范围内的数据(start <= index < end)
  colors.removeRange(1, 2);
  print(colors);

  // 循环遍历
  colors.forEach((item) {
    print(item);
  });

  // 是否都满足条件
  bool allStartWithR = colors.every((item) {
    return item.startsWith("r");
  });
  print("是否全部以 r 开头: $allStartWithR");

  // 筛选满足条件的数据
  var result = colors.where((item) {
    return item.contains("e");
  });
  print("包含 e 的元素: $result");

  // 长度
  print("长度: ${colors.length}");

  // 第一个元素
  print("第一个: ${colors.first}");

  // 最后一个元素
  print("最后一个: ${colors.last}");

  // 是否为空
  print("是否为空: ${colors.isEmpty}");
}
dart
123456789101112131415161718
void main() {
  List<String> colors = ['红red', '绿green', '蓝blue'];
  List<String> color1 = ['天蓝skyblue'];

  //是否所有元素都满足条件
  print(
    colors.every((item) {
      return item.toString().startsWith('红');
    }),
  );

  //筛选出满足条件的元素
  print(
    colors.where((item) {
      return item.toString().startsWith('绿');
    }).toList(),
  );
}

{% hideToggle 具体例子 %}

访问与修改

dart
12345678910111213141516
void main() {
  List<String> colors = ['red', 'green', 'blue'];
  
  // 访问元素
  print(colors[0]);      // red(第一个)
  print(colors.first);   // red
  print(colors.last);    // blue
  print(colors.length);  // 3
  
  // 修改元素
  colors[1] = 'yellow';
  print(colors);  // [red, yellow, blue]
  
  // 安全访问
  print(colors.elementAtOrNull(5));  // null(不会报错)
}

添加元素

dart
12345678910111213
void main() {
  List<int> list = [];
  
  // 添加单个
  list.add(1);           // [1]
  list.addAll([2, 3]);   // [1, 2, 3]
  
  // 指定位置插入
  list.insert(0, 0);     // [0, 1, 2, 3](在索引0插入0)
  list.insertAll(2, [99, 100]);  // [0, 1, 99, 100, 2, 3]
  
  print(list);
}

删除元素

dart
123456789101112131415161718
void main() {
  List<String> items = ['a', 'b', 'c', 'b', 'd'];
  
  // 按值删除(第一个匹配)
  items.remove('b');     // ['a', 'c', 'b', 'd']
  
  // 按索引删除
  items.removeAt(0);     // ['c', 'b', 'd']
  
  // 删除最后一个
  items.removeLast();    // ['c', 'b']
  
  // 按条件删除
  items.removeWhere((e) => e == 'b');  // ['c']
  
  // 清空
  items.clear();         // []
}

查询与查找

dart
12345678910111213141516171819
void main() {
  List<int> numbers = [10, 20, 30, 40, 50];
  
  // 包含判断
  print(numbers.contains(20));  // true
  
  // 查找位置
  print(numbers.indexOf(30));      // 2
  print(numbers.lastIndexOf(10));  // 0
  
  // 查找元素(按条件)
  int first = numbers.firstWhere((n) => n > 25);  // 30
  int? safe = numbers.firstWhere((n) => n > 100, orElse: () => -1);  // -1
  
  // 查找索引
  int idx = numbers.indexWhere((n) => n > 25);  // 2
  
  print('first: $first, idx: $idx');
}

切片与范围

dart
1234567891011121314
void main() {
  List<String> list = ['a', 'b', 'c', 'd', 'e'];
  
  // 获取子列表(视图,修改会影响原列表)
  List<String> sub = list.sublist(1, 4);  // ['b', 'c', 'd']
  
  // 范围替换
  list.replaceRange(1, 3, ['X', 'Y']);  // ['a', 'X', 'Y', 'd', 'e']
  
  // 填充范围
  list.fillRange(0, 2, 'Z');  // ['Z', 'Z', 'Y', 'd', 'e']
  
  print(list);
}

排序与反转

dart
123456789101112131415161718
void main() {
  List<int> nums = [3, 1, 4, 1, 5, 9, 2, 6];
  
  // 原地排序
  nums.sort();  // [1, 1, 2, 3, 4, 5, 6, 9]
  nums.sort((a, b) => b - a); // 降序 [9, 6, 5, 4, 3, 2, 1, 1]
  
  // 自定义排序
  List<String> fruits = ['banana', 'apple', 'pie'];
  fruits.sort((a, b) => a.length.compareTo(b.length));  // ['pie', 'apple', 'banana']
  
  // 反转(返回新列表)
  var reversed = nums.reversed.toList();  // [9, 6, 5, 4, 3, 2, 1, 1]
  
  // 原地反转
  nums = [1, 2, 3];
  // Dart 没有原地反转方法,需要手动或转数组
}

映射与过滤

dart
1234567891011121314151617181920212223
void main() {
  List<int> numbers = [1, 2, 3, 4, 5];
  
  // map:转换每个元素
  List<String> strings = numbers.map((n) => 'No.$n').toList();
  print(strings);  // [No.1, No.2, No.3, No.4, No.5]
  
  // where:过滤
  List<int> evens = numbers.where((n) => n.isEven).toList();
  print(evens);  // [2, 4]
  
  // expand:展开嵌套
  List<List<int>> nested = [[1, 2], [3, 4]];
  List<int> flat = nested.expand((e) => e).toList();
  print(flat);  // [1, 2, 3, 4]
  
  // 链式操作
  var result = numbers
      .where((n) => n > 2)      // [3, 4, 5]
      .map((n) => n * 2)         // [6, 8, 10]
      .toList();
  print(result);
}

聚合操作

dart
1234567891011121314151617181920
void main() {
  List<int> scores = [85, 92, 78, 90, 88];
  
  // 遍历
  scores.forEach((s) => print(s));
  
  // 折叠/归约
  int sum = scores.reduce((a, b) => a + b);  // 433
  int total = scores.fold(0, (acc, s) => acc + s);  // 433(带初始值)
  
  // 条件判断
  bool allPass = scores.every((s) => s >= 60);  // true
  bool hasExcellent = scores.any((s) => s >= 90);  // true
  
  // 查找极值
  int max = scores.reduce((a, b) => a > b ? a : b);  // 92
  int min = scores.reduce((a, b) => a < b ? a : b);  // 78
  
  print('总分: $total, 最高分: $max');
}

固定长度 vs 可增长

dart
12345678910111213141516171819
void main() {
  // 可增长列表(默认)
  List<int> growable = [1, 2, 3];
  growable.add(4);      // ✅ 可以添加
  growable.length = 10; // ✅ 可以改长度(填充null)
  
  // 固定长度列表
  List<int> fixed = List.filled(3, 0);  // [0, 0, 0]
  fixed[0] = 10;        // ✅ 可以修改元素
  // fixed.add(4);      // ❌ 错误:不能添加
  // fixed.length = 5;  // ❌ 错误:不能改长度
  
  // 从可增长转固定
  var fixed2 = List.unmodifiable(growable);  // 不可修改的列表
  // fixed2[0] = 100;   // ❌ 运行时错误
  
  print('growable: $growable');
  print('fixed: $fixed');
}

展开运算符

dart
123456789101112131415161718192021222324
void main() {
  List<int> list1 = [1, 2, 3];
  List<int> list2 = [4, 5, 6];
  
  // 展开 ...(合并列表)
  List<int> combined = [...list1, ...list2];  // [1, 2, 3, 4, 5, 6]
  
  // 条件展开
  bool showExtra = true;
  List<int> conditional = [
    1,
    2,
    if (showExtra) ...[3, 4, 5],  // 条件成立才展开
  ];
  
  // for 展开
  List<String> repeated = [
    for (var i in [1, 2, 3]) 'item-$i',  // ['item-1', 'item-2', 'item-3']
  ];
  
  print(combined);
  print(conditional);
  print(repeated);
}
操作方法
添加add(), addAll(), insert(), insertAll()
删除remove(), removeAt(), removeLast(), removeWhere(), clear()
访问[], first, last, elementAt(), single
查找contains(), indexOf(), indexWhere(), firstWhere()
切片sublist(), getRange(), replaceRange(), fillRange()
排序sort(), reversed
转换map(), where(), expand(), cast<T>()
聚合forEach(), reduce(), fold(), every(), any(), join()
其他shuffle(), asMap(), toSet(), toString()

{% endhideToggle %}

Map字典类型

这个和C#里Dictionary的一样

字典这个已经很熟悉了只不过这里的字典格式和列表差不多,反正和其他语言里的一样就是键值对

dart
123456789101112
void main() {
  // 三种创建方式
  Map<String, int> scores = {'Alice': 90, 'Bob': 85};  // 字面量
  var ages = <String, int>{'Tom': 20, 'Jerry': 22};     // 类型推断
  Map empty = {};                                       // 空 Map
  
  // 构造函数创建
  Map<String, String> config = Map();
  Map<int, String> numbers = Map.from({1: 'one', 2: 'two'});
  
  print(scores);  // {Alice: 90, Bob: 85}
}

循环---------forEach

在添加一个字典-------------addAll

是否包含某个key------------containsKey

删除某个key----------remove

清空---------clear

dart
1234567891011121314151617181920212223242526
void main() {
  Map<String, String> player = {'女儿': '阿尼亚', '母亲': '约尔', '父亲': '劳埃德'};
  print(player);

  //循环 forEach
  player.forEach((key, value) {
    print('$key: $value');
  });

  //再添加一个字典--addAll
  Map<String, String> player2 = {'同学': '达米安'};
  player.addAll(player2);
  print(player);

  //是否包含某个键
  print(player.containsKey('女儿')); //true
  print(player.containsKey('儿子')); //false

  //删除某个键
  player.remove('同学');
  print(player);

  //清空字典
  player.clear();
  print(player); //{}
}

{% hideToggle 具体例子 %}

添加与修改

dart
12345678910111213141516171819
void main() {
  Map<String, int> map = {};
  
  // 添加键值对
  map['a'] = 1;           // 直接赋值
  map.putIfAbsent('b', () => 2);  // key不存在时才添加
  
  // 批量添加
  map.addAll({'c': 3, 'd': 4});
  
  // 修改值
  map['a'] = 100;         // 已存在则覆盖
  
  // 更新(基于旧值)
  map.update('a', (v) => v * 2);           // 100 -> 200
  map.update('z', (v) => v * 2, ifAbsent: () => 0);  // 不存在则设为0
  
  print(map);  // {a: 200, b: 2, c: 3, d: 4, z: 0}
}

删除

dart
1234567891011121314
void main() {
  Map<String, int> map = {'a': 1, 'b': 2, 'c': 3, 'd': 4};
  
  // 按键删除
  map.remove('a');        // 删除key 'a',返回被删的值(1),不存在返回null
  
  // 条件删除
  map.removeWhere((k, v) => v.isEven);  // 删除值为偶数的项(b:2, d:4)
  
  // 清空
  // map.clear();
  
  print(map);  // {c: 3}
}

访问与查询

dart
1234567891011121314151617181920212223
void main() {
  Map<String, int> map = {'Alice': 90, 'Bob': 85, 'Carol': 95};
  
  // 访问值
  print(map['Alice']);        // 90
  print(map['Unknown']);      // null(key不存在)
  
  // 安全访问
  int? score = map['Unknown'];
  int safe = map['Unknown'] ?? 0;  // 不存在返回默认值0
  
  // 检查存在性
  print(map.containsKey('Alice'));   // true
  print(map.containsValue(100));     // false
  
  // 获取键值集合
  print(map.keys);     // (Alice, Bob, Carol)
  print(map.values);   // (90, 85, 95)
  print(map.entries);  // (MapEntry(Alice: 90), ...)
  
  // 遍历
  map.forEach((k, v) => print('$k: $v'));
}

遍历方式

dart
12345678910111213141516171819202122
void main() {
  Map<String, int> map = {'a': 1, 'b': 2, 'c': 3};
  
  // 方式1:forEach
  map.forEach((key, value) {
    print('$key = $value');
  });
  
  // 方式2:for-in entries
  for (var entry in map.entries) {
    print('${entry.key}: ${entry.value}');
  }
  
  // 方式3:解构遍历(Dart 3)
  for (var MapEntry(:key, :value) in map.entries) {
    print('$key -> $value');
  }
  
  // 方式4:只遍历key或value
  for (var k in map.keys) print(k);
  for (var v in map.values) print(v);
}

转换与映射

dart
123456789101112131415161718192021
void main() {
  Map<String, int> scores = {'Alice': 90, 'Bob': 85, 'Carol': 95};
  
  // map:转换每个值
  Map<String, String> grades = scores.map((k, v) => 
    MapEntry(k, v >= 90 ? 'A' : 'B')
  );
  print(grades);  // {Alice: A, Bob: B, Carol: A}
  
  // 过滤(没有直接filter,需要转换)
  Map<String, int> excellent = Map.fromEntries(
    scores.entries.where((e) => e.value >= 90)
  );
  print(excellent);  // {Alice: 90, Carol: 95}
  
  // 键值互换
  Map<int, String> reversed = Map.fromEntries(
    scores.entries.map((e) => MapEntry(e.value, e.key))
  );
  print(reversed);  // {90: Alice, 85: Bob, 95: Carol}
}

合并与操作

dart
1234567891011121314151617181920212223
void main() {
  Map<String, int> m1 = {'a': 1, 'b': 2};
  Map<String, int> m2 = {'b': 20, 'c': 3};  // b冲突
  
  // 展开合并(Dart 2.3+)
  Map<String, int> combined = {...m1, ...m2};  // m2的b覆盖m1的b
  print(combined);  // {a: 1, b: 20, c: 3}
  
  // 自定义合并(处理冲突)
  Map<String, int> merged = {};
  merged.addAll(m1);
  m2.forEach((k, v) {
    merged[k] = (merged[k] ?? 0) + v;  // 值相加
  });
  print(merged);  // {a: 1, b: 22, c: 3}
  
  // 条件展开
  bool useM2 = true;
  Map<String, int> conditional = {
    ...m1,
    if (useM2) ...m2,
  };
}

常用构造函数

dart
12345678910111213141516171819202122
void main() {
  // 固定key,延迟计算value
  Map<String, int> lazy = Map.fromIterable(
    ['a', 'b', 'c'],
    key: (item) => item,
    value: (item) => item.codeUnitAt(0),
  );
  print(lazy);  // {a: 97, b: 98, c: 99}
  
  // 分组(Dart 2.13+)
  List<String> words = ['apple', 'banana', 'apricot', 'blueberry'];
  Map<String, List<String>> grouped = {};
  for (var w in words) {
    String key = w[0];  // 按首字母分组
    grouped.putIfAbsent(key, () => []).add(w);
  }
  print(grouped);  // {a: [apple, apricot], b: [banana, blueberry]}
  
  // 不可变Map
  Map<String, int> readonly = Map.unmodifiable({'a': 1});
  // readonly['a'] = 2;  // ❌ 运行时错误
}

排序与排序后的Map

dart
123456789101112131415161718
void main() {
  Map<String, int> scores = {'Bob': 85, 'Alice': 90, 'Carol': 95};
  
  // 按键排序(返回List)
  var sortedByKey = Map.fromEntries(
    scores.entries.toList()..sort((a, b) => a.key.compareTo(b.key))
  );
  print(sortedByKey);  // {Alice: 90, Bob: 85, Carol: 95}
  
  // 按值排序
  var sortedByValue = Map.fromEntries(
    scores.entries.toList()..sort((a, b) => a.value.compareTo(b.value))
  );
  print(sortedByValue);  // {Bob: 85, Alice: 90, Carol: 95}
  
  // SplayTreeMap:自动按键排序(需要import 'dart:collection')
  // final sortedMap = SplayTreeMap<String, int>.from(scores);
}

默认值处理

dart
123456789101112
void main() {
  Map<String, List<int>> groups = {};
  
  // 方式1:putIfAbsent(推荐)
  groups.putIfAbsent('A', () => []).add(1);
  groups.putIfAbsent('A', () => []).add(2);  // 不会创建新列表
  
  // 方式2:扩展方法(需要collection_for)
  (groups['B'] ??= []).add(3);
  
  print(groups);  // {A: [1, 2], B: [3]}
}
操作方法
添加/修改[] =, putIfAbsent(), addAll(), update()
删除remove(), removeWhere(), clear()
访问[], containsKey(), containsValue()
获取集合.keys, .values, .entries
遍历forEach(), for-in
转换map(), Map.fromEntries()
合并{...m1, ...m2}
其他cast(), updateAll(), isEmpty, length

{% endhideToggle %}

dynamic(不要用)

dynamic 是 Dart 的一个特殊类型,表示禁用静态类型检查,可以在运行时持有任何类型的值,并可以调用任何方法(编译时不检查)

说人话就是完全不做类型检查,啥都能放

dynamic和var和object的区别:

dynamic------运时可自由改变类型,(无编译检查,方法和属性直接调用)

var-----根据初始值进行推断类型,确定类型后类型确定,(有编译检查,仅限推断的属性和方法)

Object------所有类型的父类,可接收任意类型,但访问成员需进行类型判断或强制转换,(有编译检查,不可直接调用具体类型的方法)

简而言之就是var和Object一旦确定类型就不可改,而dynamic随便改

dart
12345678910
void main() {
  dynamic a = "hello";
  print(a);

  a = 123;
  print(a);

  a = true;
  print(a);
}

由此可见这玩意太自由了,所以能不用就不用

late

late 是 Dart 的空安全特性,用于延迟初始化

它告诉编译器:"这个变量稍后一定会被赋值,现在先别检查它是否为空"

dart
123456
late String name;

void main() {
  name = "Tom"; // 后面再赋值
  print(name);
}

核心特点

  • 变量先声明
  • 稍后再赋值
  • 使用前必须赋值,否则报错

late final

这个等于late+ final就是不可更改的延迟初始化的类型,没什么好说的

dart
123456
late final String name;

void main() {
  name = "Tom";  // ✅ 第一次赋值
  name = "Jack"; // ❌ 报错
}

空安全机制(空安全运算符)

在dart中,默认情况下,变量不能是 null,除非你明确允许它为 null

操作符符号作用说明示例
可空类型?声明变量可以为 nullString? name
安全访问?.对象为 null 时跳过调用并返回 nulluser?.name
非空断言!.强制认为变量不为 null(否则运行时报错)name!.length
空合并??左侧为 null 时返回右侧默认值name ?? "Guest"
空值赋值??=仅当变量为 null 时才进行赋值name ??= "Tom"
级联安全访问?..对象为 null 时不执行后续级联操作user?..setName("Tom")
空展开...?集合为 null 时跳过展开[...?list]
dart
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
void main() {
  //可空类型(声明变量可以为null)
  String? name;

  name = null; // ✅ 可以
  name = "Tom"; // ✅ 也可以

  print(name);

  //安全访问(对象为 null 时跳过调用并返回 null)
  String? name_1;

  print(name_1?.length); // 输出null

  name_1 = "Tom";
  print(name_1?.length); // 输出3

  //非空断言(强制认为变量不为 null(否则运行时报错))
  String? name_2 = "Tom";

  print(name_2!.length); // ✅ 3

  String? a;
  //print(a!.length); // ❌ 运行时崩溃

  //空合并(左侧为 null 时返回右侧默认值)
  String? name_3;

  print(name_3 ?? "Guest"); // Guest

  name_3 = "Tom";
  print(name_3 ?? "Guest"); // Tom

  //空合并赋值(仅当变量为 null 时才进行赋值)
  String? name_4;

  name_4 ??= "Tom";
  print(name_4); // Tom

  name_4 ??= "Jack";
  print(name_4); // 还是 Tom(不会被覆盖)

  //级联安全访问(对象为 null 时不执行后续级联操作)
  User? user;

  user?..setName("Tom"); // ✅ 不会报错(因为 user 是 null)

  user = User();
  user?..setName("Tom");

  print(user.name); // Tom

  //空展开(集合为 null 时跳过展开)
  List<int>? list;

  var newList = [1, 2, ...?list];
  print(newList); // [1, 2]

  list = [3, 4];
  var newList2 = [1, 2, ...?list];
  print(newList2); // [1, 2, 3, 4]
}

class User {
  String name = "";

  void setName(String n) {
    name = n;
  }
}

运算符

算术运算符

运算符作用示例
+加法1 + 2
-减法5 - 3
*乘法2 * 3
/除法(结果是double)5 / 2 = 2.5
~/整除5 ~/ 2 = 2
%取余5 % 2 = 1

自增自减

运算符作用示例
++自增a++
--自减a--

关系运算符

运算符作用示例
==等于a == b
!=不等于a != b
>大于a > b
<小于a < b
>=大于等于a >= b
<=小于等于a <= b

赋值运算符

运算符作用示例
=赋值a = 10
+=加后赋值a += 2
-=减后赋值a -= 2
*=乘后赋值a *= 2
/=除后赋值a /= 2
~/=整除赋值a ~/= 2
%=取余赋值a %= 2

逻辑运算符

运算符名称含义示例
!非取反!true → false
&&与两边都为 truea && b
||或至少一边为 truea || b