Flutter 开发语言是Dart,点击可查看官方文档
注释
单行
1
// 单行注释
多行
1
2
3
/*
多行注释
*/
文档注释
1
/// 文档注释
文档注释是多行或单行的,开头使用///或/**。使用///连续的行,与多行文档注释有同样的效果。
1
2
3
4
5
6
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
class Llama {
}
语言特性
- Dart所有内容都是一个对象,每个对象都是一个类的实例。数字(numbers),函数(funtions)和null都是对象。所有对象都继承自Object类。
- Dart是强类型的语言,变量定义时Dart可以推断类型。如果要明确说明不需要任何类型,请使用特殊类型dynamic。尽量给变量定义时设定类型,会更加安全。
- Dart支持泛型类型,如List
(整数列表)或List (任何类型的对象列表)。 - Dart支持顶级函数(例如main()),以及绑定到类或对象的函数(分别是静态和实例方法)。还可以在函数内创建函数(嵌套函数)。
- 类似地,Dart支持顶级变量,以及绑定到类或对象的变量(静态和实例变量)。实例变量有时称为字段或属性。
- Dart不具备public,protected和private等关键字。如果标识符以下划线(_)开头,则表示其是私有的。
- 标识符以小写字母或下划线(_)开头,后跟字符和数字的任意组合。
- Dart有表达式(具有运行时值)和 语句(不具有)。语句通常包含一个或多个表达式,但表达式不能直接包含语句。
- Dart可以报告两种问题:警告和错误。警告只是表明您的代码可能无法正常工作,但它们不会阻止您的程序执行。错误可以是编译时或运行时,编译时错误会阻止代码执行; 运行时错误导致 代码执行时引发异常。
关键字 (60个)
上下文关键字(5个),仅在特定位置具有含义,它们不在是有效的标识符
| show | async | sync | on | hide | | :—: | :—: | :—: | :—: | :—: |
内置标识符(20个),为了简化将JavaScript移植到Dart代码,这些关键字在大多数地方都是有效的标识符,但它们不能用作类或类型名称,也不能用作前缀。
abstract | dynamic | implements | as | import |
---|---|---|---|---|
static | export | interface | external | library |
factory | mixin | operator | typedef | covariant |
Function | part | get | deferred | set |
异步支持标记(2个),Dart 1.0发布后添加的异步支持相关的更新,有限的保留字。不能使用await或yield作为任何函数体中的标识符标记async,async*或sync*。
- await
- yield
保留字(33个)
else | assert | enum | in |
---|---|---|---|
super | extends | is | break |
this | case | throw | catch |
false | new | true | class |
final | null | try | const |
finally | continue | for | var |
void | default | rethrow | while |
return | with | do | if |
set |
变量
定义一个字符串变量:
1
var name = 'Dart';
也可以使用:
1
dynamic name = 'Dart';
或者
1
2
// 指明String类型
String name = 'Dart';
如果对象不限于单一类型,可指定Object或dynamic类型 。
var, Object, dynamic 不同:
var:其实它仅仅只是一个语法. var本身并不是一种类型,而object和dynamic是类型。var声明的变量在赋值的那一刻,就已经决定了它是什么类型。
object:所有的类型都派生自Object,所以这也是它能够被赋值为任意类型的原因。
dynamic:在编译时候不确定实际类型,而在运行时确定其实际类型
默认值:
未初始化的变量的初始值为:null。
即使是具有数字类型的变量最初也是null,因为数字也是对象。
1
int lineCount ; assert (lineCount == null );
- assert在开发期间如果condition为false ,则抛出异常,在Release模式则会被忽略调用。
final和const
final定义的变量的值只能设置一次; const变量是编译时常量。实例变量可以是 final,但不能是 const。final实例变量必须在构造函数启动之前初始化。
例如:
1
2
3
final name = 'Dart' ; //没有类型注释final String nickname = 'Bobby' ;
// 无法修改name的值
name = 'Flutter'; // 错误
内置类型
- numbers
- strings
- booleans
- lists (also known as arrays)
- sets
- maps
- runes (for expressing Unicode characters in a string)
- symbols
Numbers
包含两种子类:int和double。
int
整数值不大于64位,具体取决于平台。在Dart VM上,值可以是-263到263 - 1,编译成JavaScript的Dart使用JavaScript代码,允许值从-253到253-1。
double
4位(双精度)浮点数,由IEEE 754标准规定。
在Dart 2.1之前,在双上下文中使用整数文字是错误的。从Dart 2.1开始,必要时整数文字会自动转换为双精度数:
1
double z = 1; // 相当于double z = 1.0
Strings
Dart字符串UTF-16编码的一个队列。可以使用单引号或双引号来创建字符串,也可以使用表达式拼接字符串,如果表达式是标识符,则跳过’{}‘。要获取与对象相对应的字符串,Dart会调用该对象的toString()方法。可以使用 ’==‘ 判断两个字符串是否相等。也可以使用带有单引号或双引号的三引号('''
或"""
)创建多行字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var str1 = 'abc';
var str2 = "abc";
var str3 = str1 + str2;
var b = str1 == str2; // 结果true
// 多行字符串
var str4 = ’‘’
abc
def
‘’‘;
var str4 = """
abc def
""";
可以添加r前缀,代表’raw‘字符串:
1
var str = r'abc';
Booleans
- Dart 是强 bool 类型检查,只有两个对象具有bool类型:true和false,它们都是编译时常量。
- Dart的类型安全意味着我们不能使用 if(nonbooleanValue) 或 assert(nonbooleanValue) 等代码, 相反Dart使用的是显式的检查值,这一点需要注意。
List
Dart中,数组是 List 对象。默认下标从0开始。
1
2
// Dart推断出list类型List<int>。如果尝试将非整数对象添加到此列表,则会引发错误
var l1 = [1, 2, 3];
Dart 2.3引入了spread operator(…)和 null-aware spread operator(…?),它提供了一个将多个元素插入集合的简洁方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// var l1 = List();
var l1 = [1, 2, 3];
var l1 = [1, 2, 3, 4];
l1.add(5);
l1.clear();
l1.addAll([10, 11, 12, 13, 14]);
l1.remove(1);
l1.removeAt(0);
l1.insert(0, 6);
l1.replaceRange(0, 1, [8, 9]);
l1.removeLast();
print(l1);
print(l1.first);
print(l1.last);
print(l1[1]);
l1.removeRange(0, 1);
// 删除值等于 8 的元素
l1.removeWhere((item)=>item == 8);
print(l1);
// 添加另一个数组的所有元素
var l2 = [0, ... l1];
print(l2);
Sets
Dart中的Set是无序且不重复的集合,不能通过索引去获取值,也没有固定元素的定义。从Dart 2.2开始引入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// set new是可选的
var set1 = new Set();
set1.add(1);
// 添加数组
set1.addAll(['a', 'b', 1]);
print(set1);
var set2 = new Set.of([1, 2]);
print(set2);
print(set1.contains(1));
set1.remove(1);
set1.clear();
// 指定类型
var set3 = <String>{ 'a', 'b', 'c' };
print(set3);
// Set常量
final constantSet = const {'a', 'b', 'c'};
// constantSet.add('d'); 报错,不能修改
print(constantSet);
支持(… 和 …?),参考list。
Maps
映射是无序的键值对,键和值可以是任何类型的对象。key不能相同,但是value可以一样。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Map
// 推断类型 Map<String, String>
var map1 = { 'key1' : 'value1', 'key2' : 'value2' };
print(map1);
// Map<int, String>
var map2 = { 2 : 'value1', 3 : 'value2' };
print(map2);
// 使用构造函数创建, Map可变的
var map3 = Map();
map3['key1'] = 'value1';
// 指定类型
var map4 = Map<int, String>();
map3[2] = 'value2';
print(map3);
// 断言, 如果在Map中找不到对应的键,则返回null
assert(map3['key2'] == null);
// 长度
print(map3.length);
// 常量
final constantMap = const { 'key1' : 'value1'};
支持(… 和 …?),参考list。
List、Set和Map有一些通用的方法。其中的一些通用方法都是来自于类Iterable。List和Set是iterable类的实现,Map没有实现Iterable, 但是Map的属性keys和values都是Iterable对象
Runes
在Dart中,runes用于在字符串中表示Unicode字符。Dart字符串是UTF-16代码单元的序列,所以在字符串中表示32位Unicode值需要特殊的语法。
表达Unicode代码点的常用方法是‘\uXXXX’,‘XXXX’是4位十六进制值。例如,心形表情符号(♥)是\u2665。要指定多于或少于4个十六进制数字,请将值放在大括号中。例如,笑的表情符号(😆)是\u{1f600}。
该字符串类有几个属性可以用来提取符文信息。codeUnitAt和codeUnit属性返回16位编码单元。使用runes属性获取字符串的符文。
以下示例说明了符文,16位代码单元和32位代码点之间的关系
1
2
3
4
5
6
7
8
9
10
// Runes
var r1 = '\u{1f44f}';
print(r1);
print(r1.codeUnits);
print(r1.runes.toList());
Runes input = Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'
);
print(String.fromCharCodes(input));
使用列表操作操作符文时要小心。这种方法很容易分解,具体取决于特定的语言,字符集和操作。
Symbols
符号对象表示程序声明的操作者或标识符。它们对于按名称引用标识符的API非常有用。
要获取标识符的符号,使用符号文字‘#’号后面跟着标识符:
- #radix
- #bar
符号文字是编译时常量。
Functions
方Dart是一种真正的面向对象语言,因此即使是函数也是对象并且具有类型。这意味着函数可以作为变量或作为参数传递给其他函数。也可以调用Dart类的实例,就好像它是一个函数一样。
1
2
3
4
5
6
// Functions
// 类型bool可以省略
bool compareNumber(int a, int b) {
return a > b;
}
print(compareNumber(1, 2));
对于只包函一个表达式的函数,可以简写为:
1
2
3
// 是否是偶数
bool isEvenNumber(int a) => a % 2 == 0;
print(isEvenNumber(2));
- 可选参数
使用这种形式paramName : value指定命名参数。
1
2
3
4
5
// {param1, param2, ...}
// 设置[bold]和[hidden]标志...
void enableFlags({ bool bold, bool hidden }){
// ...
};
在Flutter中通常还会使用@required来注释命名参数,以指定必须传的参数:
1
const Scrollbar({Key key, @required Widget child})
- 可选位置参数
将参数放到‘[]’中,就是可选位置参数:
1
2
3
4
String say(String from, String msg, [ String device ]) {
var result = from + msg + device;
return result;
};
- 默认参数值
默认值必须是编译时常量,如果未提供则默认为null
1
2
3
4
// 默认参数值
enableFlags1({bool bold = true}) {
// ...
};
旧代码可能使用冒号’:’而不是’=’设置命名参数的默认值。原因是最初只支持’:’命名参数。该支持可能已被弃用,因此建议使用’=’指定默认值。
- main()函数
main()函数是应用程序的入口点,每个应用程序都必须有顶级的main()函数,该main()函数返回void并具有List
- 作为第一类对象的功能
将函数作为参数传递给另一个函数:
1
2
3
4
5
6
void printElement(int element) {
print(element);
}
var l = [1, 2, 3];
// 参数为函数
l.forEach(printElement);
- 匿名函数
匿名函数看起来类似于命名函数-零个或多个参数,在逗号和括号之间用逗号和可选类型注释分隔。通常用lambda和闭包创建。
1
2
3
4
5
6
7
8
// 匿名函数, 打印元素
var list = ['a', 'b', 'c'];
list.forEach(
// 参数f是一个函数,这里使用不指定名称的匿名函数
(item) {
print(item);
}
);
- 静态作用域
Dart是静态作用域语言,变量的作用域在写代码的时候就确定过了。基本上大括号里面定义的变量就 只能在大括号里面访问。
- 词法闭包
一个闭包是一个方法对象,不管该对象在何处被调用,该对象都可以访问其作用域内的变量。
函数可以封闭定义到其作用域内的变量。下面的示例中,makeAdder()捕获到了变量addBy。不管在哪调用makeAdder()所返回的函数,都可以使用addBy参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 返回一个相加的函数
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
void main() {
// add2, 和add4是闭包,Closure: (num) => num
var add2 = makeAdder(2);
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
- 测试函数是否相等
由于Dart中的Function也是对象,所以也是可以判断相等的。
1
2
3
4
5
6
7
8
void foo() {}
void main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
}
- 返回值
所有函数都有返回值。如果未指定返回值,则将语句return null;隐式附加到函数体。
1
2
foo() {}
assert(foo() == null);
运算符
描述 | 操作符 |
---|---|
一元后置操作符 | expr++ expr– () [] . ?. |
一元前置操作符 | expr !expr ~expr ++expr –expr |
乘除 | * / % ~/ |
加减 | + - |
位移 | « » |
按位与 | & |
按位或 | |
按位异或 | ^ |
逻辑与 | && |
逻辑或 | |
关系和类型判断 | >= > <= < as is is! |
相等 | == != |
是否为空 | ?? |
条件表达式 | expr1 ? expr2 : expr3 |
赋值 | = *= /= ~/= %= += -= «= »= &= ^= = ??= |
级联 | .. |
流程控制语句
- if and else
- for loops
- while and do-while loops
- break and continue
- switch and case
- assert
switch语句跟Objective-C不同的是可以将String类型用来当做case。这一点跟Swift一样。
异常
- throw 抛出异常
- rethrow 重新抛出
- try-catch 捕获异常
- Finally
1
2
3
4
5
6
7
8
9
// 捕获异常
try {
// 抛出异常
throw FormatException('预计至少有1个部分');
} catch(e){
print(e);
// 重新抛出
rethrow;
};
可以指定多个catch()子句,使用on时需要指定异常类型,可以指定一个或两个参数catch()。第一个是抛出的异常,第二个是堆栈跟踪(StackTrace对象):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 捕获异常
try {
// 抛出异常
// throw FormatException('预计至少有1个部分');
throw Exception('预计至少有1个部分');
} on FormatException catch(e) {
print(e);
// 重新抛出
// rethrow;
} catch(e){
print('抛出的异常:$e');
} finally {
print('最后执行的');
};
类型定义
在Dart中,函数是对象,就像字符串和数字是对象一样。一个类型定义,或功能型的别名,使我们能够声明一些字段和返回值。当函数类型分配给变量时,typedef会保留类型信息。
以下代码不使用typedef,:
1
2
3
4
5
6
7
8
9
10
11
12
13
class SortedCollection {
// 声明compare函数,并未指明类型
Function compare;
SortedCollection(int f(Object a, Object b)) {
compare = f;
}
}
void main() {
int sort(Object a, Object b) => 0;
SortedCollection coll = SortedCollection(sort);
assert(coll.compare is Function);
}
上边分配的类型信息在f到compare的过程中丢失了,函数f的类型是(Object, Object → int
( → 代表返回),而无法知道compare的函数类型。
使用typedef来显式的定义名称和返回类型。
1
2
3
4
5
6
typedef Compare = int Function(Object a, Object b);
class SortedCollection {
Compare compare;
SortedCollection(this.compare);
}
-
目前,typedef仅限于函数类型,以后可能会支持更多。
-
typedef只是别名,它们提供了一种检查任何函数类型的方法。
1
2
3
4
5
6
typedef CheckCompare<T> = int Function(T a, T b);
void main() {
int checkSort(int a, int b) => a - b;
print(checkSort is CheckCompare<int>);
}
元数据
可以在代码中使用元数据提供其他信息。元数据注解以字符开头@,后跟对编译时常量的引用(如deprecated)或对常量构造函数的调用。
所有Dart代码都有两个注解:@deprecated和 @override(表示重写父类方法)。使用@deprecated示例:
1
2
3
4
5
6
7
8
9
10
11
class MTelevision {
/// _Deprecated: Use [turnOn] instead._
@deprecated
void activate() {
turnOn();
}
void turnOn() {
print('turn On');
}
}
可以定义自己的元数据注解。这是一个定义带有两个参数的@todo注解的示例:
1
2
3
4
5
6
7
8
9
10
// 必须在所有define和import的上方,一般放在文件开头
library todo;
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
使用:
1
2
3
4
5
6
7
import 'todo.dart';
@Todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
元数据可以出现在库,类,typedef,类型参数,构造函数,工厂,函数,字段,参数或变量声明之前以及导入或导出指令之前。可以使用反射在运行时检索元数据。