我觉得我还是学学flutter吧,听说这个可以制作出跨平台应用,感觉挺新奇的 我还是继续制作我的终末地聊天APP吧,因为之前使用html js css这些制作的
当然,我还是先学学怎么做出好看的应用
先下载dartsdk
下载完成后终端输入检测是否下载成功
我习惯用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
❌ 不允许
不同类型,不能隐式转换
这里我们知道隐式转换和显示转换
对于隐式转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void main() { int i = 10 ; double d = 3.14 ; num n1 = i; print ('n1: $n1 , 类型: ${n1.runtimeType} ' ); num n2 = d; print ('n2: $n2 , 类型: ${n2.runtimeType} ' ); num n3 = 100 ; n3 = 2.718 ; print ('n3: $n3 ' ); }
对于显示转换(其实就是调用方法比如to…()这种方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void main() { num n = 10 ; int i = n.toInt(); print ('i: $i ' ); double d = n.toDouble(); print ('d: $d ' ); int i2 = 10 ; double d2 = i2.toDouble(); print ('d2: $d2 ' ); double d3 = 3.99 ; int i3 = d3.toInt(); print ('i3: $i3 ' ); int i4 = d3.round(); print ('i4: $i4 ' ); }
final 当需要存储一个不变的数据,但是在运时才确定,需要使final声明常量
编译时常量:当需要存储一个不变的数据,且在编译时就确定,需要使const声明常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Circle { 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} ' ); }
这个也是和const常量一样赋值之后不可以再次修改
String String 基础 1 2 3 4 5 6 7 8 9 10 11 12 void main() { String s1 = '单引号' ; String s2 = "双引号" ; String s3 = '''多行 字符串 支持换行''' ; print (s1); print (s2); print (s3); }
字符串插值 1 2 3 4 5 6 7 8 9 10 11 void main() { String name = 'Dart' ; int version = 3 ; print ('语言: $name ' ); print ('版本: ${version + 1 } ' ); print ('大写: ${name.toUpperCase()} ' ); }
String常用方法 toString()方法将其他类型转换为字符串
1 2 3 4 5 void main() { num a = 5555 ; a.toString(); print (a); }
其他方法
一、长度 / 判空
方法
作用
示例
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()
List列表类型 这个和C#里的几乎一样,C#里面也有list,不过list和数组差不多,包括访问数组里的元素方法也是一样的
1 2 3 4 5 6 7 8 9 void main() { List <int > numbers = [1 , 2 , 3 ]; var fruits = <String >['apple' , 'banana' ]; List empty = List .empty(); List growable = List .empty(growable: true ); print (numbers); }
常用的方法:
在尾部添加————add(内容)
在尾部添加一个列表————addAll (列表)
删除满足内容的第一个———-remove(内容)
删除最后一个———–removeLast()
删除索引范围内数据———-removeRange(start,end)
循环————-forEach((item) {});
是否都满足条件—————every((item){return 布尔值 });
筛选出满足条件的数据—————where((item){return 布尔值 });
列表的长度(属性)-length
最后一个元素(属性)————–last
第一个元素(属性)-first是否为空(属性)———–isEmpty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 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); 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} " ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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(), ); }
具体例子
访问与修改 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void main() { List <String > colors = ['red' , 'green' , 'blue' ]; print (colors[0 ]); print (colors.first); print (colors.last); print (colors.length); colors[1 ] = 'yellow' ; print (colors); print (colors.elementAtOrNull(5 )); }
添加元素 1 2 3 4 5 6 7 8 9 10 11 12 13 void main() { List <int > list = []; list.add(1 ); list.addAll([2 , 3 ]); list.insert(0 , 0 ); list.insertAll(2 , [99 , 100 ]); print (list); }
删除元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void main() { List <String > items = ['a' , 'b' , 'c' , 'b' , 'd' ]; items.remove('b' ); items.removeAt(0 ); items.removeLast(); items.removeWhere((e) => e == 'b' ); items.clear(); }
查询与查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void main() { List <int > numbers = [10 , 20 , 30 , 40 , 50 ]; print (numbers.contains(20 )); print (numbers.indexOf(30 )); print (numbers.lastIndexOf(10 )); int first = numbers.firstWhere((n) => n > 25 ); int? safe = numbers.firstWhere((n) => n > 100 , orElse: () => -1 ); int idx = numbers.indexWhere((n) => n > 25 ); print ('first: $first , idx: $idx ' ); }
切片与范围 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void main() { List <String > list = ['a' , 'b' , 'c' , 'd' , 'e' ]; List <String > sub = list.sublist(1 , 4 ); list.replaceRange(1 , 3 , ['X' , 'Y' ]); list.fillRange(0 , 2 , 'Z' ); print (list); }
排序与反转 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void main() { List <int > nums = [3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ]; nums.sort(); nums.sort((a, b) => b - a); List <String > fruits = ['banana' , 'apple' , 'pie' ]; fruits.sort((a, b) => a.length.compareTo(b.length)); var reversed = nums.reversed.toList(); nums = [1 , 2 , 3 ]; }
映射与过滤 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void main() { List <int > numbers = [1 , 2 , 3 , 4 , 5 ]; List <String > strings = numbers.map((n) => 'No.$n ' ).toList(); print (strings); List <int > evens = numbers.where((n) => n.isEven).toList(); print (evens); List <List <int >> nested = [[1 , 2 ], [3 , 4 ]]; List <int > flat = nested.expand((e) => e).toList(); print (flat); var result = numbers .where((n) => n > 2 ) .map((n) => n * 2 ) .toList(); print (result); }
聚合操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void main() { List <int > scores = [85 , 92 , 78 , 90 , 88 ]; scores.forEach((s) => print (s)); int sum = scores.reduce((a, b) => a + b); int total = scores.fold(0 , (acc, s) => acc + s); bool allPass = scores.every((s) => s >= 60 ); bool hasExcellent = scores.any((s) => s >= 90 ); int max = scores.reduce((a, b) => a > b ? a : b); int min = scores.reduce((a, b) => a < b ? a : b); print ('总分: $total , 最高分: $max ' ); }
固定长度 vs 可增长 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void main() { List <int > growable = [1 , 2 , 3 ]; growable.add(4 ); growable.length = 10 ; List <int > fixed = List .filled(3 , 0 ); fixed[0 ] = 10 ; var fixed2 = List .unmodifiable(growable); print ('growable: $growable ' ); print ('fixed: $fixed ' ); }
展开运算符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void main() { List <int > list1 = [1 , 2 , 3 ]; List <int > list2 = [4 , 5 , 6 ]; List <int > combined = [...list1, ...list2]; bool showExtra = true ; List <int > conditional = [ 1 , 2 , if (showExtra) ...[3 , 4 , 5 ], ]; List <String > repeated = [ for (var i in [1 , 2 , 3 ]) 'item-$i ' , ]; 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()
Map字典类型 字典这个已经很熟悉了只不过这里的字典格式和列表差不多,反正和其他语言里的一样就是键值对
1 2 3 4 5 6 7 8 9 10 11 12 void main() { Map <String , int > scores = {'Alice' : 90 , 'Bob' : 85 }; var ages = <String , int >{'Tom' : 20 , 'Jerry' : 22 }; Map empty = {}; Map <String , String > config = Map (); Map <int , String > numbers = Map .from({1 : 'one' , 2 : 'two' }); print (scores); }
循环———forEach
在添加一个字典————-addAll
是否包含某个key————containsKey
删除某个key———-remove
清空———clear
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 void main() { Map <String , String > player = {'女儿' : '阿尼亚' , '母亲' : '约尔' , '父亲' : '劳埃德' }; print (player); player.forEach((key, value) { print ('$key : $value ' ); }); Map <String , String > player2 = {'同学' : '达米安' }; player.addAll(player2); print (player); print (player.containsKey('女儿' )); print (player.containsKey('儿子' )); player.remove('同学' ); print (player); player.clear(); print (player); }
具体例子
添加与修改 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void main() { Map <String , int > map = {}; map['a' ] = 1 ; map.putIfAbsent('b' , () => 2 ); map.addAll({'c' : 3 , 'd' : 4 }); map['a' ] = 100 ; map.update('a' , (v) => v * 2 ); map.update('z' , (v) => v * 2 , ifAbsent: () => 0 ); print (map); }
删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void main() { Map <String , int > map = {'a' : 1 , 'b' : 2 , 'c' : 3 , 'd' : 4 }; map.remove('a' ); map.removeWhere((k, v) => v.isEven); print (map); }
访问与查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void main() { Map <String , int > map = {'Alice' : 90 , 'Bob' : 85 , 'Carol' : 95 }; print (map['Alice' ]); print (map['Unknown' ]); int? score = map['Unknown' ]; int safe = map['Unknown' ] ?? 0 ; print (map.containsKey('Alice' )); print (map.containsValue(100 )); print (map.keys); print (map.values); print (map.entries); map.forEach((k, v) => print ('$k : $v ' )); }
遍历方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void main() { Map <String , int > map = {'a' : 1 , 'b' : 2 , 'c' : 3 }; map.forEach((key, value) { print ('$key = $value ' ); }); for (var entry in map.entries) { print ('${entry.key} : ${entry.value} ' ); } for (var MapEntry(:key, :value) in map.entries) { print ('$key -> $value ' ); } for (var k in map.keys) print (k); for (var v in map.values) print (v); }
转换与映射 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void main() { Map <String , int > scores = {'Alice' : 90 , 'Bob' : 85 , 'Carol' : 95 }; Map <String , String > grades = scores.map((k, v) => MapEntry(k, v >= 90 ? 'A' : 'B' ) ); print (grades); Map <String , int > excellent = Map .fromEntries( scores.entries.where((e) => e.value >= 90 ) ); print (excellent); Map <int , String > reversed = Map .fromEntries( scores.entries.map((e) => MapEntry(e.value, e.key)) ); print (reversed); }
合并与操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void main() { Map <String , int > m1 = {'a' : 1 , 'b' : 2 }; Map <String , int > m2 = {'b' : 20 , 'c' : 3 }; Map <String , int > combined = {...m1, ...m2}; print (combined); Map <String , int > merged = {}; merged.addAll(m1); m2.forEach((k, v) { merged[k] = (merged[k] ?? 0 ) + v; }); print (merged); bool useM2 = true ; Map <String , int > conditional = { ...m1, if (useM2) ...m2, }; }
常用构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void main() { Map <String , int > lazy = Map .fromIterable( ['a' , 'b' , 'c' ], key: (item) => item, value: (item) => item.codeUnitAt(0 ), ); print (lazy); 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); Map <String , int > readonly = Map .unmodifiable({'a' : 1 }); }
排序与排序后的Map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void main() { Map <String , int > scores = {'Bob' : 85 , 'Alice' : 90 , 'Carol' : 95 }; var sortedByKey = Map .fromEntries( scores.entries.toList()..sort((a, b) => a.key.compareTo(b.key)) ); print (sortedByKey); var sortedByValue = Map .fromEntries( scores.entries.toList()..sort((a, b) => a.value.compareTo(b.value)) ); print (sortedByValue); }
默认值处理 1 2 3 4 5 6 7 8 9 10 11 12 void main() { Map <String , List <int >> groups = {}; groups.putIfAbsent('A' , () => []).add(1 ); groups.putIfAbsent('A' , () => []).add(2 ); (groups['B' ] ??= []).add(3 ); print (groups); }
操作
方法
添加/修改
[] =, putIfAbsent(), addAll(), update()
删除
remove(), removeWhere(), clear()
访问
[], containsKey(), containsValue()
获取集合
.keys, .values, .entries
遍历
forEach(), for-in
转换
map(), Map.fromEntries()
合并
{...m1, ...m2}
其他
cast(), updateAll(), isEmpty, length
dynamic(不要用) dynamic 是 Dart 的一个特殊类型,表示禁用静态类型检查 ,可以在运行时持有任何类型的值,并可以调用任何方法(编译时不检查)
说人话就是完全不做类型检查,啥都能放
dynamic和var和object的区别:
dynamic——运时可自由改变类型,(无编译检查,方法和属性直接调用)
var—–根据初始值进行推断类型,确定类型后类型确定,(有编译检查,仅限推断的属性和方法)
Object——所有类型的父类,可接收任意类型,但访问成员需进行类型判断或强制转换,(有编译检查,不可直接调用具体类型的方法)
简而言之就是var和Object一旦确定类型就不可改,而dynamic随便改
1 2 3 4 5 6 7 8 9 10 void main() { dynamic a = "hello" ; print (a); a = 123 ; print (a); a = true ; print (a); }
由此可见这玩意太自由了,所以能不用就不用
late late 是 Dart 的空安全特性,用于延迟初始化
它告诉编译器:”这个变量稍后一定会被赋值,现在先别检查它是否为空”
1 2 3 4 5 6 late String name;void main() { name = "Tom" ; print (name); }
核心特点
late final 这个等于late+ final就是不可更改的延迟初始化的类型,没什么好说的
1 2 3 4 5 6 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]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 void main() { String? name; name = null ; name = "Tom" ; print (name); String? name_1; print (name_1?.length); name_1 = "Tom" ; print (name_1?.length); String? name_2 = "Tom" ; print (name_2!.length); String? a; String? name_3; print (name_3 ?? "Guest" ); name_3 = "Tom" ; print (name_3 ?? "Guest" ); String? name_4; name_4 ??= "Tom" ; print (name_4); name_4 ??= "Jack" ; print (name_4); User? user; user?..setName("Tom" ); user = User(); user?..setName("Tom" ); print (user.name); List <int >? list; var newList = [1 , 2 , ...?list]; print (newList); list = [3 , 4 ]; var newList2 = [1 , 2 , ...?list]; print (newList2); } 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
评论区