Flutter 基础 | Dart 语法
该系列记录了从零开始学习 Flutter 的学习路径,第一站就是 Dart 语法。本文可以扫除看 Flutter 教程,写 Flutter 代码中和语言有关的绝大部分障碍。值得收藏~
声明并初始化变量
int i = 1; // 非空类型必须被初始化
int? k = 2; // 可空类型
int? h; // 只声明未初始化,则默认为 null
var j = 2; // 自动推断类型为int
late int m; // 惰性加载
final name = 'taylor'; // 不可变量
final String name = 'taylor'; // 不可变量
Dart 中语句的结尾是带有分号;
的。
Dart 中声明变量时可以选择是否为它赋初始值。但非空类型必须被初始化。
Dart 中声明变量可以显示指明类型,类型分为可空和非空,前者用类型?
表示。也可以用var
来声明变量,此时编译器会根据变量初始值自动推断类型。
late
关键词用于表示惰性加载,它让非空类型惰性赋值成为可能。得在使用它之前赋值,否则会报运行时错误。
惰性加载用于延迟计算耗时操作,比如:
late String str = readFile();
str 的值不会被计算,直到它第一次被使用。
??
可为空类型提供默认值
String? name;
var ret = name ?? ''
如果 name 为空则返回空字串,否则返回 name 本身。
数量
在 Dart 中int
和double
是两个有关数量的内建类型,它们都是num
的子类型。
若声明变量为num
,则可同时被赋值为int
或double
num i = 1;
i = 2.5;
字串
''
和""
都可以定义一个字串
var str1 = 'this is a str';
var str2 = "this is another str";
字串拼接
使用+
拼接字符串
var str = 'abc'+'def'; // 输出 abcdef
多行字串
使用'''
声明多行字符串
var str = '''
this is a
multiple line string
''';
纯字串
使用r
声明纯字符串,其中不会发生转义。
var str = r'this is a raw \n string'; // 输出 this is a raw \n string
字串内嵌表达式
字符串中可以内嵌使用${}
来包裹一个有返回值的表达式。
var str = 'today is ${data.get()}';
字串和数量相互转化:
int.parse('1'); // 将字串转换为 int
double.parse('1.1'); // 将字串转换为 double
1.toString(); // 将 int 转换为字串
1.123.toStringAsFixed(2); // 将 double 转换为字串,输出 '1.12'
集合
声明 List
与有序列表对应的类型是List
。
用[]
声明有序列表,并用,
分割列表元素,最后一个列表元素后依然可以跟一个,
以消灭复制粘贴带来的错误。
var list = [1,2,3,];
存取 List 元素
列表是基于索引的线性结构,索引从 0 开始。使用[index]
可以获取指定索引的列表元素:
var first = list[0]; // 获取列表第一个元素
list[0] = 1; //为列表第一个元素赋值
展开操作符
...
是展开操作符,用于将一个列表的所有元素展开:
var list1 = [1, 2, 3];
var list2 = [...list1, 4, 5, 6];
上述代码在声明 list2 时将 list1 展开,此时 list2 包含 [1,2,3,4,5,6]
除此之外,还有一个可空的展开操作符...?
,用于过滤为null
的列表:
var list; // 声明时未赋初始值,则默认为 null
var list2 = [1, ...?list]; // 此时 list2 内容还是[1]
条件插入
if
和for
是两个条件表达式,用于有条件的向列表中插入内容:
var list = [
'aa',
'bb',
if (hasMore) 'cc'
];
如果 hasMore 为 true 则 list 中包含'cc',否则就不包含。
var list = [1,2,3];
var list2 = [
'0',
for (var i in list) '$i'
];// list2 中包含 0,1,2,3
在构建 list2 的时候,通过遍历 list 来向其中添加元素。
Set
Set
中的元素是可不重复的。
用{}
声明Set
,并用,
分割元素:
var set = {1,2,3}; // 声明一个 set 并赋初始元素
var set2 = {}; // 声明一个空 set
var set3 = new Set(); // 声明一个空 set
var set4 = Set(); // 声明一个空 set,new 关键词可有可无
Map
Map
是键值对,其中键可以是任何类型但不能重复。
var map = {
'a': 1,
'b': 2,
}; // 声明并初始化一个 map,自动推断类型为 Map
var map2 = Map(); // 声明一个空 map
map2['a'] = 1; // 写 map
var value = map['a']; //读 map
,int>,int>
读写Map
都通过[]
。
const
const
是一个关键词,表示一经赋值则不可修改:
// list
var list = const [1,2,3];
list.add(4); // 运行时报错,const list 不可新增元素
// set
var set = const {1,2,3};
set.add(4); // 运行时报错,const set 不可新增元素
// map
var map = const {'a': 1};
map['b'] = 2; // 运行时报错,const map 不能新增元素。
类
声明类
class Pointer {
double x;
double y;
void func() {...} // void 表示没有返回值
double getX(){
return x;
}
}
- 用关键词
class
声明一个类。 - 类体中用
类型 变量名;
来声明类成员变量。 - 类体中用
返回值 方法名(){方法体}
来声明类实例方法。
构造方法
上述代码会在 x ,y 这里报错,说是非空字段必须被初始化。通常在构造方法中初始化成员变量。
构造方法是一种特殊的方法,它返回类实例且签名和类名一模一样。
class Point {
double x = 0;
double y = 0;
// 带两个参数的构造方法
Point(double x, double y) {
this.x = x;
this.y = y;
}
}
这种给成员变量直接赋值的构造方法有一种简洁的表达方式:
class Point {
double x = 0;
double y = 0;
Point(this.x, this.y); // 当方法没有方法体时,得用;表示结束
}
命名构造方法
Dart 中还有另一个构造方法,它的名字不必和类名一致:
class Point {
double x;
double y;
Point.fromMap(Map map)
: x = map['x'],
y = map['y'];
}
为 Point 声明一个名为fromMap
的构造方法,其中的:
表示初始化列表,初始化列表用来初始化成员变量,每一个初始化赋值语句用,
隔开。
初始化列表的调用顺序是最高的,在一个类实例化时会遵循如下顺序进行初始化:
- 初始化列表
- 父类构造方法
- 子类构造方法
Point.fromMap() 从一个 Map 实例中取值并初始化给成员变量。
然后就可以像这样使用命名构造方法:
Map map = {'x': 1.0, 'y': 2.0};
Point point = Point.fromMap(map);
命名构造方法的好处是可以将复杂的成员赋值的逻辑隐藏在类内部。
继承构造方法
子类的构造方法不能独立存在,而是必须调用父类的构造方法:
class SubPoint extends Point {
SubPoint(double x, double y) {}
}
上述 SubPointer 的声明会报错,提示得调用父类构造方法,于是改造如下:
class SubPoint extends Point {
SubPoint(double x, double y) : super(x, y);
}
在初始化列表中通过super
调用了父类的构造方法。父类命名构造方法的调用也是类似的:
class SubPoint extends Point {
SubPoint(Map map) : super.fromMap(map);
}
构造方法重定向
有些构造方法的目的只是调用另一个构造方法,为此可以在初始化列表中通过this
实现:
class Point {
double x = 0;
double y = 0;
Point(this.x, this.y);
Point.onlyX(double x): this(x, 0);
}
Point.onlyX() 通过调用另一个构造方法并为 y 值赋值为 0 来实现初始化。
方法
Dart 中方法也是一种类型,对应Function
类,所以方法可以被赋值给变量或作为参数传入另一个方法。
// 下面声明的两个方法是等价的。
bool isValid(int value){
return value != 0;
}
isValid(int value){// 可自动推断返回值类型为 bool
return value != 0;
}
声明一个返回布尔值的方法,它需传入一个 int 类型的参数。
其中方法返回值bool
是可有可无的。
bool isValid(int value) => value != 0;
如果方法体只有一行表达式,可将其书写成单行方法样式,方法名和方法体用=>
连接。
Dart 中的方法不必隶属于一个类,它也可以顶层方法的形式出现(即定义在.dart文件中)。定义在类中的方法没有可见性修饰符public private protected ,而是简单的以下划线区分,_
开头的函数及变量是私有的,否则是公有的。
可选参数 & 命名参数
Dart 方法可以拥有任意数据的参数,对于非必要参数,可将其声明为可选参数,调用方法时,就不用为其传入实参:
bool isValid(int value1, [int value2 = 2, int value3 = 3]){...}
定义了一个具有两个可选参数的方法,其中第二三个参数用[]
包裹,表示是可选的。而且在声明方法时为可选参数提供了默认值,以便在未提供相应实参时使用。所以如下对该方法的调用都是合法的。
var ret = isValid(1) // 不传任何可选参数
var ret2 = isValid(1,2) // 传入1个可选参数
var ret3 = isValid(1,2,3) // 传入2个可选参数
使用[]
定义可选参数时,如果想只给 value1,value3 传参,则无法做到。于是乎就有了{}
:
bool isValid(int value1, {int value2 = 2, int value3 = 3}) {...}
然后就可以跳过 value2 直接给 value3 传参:
var ret = isValid(1, value3 : 3)
这种语法叫可选命名参数。
Dart 还提供了关键词required
指定在众多可选命名参数中哪些是必选的:
bool isValid(int value1, {int value2, required int value3}) {...}
匿名方法
匿名方法表示在给定参数上进行一顿操作,它的定义语法如下:
(类型 形参) {
方法体
};
如果方法体只有一行代码可以将匿名函数用单行表示:
(类型 形参) => 方法体;
操作符
三元操作符
三元操作符格式如下:布尔值 ? 表达式1 : 表达式2;
var ret = isValid ? 'good' : 'no-good';
如果 isValid 为 true 则返回表达式1,否则返回表达式2。
瀑布符
该操作符..
用于合并在同一对象上的多个连续操作:
val paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0
构建一个画笔对象并连续设置了 3 个属性。
如果对象可控则需使用?..
:
paint?..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0
类型判定操作符
as
是强转操作符,表示将一个类型强转为另一个类型。
is
是类型判定操作符,用于判断某个实例是否是指定类型。
is!
是与 is
相反的判定。
流程控制
if-else
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
for
for (var i = 0; i < 5; i++) {
message.write('!');
}
如果不需要关心循环的索引值,则可以这样:
for (var item in list) {
item.do();
}
while
while (!isDone()) {
doSomething();
}
do {
printLine();
} while (!atEndOfPage());
break & continue
break & continue 可用于 for 和 while 循环。
break
用于跳出循环
var i = 0
while (true) {
if (i > 2) break;
print('$i');
i++;
} // 输出 0,1,2
continue
用于跳过当前循环的剩余代码:
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) continue;
print('$i');
}// 输出 1,3,5,7,9
switch-case
Dart 中的 switch-case 支持 String、int、枚举的比较,以 String 为例:
var command = 'OPEN';
switch (command) {
case 'CLOSED':
case 'PENDING': // 两个 case 共用逻辑
executePending();
break; // 必须有 break
case 'APPROVED':
executeApproved();
break;
case 'DENIED':
executeDenied();
break;
case 'OPEN':
executeOpen();
break;
default: // 当所有 case 都未命中时执行 default 逻辑
executeUnknown();
}
关键词
所有的关键词如下所示:
作者:唐子玄
链接:https://juejin.cn/post/7028710779171897351
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。