我觉得我还是学学flutter吧,听说这个可以制作出跨平台应用,感觉挺新奇的
我还是继续制作我的终末地聊天APP吧,因为之前使用html js css这些制作的
当然,我还是先学学怎么做出好看的应用

下载完成后终端输入检测是否下载成功
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 |
❌ 不允许 | 不同类型,不能隐式转换 |
这里我们知道隐式转换和显示转换
对于隐式转换
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…()这种方法)
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声明常量
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 基础
void main() {
// 三种创建方式
String s1 = '单引号';
String s2 = "双引号";
String s3 = '''多行
字符串
支持换行''';
print(s1);
print(s2);
print(s3);
}
字符串插值
void main() {
String name = 'Dart';
int version = 3;
// $变量名
print('语言: $name');
// ${表达式}
print('版本: ${version + 1}');
print('大写: ${name.toUpperCase()}');
}
String常用方法
toString()方法将其他类型转换为字符串
void main() {
num a = 5555;
a.toString();
print(a);
}
List列表类型
这个和C#里的几乎一样,C#里面也有list,不过list和数组差不多,包括访问数组里的元素方法也是一样的
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
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}");
}
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(),
);
}
Map字典类型
这个和C#里Dictionary的一样
字典这个已经很熟悉了只不过这里的字典格式和列表差不多,反正和其他语言里的一样就是键值对
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
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); //{}
}
dynamic(不要用)
dynamic 是 Dart 的一个特殊类型,表示禁用静态类型检查,可以在运行时持有任何类型的值,并可以调用任何方法(编译时不检查)
说人话就是完全不做类型检查,啥都能放
dynamic和var和object的区别:
dynamic——运时可自由改变类型,(无编译检查,方法和属性直接调用)
var—–根据初始值进行推断类型,确定类型后类型确定,(有编译检查,仅限推断的属性和方法)
Object——所有类型的父类,可接收任意类型,但访问成员需进行类型判断或强制转换,(有编译检查,不可直接调用具体类型的方法)
简而言之就是var和Object一旦确定类型就不可改,而dynamic随便改
void main() {
dynamic a = "hello";
print(a);
a = 123;
print(a);
a = true;
print(a);
}
由此可见这玩意太自由了,所以能不用就不用
late
late 是 Dart 的空安全特性,用于延迟初始化
它告诉编译器:”这个变量稍后一定会被赋值,现在先别检查它是否为空”
late String name;
void main() {
name = "Tom"; // 后面再赋值
print(name);
}
核心特点
- 变量先声明
- 稍后再赋值
- 使用前必须赋值,否则报错
late final
这个等于late+ final就是不可更改的延迟初始化的类型,没什么好说的
late final String name;
void main() {
name = "Tom"; // ✅ 第一次赋值
name = "Jack"; // ❌ 报错
}
空安全机制(空安全运算符)
在dart中,默认情况下,变量不能是 null,除非你明确允许它为 null
| 操作符 | 符号 | 作用说明 | 示例 |
|---|---|---|---|
| 可空类型 | ? |
声明变量可以为 null |
String? name |
| 安全访问 | ?. |
对象为 null 时跳过调用并返回 null |
user?.name |
| 非空断言 | !. |
强制认为变量不为 null(否则运行时报错) |
name!.length |
| 空合并 | ?? |
左侧为 null 时返回右侧默认值 |
name ?? "Guest" |
| 空值赋值 | ??= |
仅当变量为 null 时才进行赋值 |
name ??= "Tom" |
| 级联安全访问 | ?.. |
对象为 null 时不执行后续级联操作 |
user?..setName("Tom") |
| 空展开 | ...? |
集合为 null 时跳过展开 |
[...?list] |
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 |
&& |
与 | 两边都为 true | a && b |
|| |
或 | 至少一边为 true | a || b |


评论区