基础类型
Number类型
Number
中包含了两个类型int
和double
。分别代表整数类型和双精度浮点类型。
- int
int
类型在不同的环境中长度并不一样,在DartVM上,int的范围是-2^63 至 2^63 - 1
,而在web中,int的范围是-2^53 至 2^53 - 1
定义int
型变量可以通过以下几种方式,其中通过var
来定义变量,可以不需要指定类型,编译器可根据字面量进行推断。
int a = 1;
int b = 0xDEFF0;
var c = 2;
注意:通过
int
类型来定义变量时,如果定义的是全局变量一定要赋值(或声明late),否则编译器会报错。如果是定义局部变量,则在使用前必须赋值,否则使用时会抛出异常。这是因为在dart的空安全机制的限制。
- double
double
64位双精度浮点数字。
定义double
变量,可通过以下方式,如果显示的申明变量类型是double
,字面量为整形时,必要时会自动转换成double
型
var a = 1.0;
double b = 2.0;
double c = 1;
其他
Number
类型的变量都支持基本的运算符+ - * /
等,还有ceil()
abs()
floor()
等方法。整型支持传统的位移操作,比如移位(<<
、>>
和 >>>
)、补码 (~
)、按位与 (&
)、按位或 (|
) 以及按位异或 (^
)。
print((3 << 1) == 6); // true; 0011 << 1 == 0110
print((3 | 4) == 7); // true; 0011 | 0100 == 0111
print((3 & 4) == 0); // true; 0011 & 0100 == 0000
String
Dart中的字符串包含了 UTF-16
编码的字符序列??梢允褂玫ヒ呕蛘咚爬创唇ㄗ址?/p>
var str1 = '我是一个字符串';
var str2 = "我也是一个字符串"
'
与"
的区别:在'
中需要使用\
来转义那些与单引号冲突的字符:
var str1 = 'it\'s easy to escape the string delimiter';
模版字符串
在字符串中,请以 ${表达式}
的形式使用表达式,如果表达式是一个标识符,可以省略掉 {}
。如果表达式的结果为一个对象,则 Dart 会调用该对象的 toString
方法来获取一个字符串。
var s = 'string interpolation';
print('Dart has $s, which is very handy.' ==
'Dart has string interpolation, '
'which is very handy.'); // true
print('That deserves all caps. '
'${s.toUpperCase()} is very handy!' ==
'That deserves all caps. '
'STRING INTERPOLATION is very handy!'); // true
字符串字面量是一个编译时常量,只要是编译时常量 (null、数字、字符串、布尔) 都可以作为字符串字面量的插值表达式:
// 这里定义为常量是可行的
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// 非常量的字符串是不被允许使用在定义字符串的模版内
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];
const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';
字符串拼接
我们可以使用+
号或者并列放置在多行的字符串来拼接字符串。
var str1 = 'the + operate' + ' works';
var str2 = 'String '
'concatenation'
" works even over line breaks.";
多行字符串
使用三个'
或者三个"
来定义多行字符串
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
raw字符串
通过在字符串前加上r
做前缀可创建raw字符串,即字符串不会被做任何处理(如转义)。
var s = r'In a raw string, not even \n gets special treatment.';
print(s); // In a raw string, not even \n gets special treatment.
String与Number类型转换
下面是字符串与数字类型的转换方式:
// String -> int
var one = int.parse('1');
print(one == 1); // true
// String -> double
var onePointOne = double.parse('1.1');
print(onePointOne == 1.1); // true
// int -> String
String oneAsString = 1.toString();
print(oneAsString == '1'); //true
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
print(piAsString == '3.14'); //true
布尔型
Dart 使用 bool
关键字表示布尔类型,布尔类型只有两个对象 true
和 false
,两者都是编译时常量
Dart的类型安全检查不允许出现一个非bool型的判断,例如if(0)
assert(1)
;
List
Dart使用 bool
关键字表示数组类型,字面量是由逗号分隔的一串表达式或值并以方括号 []
包裹而组成的:
var lList = [1,2,3]; // 编译器会自动推断出数组的类型List<int>,如果向数组里添加一个非int类型,会报错
在字面量前面加上关键字 const
,会定义一个编译时常量,那么就不能执行赋值操作,如:
var list = const [1,2,3];
list[1] = 0; // 报错!!
索引
跟其他语言一样,在Dart里,List
的索引也是从0
开始的,最后一个元素的索引位置位length-1
,可以通过[]
来获取对应索引的值。
var list = [1, 2, 3];
print(list.length); // 3
print(list[1]); // 2
list[1] = 1;
print(list[1]); // 1
扩展符
Dart提供了 ...
和 ...?
的扩展操作,可以将数组中所有元素插入另一个数组内, 而 ...?
空判断扩展可以避免将null
插入到目标数组。
var list = [1, 2, 3];
var list2 = [0, ...list];
print(list2.length); // 4
print(list2); // [0,1,2,3]
List? list3 = null;
var list4 = [0, ...?list3];
print(list4); // 1
控制语句
Dart 还同时引入了 集合中的 if
和 集合中的 for
操作,在构建集合时,可以使用条件判断 if
和循环 for
。
var promoActive = false;
var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];
print(nav); // ['Home', 'Furniture', 'Plants'];
var listOfInts = [1, 2, 3];
var listOfStrings = ['#0', for (var i in listOfInts) '#$i'];
print(listOfStrings[1]); // #1
集合
Dart中使用Set
来表示集合类型,它是一组特定集合的无序集合。可以通过字面量{}
和Set
类来定义
var set = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
Dart 会根据字面量来推断变量是一个Set<String>类型,如果往set对象中添加其他类型的元素,会抛异常
可以通过在{}
前加上类型来定义一个空的集合,或者通过将{}
赋值给一个Set类型的变量来实现。
var set = <String>{};
Set set2 = {};
Map的字面量语法和Set的字面量语法很接近,因为现有Map的字面量语法,因此{}定义的是一个Map而不是Set,Dart会为{}创建一个Map<dynamic, dynamic>对象。
常用用法
- 使用
add
或者addAll
方法向集合中添加元素 -
const
定义的常量集合,不能调用add
等方法,否则会报错 -
Set
支持...
扩展,支持if/for
控制语句 - 可以通过
length
来获取集合的数量
Map
Map
是用来关联 keys
和 values
的对象。其中键和值都可以是任何类型的对象。每个键只能出现一次但是值可以重复出现多次。 Dart中 Map
提供了 Map
字面量以及 Map
类型两种形式的 Map
:
- 通过字面量定义Map
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
Dart 将
gifts
推断为Map<String, String>类型,而将nobleGases
推断位Map<int, String>类型,如果向其中添加不正确的类型,则会抛出异常。
- 通过Map类型来定义Map
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
Map的常用用法
- 给Map添加一组键值对,可通过
gifts['third']=glasses
; - 获取Map中的值可以使用
gifts['forth']
; - 判断某个key是否存在使用
gifts['sixth']==null
; - 使用
const
定义常量的Map时,不可以添加,修改操作 - Map同样支持
...
扩展操作和if/for
控制语句
函数
Dart是一种真正面向对象的语言,所以即便函数也是对象并且类型为 Function
,这意味着函数可以被赋值给变量或者作为其它函数的参数。
我们可以像这样定义一个函数 :
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
也可以省略 返回类型和参数类型(不建议这样做)
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
如果函数内只包含一个表达式,那么也可以简写成:
bool isNoble(int atomicNumber)=> _nobleGases[atomicNumber] != null;
语法
=>
表达式是{ return 表达式; }
的简写,=>
有时也称之为箭头函数;=>
后面只能跟表达式,不能跟if判断语句
参数
函数的参数主要分两种: 必要参数和可选参数。必要参数必须定义在参数列表前面,可选参数定义在必要参数的后面??裳〔问梢允恰该摹挂部梢允恰肝恢玫摹埂?/p>
命名参数
命名参数默认时可选的,除非使用了required
关键字标记。
命名参数使用 {param1, param2}
来定义,如果没有指定默认值,没有required
标记,默认值为null
void enableFlags({bool? bold, bool? hidden}){
...
}
//使用
enableFlags(bold:true, hidden:false);
可以使用 =
来给参数指定默认值,指定的默认值必须时一个常量
void enableFlags({bool bold = false, bool hidden = false}) {...}
// hidden参数位false
enableFlags(bold: true);
如果希望某个参数时必须要传的,那么就可以使用required
来修饰,例如:
const Scrollbar({super.key, required Widget child});
如果创建一个Scrollbar不包含child的时,编译器就会报错
required 参数也是可以为null的。
位置参数
使用 []
将一系列参数包裹起来,即可将其标记为位置参数,因为它们的默认值是 null
,所以如果没有提供默认值的话,它们的类型必须得是允许为空 nullable
的类型
String say(String from, String msg, [String? device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
//不使用位置参数调用函数
print(say('Bob', 'Howdy')); // Bob says Howdy
//使用位置参数调用函数
print(say('Bob', 'Howdy', 'smoke signal')); //Bob says Howdy with a smoke signal
可以使用 =
来为一个位置可选参数指定除了 null
以外的默认值。指定的默认值必须要为编译时的常量
String say(String from, String msg, [String device = 'carrier pigeon']) {
var result = '$from says $msg with a $device';
return result;
}
print(say('Bob', 'Howdy')); //Bob says Howdy with a carrier pigeon
mian()
main()
顶级函数作为程序的入口, main()
函数返回值为 void
并且有一个 List<String>
类型的可选参数。
函数是一级对象
可以将函数作为参数传递给另一个函数。例如:
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// 将printElement作为一个参数
list.forEach(printElement); // 1 2 3
也可以将函数赋值给一个变量,比如:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
print(loudify('hello')); // !!! HELLO !!!
匿名函数
顾名思义,就是没有名字; 通常称为 匿名函数
、 Lambda 表达式
或 Closure 闭包
;可以将匿名函数赋值给一个变量然后使用。
匿名函数的定义如下
(param1, params2) {
...
}
下面例子就是定义了一个只有一个参数item
的匿名函数用来打印List
中每个字符串和字符串长度:
const list = ['apples', 'bananas', 'oranges'];
list.map((item) {
return item.toUpperCase();
}).forEach((item) {
print('$item: ${item.length}'); // APPLES: 6 BANANAS: 7 ORANGES: 7
});
运算符
下表中是常用的运算符,表中从上到下也表示运算符的优先级顺序。
描述 | 操作符 |
---|---|
一元后缀 | expr++ expr-- () [] ?[] . ?. ! |
一元前缀 | -expr !expr ~expr ++expr --expr await expr |
乘除运算 | * / % ~/ |
加减运算 | + - |
位移 | << >> >>> |
位运算AND | & |
位运算异或 | ^ |
位运算或 | | |
关系类型判断 | >= > <= < as is is! |
相等 | == != |
逻辑与 | && |
逻辑或 | |
如果空 | ?? |
三元运算 | expr1 ? expr2 : expr3 |
级联 | .. ?.. |
复合运算 | = *= /= += -= &= ^= etc. |
类型判断
|操作符|含义|
|--|--|
|as|类型转换|
|is|如果对象是指定对象返回true|
|is!|如果对象是指定对象返回false|
当 obj
实现了 T
接口时, obj is T
才会返回 true
;
只有当obj
明确是某个对象类型时,才使用as
// 1
(employee as Person).firstName = 'Bob';
// 2
if (employee is Person) {
// 类型检查
employee.firstName = 'Bob';
}
在上面例子中,如果
employee
是null
或者不是Person
类型时, 注释1处语句就会抛出异常,而注释2处不会抛出异常。
赋值运算
??=
使用场景 obj ??= 123
,当obj
为 null
时,才会赋值 123
否则不处理。
条件表达式
表达式1 ?? 表达式2
如果表达式 1 为
非 null
则返回其值,否则执行表达式 2 并返回其值
级联表达式
级联运算符 ..
, ?..
可以在同一个对象上连续调用多个对象的变量或方法
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
等同于代码:
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;
如果对象可能为null的话,可以使用?..
querySelector('#confirm')
?..text = 'Confirm'
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'))
..scrollIntoView();
代码等同于
var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.classes.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();
级联对象可以嵌套使用:
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
在调用返回对象的函数时,一定要谨慎使用级联操作;如果函数返回
void
,那么就不能使用级联操作。
其他运算符
操作符 | 含义 |
---|---|
?[] | 判空访问List,不为空时访问List中元素 |
?. | 条件访问成员,不为空时访问成员 |
! | 断言操作,认为一定不为空访问相关成员,如果为空抛出异常 |
控制语句
if-else
与其他语言类似,通过条件判断执行代码块 其中 else
和 else if
是可选判断。但是判断的条件一定是bool
型,不可以是其他类型。
if(表达式1){
...
}else if(表达式2){
...
}else{
...
}
for循环
- 可以使用标准的for循环进行迭代
for(int i=0; i<10; i++)
- 使用for-in方法遍历一个可迭代的对象
for(final a in objs)
- 使用可迭代对象的forEach方法
iteractor.forEach()
while 和 do-while循环
while
和 do-while
的区别是:
-
while
会先进行判断,然后再执行代码块, -
do-while
是先执行代码块,再进行判断
contin 和 break
continue
表示跳过此次循环,break
表示跳出循环
如果在Iterable
对象中,可以使用where
代替continue
:
candidates
.where((c) => c.yearsExperience >= 5)
.forEach((c) => c.interview());
switch-case
Switch
语句在Dart中使用 ==
来比较整数、字符串或编译时常量,比较的两个对象必须是同一个类型且不能是子类并且没有重写 ==
操作符。 枚举类型非常适合在 Switch
语句中使用。
非空case
想要实现穿透可通过continue
来实现:
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
nowClosed:
case 'NOW_CLOSED':
executeNowClosed();
break;
}
每个case内都可以有局部变量,并且只在当前case可见。
注释
单行注释
单行注释以//
开始,所在行的内容都属于注释内容
// 这是一个单行注释
多行注释
多行注释以/*
开头,以*/
结尾,在/*
和*/
之间的内容都会被编译器忽略,认为是注释内容
/*
这是一个多行注释
注释第二行内容
...
*/
/*
嵌套注释的外层
/*
嵌套注释内层
*/
嵌套注释外层
*/
文档注释
文档注释使用///
或者是/**
,多个连续///
注释与多行注释效果一致。多行文档注释,编译器会忽略注释的内容,但是如果使用了[]
,则编译器会产生引用括号内的内容如类、方法、字段、顶级变量、函数和参数。
///这是一个文档注释举例
///如果使用了中括号,则会解析括号内容,引用相关内容[feed]
上面举例中[feed]
将会生成一个链接,指向feed的方法文档