变量
1. 定义变量
Dart 中定义变量有两种方式,一种是静态类型语言常用的方式,即指定变量类型;另一种则是动态语言的常用方式,即不指定类型,由 Dart 自动类型判断。建议在编写代码时,尽可能指定变量类型,这样可以提升代码可读性与调试的便利性。
// 指定变量类型:
String name = 'DrM';
// 使用关键字 var ,不指定变量类型:
var name = 'DrM';
//
`int? aNullableAge;`
未初始化的变量默认值是 null。即使变量是数字类型默认值也是 null,因为在 Dart 中一切都是对象,数字类型也不例外。
var a; // 也可使用 dynamic a;
print(a); // 输出值为 null
int b;
// Error: Non-nullable variable 'a' must be assigned before it can be used.
2.明确指定可为空的变量
可为空(dynamic、var 或者 ?声明)的变量,初始化可以为空;
int b;
// Error: Non-nullable variable 'a' must be assigned before it can be used.
int ?c;
print(c); // 输出值为 null
3.动态改变变量类型
如想动态改变变量的数据类型,应当使用 dynamic 或 Object 来定义变量:
dynamic a = 'DrM'; // 也可使用 Object a = 'DrM';
a = 2022;
print(a); // 输出结果为 2022
4. 定义常量
Dart 中定义常量也有两种方式,一种使用 final 关键字,另一种是使用 const 关键字。需要注意的是, final 定义的常量是运行时常量,而 const 常量则是编译时常量,也就是说 final 定义常量时,其值可以是一个变量,而 const 定义的常量,其值必须是一个字面常量值。
final a = DateTime.now();
print(a); // 输出现在的时间
const a = DateTime.now();
print(a); // 报错
内建类型
Dart 语言支持以下内建类型:
graph LR
内建类型 --> Number
Number --> int --> 整数
Number --> double --> 浮点数
内建类型 --> String --> 字符串
内建类型 --> Boolean --> 布尔类型
内建类型 --> List --> 数组
内建类型 --> Map --> 字典或映射
内建类型 --> Set --> 集合
1. Number
// int 必须是整型:
int a = 123;
// 从 Dart 2.0 开始,double 既可以是浮点数也可以是整型:
double b = 12.34;
double c = 6; // 相当于 double c = 6.0
2. String
// 用单引号或者双引号定义字符串:
String s1 = 'DrM'
String s2 = "DrM"
// 当字符串有引号时:
String s3 = 'I\'m DrM'
String s4 = "I'm DrM"
// 使用连续三个单引号或者三个双引号实现多行字符串对象的创建:
String s5 = '''
Hello Dart!
Hello Flutter!
Hello world!
''';
/*
类似于js的字符串模板``
let s5 = `
Hello Dart!
Hello Flutter!
Hello world!
`;
*/
// 使用 r 前缀,可以创建 “原始 raw” 字符串:
String s6 = r'Hello Dart \( ̄▽ ̄)/ Hello Flutter!'
// String -> int:必须是整型
int a = int.parse('123');
// 123
// String -> double:
double b = double.parse('12.34');
// 12.34
// int -> String:
String str1 = 123.toString();
//'123'
// double -> String:
String str2 = 12.3456.toString();
//'12.3456'
// 字符串拼接,用“+”拼接,字面量字符串也可以直接写在一起:
String s1 = 'Hello Dart! ' 'Hello Flutter! ' 'Hello ImportV! ';
String s2 = 'Hello Dart! ' + 'Hello Flutter! ' + 'Hello ImportV! ';
//s1==s2
// 字符串可以通过 ${expression} 的方式内嵌表达式。如果表达式是一个标识符并且后方没有紧跟字面量,则{}可以省略:
String s1 = 'Hello Flutter!';
String s2 = 'Hello Dart! ' + s1 + 'Hello ImportV!';
String s3 = 'Hello Dart! ${s1} Hello ImportV!';
String s4 = 'Hello Dart! $s1 Hello ImportV!';
//s2==s3==s4
// String toStringAsFixed(int fractionDigits):
//该方法是将Number转为String的一个方法,接受int类型作为参数来控制小数点后的精度
//官网说只能在double类型上使用该方法,但经过测试int类型也可以
1.toStringAsFixed(3); // 1.000
4321.12345678.toStringAsFixed(3); // 4321.123
4321.12345678.toStringAsFixed(5); // 4321.12346
123456789012345.toStringAsFixed(3); // 123456789012345.000
10000000000000000.toStringAsFixed(4); // 10000000000000000.0000
5.25.toStringAsFixed(0); // 5
常见方法
//indexOf 同List
//replaceFirst
'0.0001'.replaceFirst('0', ''); // .0001
//replaceAll
'0.0001'.replaceFirst('0', ''); // .1
3. Boolean
// 布尔类型默认值为null:
bool b = true;
4. List
具有长度的可索引对象集合
// 定义列表方法一:
List lt1 = [1, 2, 3, 4];
// 定义列表方法二:
List a = [];
a.add(1);
a.add(2);
a.add(3);
// 获取列表属性:
List a = ['Dart', 'Flutter', 'ImportV'];
print(a.length); // 获取列表长度
print(a.isEmpty); // 判断列表是否为空,如果是,输出 true
print(a.isNotEmpty); // 判断列表是否为空,如果不是,输出 true
print(a.reversed); // 将列表逆序输出(翻转列表)
// 列表的下标索引是从0开始的:
List a = [1, 2, 3, 4];
print(a[0]); // 1
// 列表转字符串:
List a = ['Dart', 'Flutter', 'Android'];
String b = a.join('->'); // 此处 b 的字面量为 Dart->Flutter->Android
// 字符串转列表:
String a = 'Dart->Flutter->Android';
List b = a.split('->'); // 此处 b 的字面量为 [Dart, Flutter, Android]
// 指定列表类型:
List a = <String>[];
a.add('Flutter');
a.add(1); //报错
// 在列表字面量前添加 const 关键字,定义一个不可改变的列表:
List a = const [1, 2, 3, 4];
a[0] = 2; // 报错
常见方法
1.增加
List a = ['Dart', 'Flutter'];
a.add('Android'); // 拼接一个元素
a.addAll(['Android', 'iOS']); // 拼接多个元素
a.insert(2, 'Android'); // 插入一个元素,2 为插入位置的索引值
a.insertAll(2, ['Android', 'iOS']); // 插入多个元素
2.删除
List a = ['Dart', 'Flutter'];
/*remove(value) 从此列表中删除第一次出现的,如果在列表中,则返回 true ,否则返回 false。
不在列表中,则该方法无效。*/
print(a.remove('Dart')); //true
//removeAt(int index) 从此列表中删除位置处的对象并返回删除的对象,必须在范围内0 ≤ index < length
print(a.removeAt(0)); //Dart
//removeLast() 删除并返回此列表中的最后一个对象。
print(a.removeLast()); //Flutter
//void removeRange(int start,int end) 删除`start`包含到`end`排除范围内的对象,0 <= start <= end <= length
a.removeRange(); //没有返回值
//void removeWhere(bool Function) 从此列表中删除所有满足的对象。
a.removeWhere(item=>item=='Flutter'); //没有返回值
//void clear() 从此列表中删除所有对象;列表的长度变为零。
a.clear(); //没有返回值
3.替换
List a = ['Dart', 'Flutter'];
//void replaceRange (int start,int end,Iterable<E> replacement)
a.replaceRange(0,1,['DrM']); //没有返回值
print(a); //[DrM, Flutter]
4.查找
List a = ['Dart', 'Flutter'];
//indexOf(E element, int start = 0)在索引start到列表末尾之间搜索,返回查找到的第一个索引
print(a.indexOf('Dart')); //0
print(a.indexOf('Dart',1)); //-1
//indexWhere(bool Function, int start = 0)返回列表中满足条件的第一个索引
a.indexWhere((item) => item.startsWith('D')); // 0
a.indexWhere((item) => item.startsWith('D'), 1); // -1
5. Set
Set 是没有顺序且元素不能重复的集合,因此不能通过索引去获取值。
// 定义集合方法一:
Set a = {'Dart', 'Flutter', 'ImportV'};
// 定义集合方法二
Set a = {};
a.add('Dart');
a.add('Flutter');
a.add('ImportV');
// 创建空集合:
Set a = {}; // 方法一
var a = <String>{}; // 方法二
var a = {}; // 这样会创建一个空的 Map,而不是 Set
// 获取集合中元素的个数:
Set a = {'Dart', 'Flutter', 'ImportV'};
print(a.length); // 输出为 3
// 创建一个不可改变的集合:
Set a = const {'Dart', 'Flutter', 'ImportV'};
a.add('Android'); // 报错
// 列表转集合:
List a = ['Dart', 'Flutter', 'Android'];
print(a.toSet());
// 集合转列表:
Set a = {'Dart', 'Flutter', 'Android'};
print(a.toList());
因为Set是不能重复的集合,所以可以用Set去重;
List a = [1,2,3,1,4,2].toSet().toList();
print(a); // [1, 2, 3, 4]
6. Map
Map(映射)是用来关联 keys 和 values 的对象。 keys 和 values 可以是任何类型的对象。在一个 Map 对象中一个 key 只能出现一次。
// 定义映射方法一:
Map a = {
// key value
'first': 'Dart',
'second': 'Flutter',
'third': 'Android'
};
// 定义映射方法二:
Map a = {};
a['first'] = 'Dart';
a['second'] = 'Flutter';
a['third'] = 'Android';
// 获取映射长度:
print(a.length);
// 获取指定 value 值:
print(a['first']);
// 在映射字面量前添加 const 关键字,定义一个不可改变的映射:
Map a = const {'first': 'Dart', 'second': 'Flutter', 'third': 'Android'};
a['third'] = 'iOS' //报错
常见方法
1.新增一个键值对
Map<String, int> map8 = Map();
map8['a8'] = 1;
print(map8); //{a8: 1}
2.修改一个键值对
Map<String, int> map9 = {'a9': 1, 'b9': 2};
map9['a9'] = 9;
print(map9); //{a9: 9, b9: 2}
3.update(K key, V update(V value), {V ifAbsent()}) 根据指定的Key对应的value做出修改,同时Map本身也会被修改
Map<String, int> map10 = {'a10': 1, 'b10': 2, 'c10': 3};
var resultMap10 = map10.update('b10', (value) => value * 2);
print(resultMap10); //4
print(map10); //{a10: 1, b10: 4, c10: 3}
var resultMap101 = map10.update('c', (value) => (value * 2),
ifAbsent: () => (10)); //如果key不存在,但是有ifAbsent参数,返回idAbsent函数的值,并添加到map中
print('$resultMap101,${resultMap101.runtimeType}'); //10,int
print(map10); //{a10: 1, b10: 4, c10: 3, c: 10}
4.remove() 删除一个key
Map<String, int> map12 = {'a12': 2, "b12": 1};
map12.remove('a12');
print(map12); //{b12: 1}
map12.remove('c12'); //删除一个不存在的key,毫无影响,无报错无警告
print(map12); //{b12: 1}
5.removeWhere(bool predicate(K key, V value)) 根据函数条件批量删除key
Map<String, int> map13 = {'a13': 3, 'b13': 4, 'c13': 1};
map13.removeWhere((key, value) => value > 3);
print(map13); //{a13: 3, c13: 1}
6.containsKey() 是否包含某个key contrainsValue()是否包含某个value
Map<String ,int> map14 = {'a14':1};
bool resultMap14 = map14.containsKey('a11'); //false
bool resultMap141 = map14.containsValue(1); //true
7.forEach(void f(K key, V value)) 遍历Map ,遍历时不可add或者remove
Map<String, int> map15 = {'a15': 1, 'b15': 2, 'c': 3, 'd': 4, 'e': 5};
map15.forEach((key, value) {
print('$key,$value');
/*
a15,1
b15,2
c,3
d,4
e,5
*/
});
map15.forEach((key, value) {
map15['a15'] = 8;
});
print(map15); //{a15: 8, b15: 2, c: 3, d: 4, e: 5}
map15.forEach((key, value) {
map15.addAll({'ccc':22});
});
//Uncaught Error: Concurrent modification during iteration: Instance of 'JsLinkedHashMap<String, int>'.
8.map() 遍历每个键值对 根据参数函数,对keyvalue做出修改,转换成其他泛型Map
Map<String,int> map16 = {'a16':7,"b16":5,'c16':4};
Map<int,String> map17 = map16.map((key, value) {
return MapEntry(value, key);
});
print(map17);
9.addAll() 两个Map合并,类型需要一致 ,且如果key相同,则会覆盖value
Map<String,int> map18 = {'a18':1,'b18':7,'a19':2};
Map<String,int> map19 = {'a19':9};
map18.addAll(map19);
print(map18); //{a18: 1, b18: 7, a19: 9}
10.addEntries(key,value) 两个Map合并,类型需要一致 ,且如果key相同,则会覆盖value
Map<String,int> map20 = {'a20':2,'b20':3};
Map<String,int> map21 = {'a21':5,'b21':9};
map20.addEntries(map21.entries);
print(map20); //{a20: 2, b20: 3, a21: 5, b21: 9}
11.putIfAbsent() 存在key则返回value,查不到则返回值 不修改Map
Map<String,int> map22 = {'a22':3,'b22':4};
var resultMap22 = map22.putIfAbsent('a22', () => 2); //存在key则返回value,查不到则返回 2 不修改Map
print('$resultMap22,$map22');//3,{a22: 3, b22: 4}
var resultMap221 = map2.putIfAbsent('a2', () => 1);
print('$resultMap221,$map22'); //1,{a22: 3, b22: 4} //存在key则返回value,查不到则返回 1 不修改Map
12.清除所有键值对
Map<String,int> map25 = {'a25':2,'b25':3};
map25.clear();
print('$map25,${map25.runtimeType}'); //{},_InternalLinkedHashMap<String, int>
运算符
下面是 Dart 定义的运算符:
graph LR
运算符 --> 算数运算符
算数运算符 --> A["+ - * / 加减乘除"]
算数运算符 --> B["~/ 除法,返回整数值"]
算数运算符 --> C["% 返回除法余数值"]
运算符 --> 关系运算符
关系运算符 --> 2A["== !="]
关系运算符 --> 2B["> <"]
关系运算符 --> 2C[">= <="]
运算符 --> 类型判断运算符
类型判断运算符 -->8A["as 用于类型转换"]
类型判断运算符 -->8B["is 如果对象是指定的类型就返回true"]
类型判断运算符 -->8C["is! 如果对象不是指定的类型就返回true"]
运算符 --> 赋值运算符 --> 3A["= -= /= %= >>= ^= += *= ~/= <<= &= |="]
运算符 --> 逻辑运算符 --> 4A["&& || ! 与或非"]
运算符 --> 位运算符
位运算符 --> 5A["& | ^ 按位与 按位或 按位异或"]
位运算符 --> 5B["~ << >> 按位取反 左移动 右移动"]
运算符 --> 条件表达式
条件表达式 --> 6A["condition ? expr1 : expr2 三目运算"]
条件表达式 --> 6B["expr1 ?? expr2"]
运算符 --> 级联运算符 --> 7A["级联运算符(..)严格来讲不是一个运算符﹐而是一个Dart的特殊语法
"]
1. 算数运算符
除了基本的算术运算外,Dart 还支持前缀和后缀、自增和自减运算符:
var a, b;
a = 0;
b = ++a; // a自加1后赋值给b
b = a++; // a赋值给b后自加1
b = --a; // a自减1后赋值给b
b = a--; // a赋值给b后自减1
2. 类型判断运算符
dart也是通过 is 关键字来对类型进行检查以及使用 as 关键字对类型进行强制转换,如果判断不是某个类型dart中使用 is!:
int number = 123;
double distance = 123.4;
num age = 12;
print(number is num);//true
print(distance is! int);//true
print(age as int);//12
print(distance as int);
//Uncaught Error: TypeError: 123.4: type 'JSNumNotInt' is not a subtype of type 'int'
3. 赋值运算符
下面示例使用几个赋值和复合赋值运算符,其他使用方法类似:
a = 123; // 使用 = 直接为变量赋值。
b ??= 456; // 使用 ??= 运算符时,只有当 b 值为 null 时才会被赋值
a *= 3; // 赋值并做乘法运算,相当于 a = a * 3
4. 逻辑运算符
下面是关于逻辑表达式的示例:
if (!list && (count == 0 || count == 3)) {
// ...Do something...
}
5. 位运算符
位运算符把数字转为二进制来运算:
int a = 12
int b = 6
// 按位与,参与运算的两个值,如果两个相应二进位都为 1,则该位结果为 1,否则为 0
print(a & b); // 结果为 4
// 按位或,只要对应的两个二进位有一个为 1,结果位就为 1
print(a | b); // 结果为 14
// 按位异或,当两个对应的二进位相异时,结果位为 1,否则为 0
print(a ^ b); // 结果为 10
// 按位取反,对数据的每一个二进位,把 1 变为 0,把 0 变为 1
print(~a); // 结果我为 -13
// 左移,把 << 左边的数的各二进位左移若干位,由 << 右边的数指定移动位数,高位丢弃,低位补 0
print(a << 2); // 结果为 48,相当于乘以 2 的 2 次方
// 右移,把 << 左边的数的各二进位右移若干位,由 << 右边的数指定移动位数
print(a >> 2); // 结果为 3,相当于除以 2 的 2 次方
6. 条件表达式
Dart有两个运算符,有时可以替换 if-else 表达式, 让表达式更简洁,称为条件表达式:
condition ? expr1 : expr2
如果条件为 true, 执行 expr1 (并返回它的值): 否则, 执行并返回 expr2 的值。
expr1 ?? expr2
如果 expr1 不是 null, 返回 expr1 的值; 否则, 执行并返回 expr2 的值。
如果条件判断是根据布尔值, 考虑使用第一种,如果是根据是否为 null,考虑使用第二种。
var a = 'Dart';
var b = 'Flutter';
var c = a ?? b.toUpperCase(); // 此处 c 的字面量为 Dart
7. 级联运算符
级联运算符 .. 可以实现对同一个对像进行一系列的操作。 除了调用函数外, 还可以访问同一对象上的字段属性。 这通常可以节省创建临时变量的步骤, 同时编写出更流畅的代码。
querySelector('#confirm') // 获取对象。
..text = 'Confirm' // 调用成员变量。
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
第一句调用函数 querySelector() ,返回获取到的对象。 获取的对象依次执行级联运算符后面的代码, 代码执行后的返回值会被忽略。
上面的代码等价于:
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
级联运算符是可以嵌套的。但要注意,在返回对象的函数中谨慎使用级联操作符。 例如,下面的代码是错误的:
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // sb.write() 函数调用返回 void, 不能在 void 对象上创建级联操作。
流程控制语句
Dart 中的流程控制语句与 Java 相似。
1. if - else 分支
和 JavaScript 不同, Dart 的判断条件必须是布尔值,不能是其他类型。
int a = 1;
if (a < 0) {
a++;
} else if (a > 0) {
a--;
}
2. switch - case 语句
在 Dart 中 switch 语句使用 == 比较整数、字符串、编译时常量或者枚举类型。 比较的对象必须都是同一个类的实例(并且不可以是子类), 类必须没有对 == 重写。
在 case 语句中,每个非空的 case 语句结尾需要跟一个 break 语句。 除 break 以外,还有可以使用 continue, throw 或者 return。当没有 case 语句匹配时,执行 default 代码:
var a = 'yes';
switch (a) {
case 'yes':
print('yes');
break;
case 'no':
print('no');
break;
default:
print('fault');
}
3. for 循环
进行迭代操作,可以使用标准 for 语句。 例如:
// 计算 5 的阶乘
int result = 1;
for (int i = 1; i < 6; i++) {
result = result * i;
}
闭包在 Dart 的 for 循环中会捕获循环的初始索引值, 来避免 JavaScript 中常见的陷阱。下面的代码输出的是 0 和 1,但是在 JavaScript 中会连续输出两个 2 :
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());
4. while 和 do while 循环
while 循环是在执行前判断执行条件,do-while 循环是在执行后判断执行条件:
// 计算 5 的阶乘
int i = 1;
int result = 1;
// 使用 while 循环
while (i < 6) {
result = result * i;
i++;
}
// 使用 do - while 循环
do {
result = result * i;
i++;
} while (i < 6);
5. break 和 continue
使用 break 停止程序循环:
for (int i = 1; i < 5; i++) {
if (i == 3) {
break;
}
print(i); // 输出结果为 1 2(i = 3 时循环结束)
}
使用 continue 跳转到下一次循环:
for (int i = 1; i < 5; i++) {
if (i == 3) {
continue;
}
print(i); // 输出结果为 1 2 4(i = 3 时跳到下一次循环)
}
6. 其他循环
使用 for...in... 循环:
// 遍历数组
List a = ['Dart', 'Flutter', 'Android'];
for (var item in a) {
print(item);
}
使用 forEach 循环:
// 遍历数组
List a = ['Dart', 'Flutter', 'Android'];
a.forEach((var item) {
print(item);
});