Flutter入门学习笔记(二):dart基本语法
1.Hello World
- (1) vscode安装
dart
插件 - (2) 创建项目
首先 ctrl
+shift
+p
打开命令面板,输入 dart
,选择 Dart: New Project
;然后选择 Console Application
及项目存放路径;最后输入项目名 hello_world
。
- (3) 运行项目
命令行输入如下命令,运行项目
>> dart run
Building package executable...
Built hello_world:hello_world.
Hello world: 42!
- (4) 源码解析
项目层级结构如下:
├───bin
├───lib
└───test
入口函数为 bin/hello_world.dart
文件中的main函数:
// 导入hello_world包
import 'package:hello_world/hello_world.dart' as hello_world;
void main(List<String> arguments) {
// 调用hello_world包中的calculate()函数
print('Hello world: ${hello_world.calculate()}!');
}
hello_world
包对应于 lib/hello_world.dart
文件:
int calculate() {
return 6 * 7;
}
这里有个 calculate
函数,返回42,这也就得到了最终的运行后结果:Hello world: 42!
2.变量
在Dart中,可以使用显式声明方法,或使用 var
、Object
和 dynamic
来声明变量,但它们在类型检查、使用方法和编译器行为上有显著区别。
2.1 显式声明
直接在声明是显式指定变量类型:
String name = 'Bob';
2.2 var
var
声明变量,可以让编译器根据初始值自动推断类型,类型推断完成后,赋值必须是相同类型。
var name = 'Bob';
print(name.runtimeType);
// name = 20; 编译错误,类型不匹配
name = 'mirror'; // 合法
2.3 Object
Object
是所有类的基类,可以存储任何对象(包括基本类型和自定义类),但是需要显式转换,才能调用子类的特定方法。使用时,可以通过 is
判断其类型,然后使用 as
来完成类型转换:
Object obj = 42;
if (obj is int) {
print(obj + 1);
}
int value = obj as int;
print('value: $value');
2.4 dynamic
dynamic
会关闭类型检查,允许变量在运行时持有任何类型,并调用任何方法(即使调用不存在的方法,也能编译通过,这可能导致运行时错误),通常适用于动态场景(如,Json解析和反射):
dynamic d = 42;
print(d.foo()); // 编译通过,但运行时报错:NoSuchMethodError
d = 'hello';
print(d.length); // 合法(运行时类型为 String)
3.空安全
Dart语言要求以健全的控安全方式编写代码。空安全能防止意外访问 null
变量而导致的错误,这样的错误也被称为空解引用错误。在空安全的基础上,Dart编译器可以在编译期检测这些潜在错误。
空安全引入三个关键更改:
- (1) 在类型声明末尾添加
?
控制类型是否允许null
:
String? name // 可空类型,name可以为'null',也可以为String
String name // 不可空类型,name不可以为'null',但是可以为String
- (2) 变量使用前必须先进行初始化。可空变量默认初始化为
null
。 - (3) 不可对可空类型表达式上访问属性或调用方法。例外情况适用于
null
支持的属性或方法,如,hashCode
或toString()
。
4.三个关键字
4.1 const
const
编译期常量,值在编译时就确定,永远不会改变。只能用于字面量、构造函数被标记为 const
的对象:
const pi = 3.1415926;
const list = [1, 2, 3];
const point = Point(0, 0);
class Point {
final int x, y;
const Point(this.x, this.y);
}
4.2 final
运行时一次性赋值,只能赋值一次,对象本身可内部可变:
final now = DateTime.now();
final list = [1, 2, 3];
list.add(4); // 合法
// list = [5, 6] // 编译错误:不能第二次赋值
4.3 late
延迟初始化,先声明,后赋值,但是使用前必须赋值,否则会运行时抛 LateError
。
class Service {
late final String config; // 先不赋初值
void init(String path) {
config = path; // 运行期再赋值
}
}
class Lazy {
late final heavy = _compute(); // 第一次访问时才执行 _compute
int _compute() {
print('compute once');
return 42;
}
}
void main() {
var l = Lazy();
print('before first use');
print(l.heavy); // 这里才触发 compute
print(l.heavy); // 第二次直接用缓存结果
}
5.操作符
5.1 基本算术运算符和逻辑运算符
assert(2 + 3 == 5); // 加
assert(2 - 3 == -1); // 减
assert(2 * 3 == 6); // 乘
assert(5 / 2 == 2.5); // 除
assert(5 ~/ 2 == 2); // 整除
assert(5 % 2 == 1); // 求余
assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
int a;
int b;
a = 0;
b = ++a; // 先自增后赋值
assert(a == b); // 1 == 1
a = 0;
b = a++; // 先赋值后自增.
assert(a != b); // 1 != 0
a = 0;
b = --a; // 先自减,后赋值.
assert(a == b); // -1 == -1
a = 0;
b = a--; // 先赋值后自减.
assert(a != b); // -1 != 0
assert(2 == 2); // 等于
assert(2 != 3); // 不等于
assert(3 > 2); // 大于
assert(2 < 3); // 小于
assert(3 >= 3); // 大于等于
assert(2 <= 3); // 小于等于
5.2类型测试运算符
as
(类型转换),is
(是否有特定类型),is!
(是否没有特定类型)
5.3 赋值运算符
常见的赋值运算符有:
= |
*= |
%= |
>>>= |
^= |
---|---|---|---|---|
+= |
/= |
<<= |
&= |
` |
-= |
~/= |
>>= |
a = value; // 将value赋值给a
b ??= value; // 当b为null时,将value赋值给b,否则,b保持原始值
a *= 2; // 乘法复合赋值
a /= 2; // 除法复合赋值
5.4逻辑运算符
操作符 | 描述 |
---|---|
! expr |
逻辑非 |
` | |
&& |
逻辑与 |
5.5 位运算和移位运算
操作符 | 描述 |
---|---|
& |
与 |
` | ` |
^ |
异或 |
~ expr |
非 |
<< |
左移 |
>> |
右移 |
>>> |
无符号右移 |
5.6 条件表达式
- (1)
condition
满足返回expr1
的值,否则,返回expr2
的值
condition
?
expr1
:
expr2
- (2)
expr1
非空,返回expr1
的值,否则,返回expr2
的值
expr1
??
expr2
6.控制流
6.1 循环
// 1.for循环
var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
message.write('!');
}
var callbacks = [];
for (var i = 0; i < 2; i++) {
callbacks.add(() => print(i));
}
// 2.for-in循环
for (final c in callbacks) {
c();
}
// 3.forEach循环
var collection = [1, 2, 3];
collection.forEach(print); // 1 2 3
// 4.while循环
while (!isDone()) {
doSomething();
}
// 5.do-while循环
do {
printLine();
} while (!atEndOfPage());
// 6.break和continue
while (true) {
if (shutDownRequested()) break;
processIncomingRequests();
}
for (int i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.yearsExperience < 5) {
continue;
}
candidate.interview();
}
6.2 分支
// 1.if-else
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
// 2.if-case
if (pair case [int x, int y]) {
print('Was coordinate array $x,$y');
} else {
throw FormatException('Invalid coordinates.');
}
// 3.switch-case
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
case 'PENDING':
executePending();
case 'APPROVED':
executeApproved();
case 'DENIED':
executeDenied();
case 'OPEN':
executeOpen();
default:
executeUnknown();
}
6.3 错误处理
// 1.Throw
throw FormatException('Expected at least 1 section');
// 2.try-catch-finally
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
// 3.assert
assert(text != null);