最近开始接触Flutter相关的项目,通过源码、例子和一些资料的阅读,发现如果掌握了Dart的一些语法和一些基础对于Flutter的学习有着事半功倍的效果。下面是我在查阅一些资料和实际的开发中的一些总结。希望对今后的学习有所帮助。
Dart 诞生于 2011.10.10 日, 谷歌 Dart 语言项目的领导人 Lars Bak 在丹麦举行的Goto 会议上 布, Dart 种“结构化的 Web 程”语言, Dart 编程语言在所有现代浏览器和环境中提供高性能。
Dart语言的特性:
Dart重要概念:
public
protected
private
这些修饰符的概念,私有特性通过变量或函数加上下划线来表示。warning
) 和 错误信息(errors
), 警告信息只是表明代码可能不工作,但是不会妨碍程序运行,错误信息可以是编译时的错误,也可能是运行时的错误, 编译时的错误将阻止程序运行,运行时的错误将会以异常(exception )的方式呈现。在Dart里,变量的声明有三种方式:
用var
关键字
用var声明变量时,如果未指定初始值,可以变成任意类型。如果指定了类型,则类型会被锁定。如:
void main() {
var v; // 初始值未指定类型
v = '2'; // 可以变为任意类型
v = 3;
print('v: $v');
var v1 = '111'; // 初始化时已经指定类型
var v1 = 222; // 此时无法赋值成功,编译时报错
}
用dynamic
关键字
dynamic声明的变量,表示动态任意类型,编译时不检查类型。如:
void main() {
dynamic d1 = '111';
d1 = 222;
d1 = 333.0;
print("d1: $d1");
// dynamic修饰的变量,在编译时不做检查
d1.test(); // 这行还代码,在编译时是不会检查错误信息的,运行时会报错
}
用Object
Object声明的变量,表示任意动态类型,编译时检查类型。如:
void main() {
Object o1 = "sss";
o1 = 222;
o1.test(); // 编译时会做检查,报错
}
final 或 const 修饰的变量为常量或固定值。
var
同时使用static const
Dart语言常用的基本数据类型包括: Number
,String
,Boolean
,List和Map
Number类型
Number 类型包括 int 整形,double浮点类型,他们都是num
类型的子类
String类型
"${expression}"
,如果表达式是一个标识符,可以省略{}
,如果表达式的结果为一个对象,则 Dart会调用对象的toString
方法Boolean类型
Dart是强bool类型检查,只有bool类型的值是true才被认为是true, 如果未赋初值,则为null
List类型
在Dart语言中,具有一系列相同类型的数据称为List对象。Dart中List可以直接打印出出各个元素,而java中则是地址。
Map类型
与java类似。
Set
Runs
Dart是一个面向对象的语言,所以函数也是对象,函数属于Function对象。函数可以像参数一样传递给其他函数。
定义函数时可以省略类型(不建议)
void func(a, b){} // 参数a,b都省略了类型
支持缩写语法=>
void main() => runApp(MyApp()); // 当函数体只有一条语句时可以使用此种写法
可在函数内定义函数
可以在函数里定义函数,如:
int func(int a, int b) {
int sum(int a, int b, int c) {
return a + b + c;
}
return sum(a, b, 1);
}
dart 函数支持闭包
Function addFunction(int a) {
return (y) => a + y;
}
void main() {
var addFunc = addFunction(12);
print(addFunc(22));
}
可选命名参数
使用{param1, param2,...}
的形式来指定命名参数。
main() {
func(a:1);
func(b:1);
func(a:1, b:1);
}
int func({int a, int b}) {
return a + b;
}
可选位置参数
将参数使用中括号[]括起来,用来表明是可选位置参数,必填参数要放在可选参数前面。
String getUserInfo(String name, String sex, [String from]) {}
其中name和sex是必须传入的参数,from参数可以不传
参数默认值
String getUserInfo(String name, String sex, [String from = '中国']) {}
=
或冒号:
,Dart SDK 1.21 之前只能用冒号,冒号的支持以后会移除,所以建议使用等号。=
。int fun([List list = const [1, 2, 3]]) {}
可赋值给变量,通过变量调用
main() {
var func = (int a, int b) => a + b;
func(1, 2);
}
可在其他函数中直接调用或传递给其他函数
函数别名使用typedef
表示,如:
typedef Fun1(int a, int b);
typedef Fun2(T a, K b);
Flutter应用程序必须要有一个main函数作为程序的入口函数。
Dart所有的运算符如下表所示, 与java不同的有8个,如下:
?.
,条件成员访问 和 . 类似,但是左边的操作对象不能为 null,例如 foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员。
~/
,除后取整。
as
,类型转换。
is
,如果对象是指定类型返回true。
is!
,如果对象是指定类型返回false。
??
,双问号左边为true返回左边结果,否则返回右边结果。
..
,级联语法。严格来说, 两个点的级联语法不是一个操作符。 只是一个 Dart 特殊语法。
??:
,如果左边是 null,则右边赋值给左边;如果不是 null,则左边的值保持不变。
描述 | 操作符 |
---|---|
后缀操作 | expr++ expr-- () [] . ?. |
前缀操作 | -expr !expr ~expr ++expr --expr |
乘除 | * / % ~/ |
加减 | + - |
位移 | << >> |
按位与 | & |
按位异或 | ^ |
按位或 | | |
类型操作 | >= > <= < as is is! |
相等 | == != |
逻辑与 | && |
逻辑或 | || |
是否为空 | ?? |
三目运算 | expr1 ? expr2 : expr3 |
级联 | .. |
赋值 | = *= /= ~/= %= += -= <<= >>= &= ^= |= ??= |
Dart中控制流程语句和Java类似。List和Set等实现了Iterable接口的类支持for-in遍历元素。
for-in
异常是表示发生意外错误,如果没有捕获异常,引发异常的隔离程序将被挂起,并且程序终止。
Dart代码可抛出并捕获异常,但Dart的所有异常都是未检查异常,方法不声明他们可能抛出的异常,也不需要捕获任何异常。
Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。
可以使用on 或者 catch 来声明捕获语句,也可以 同时使用。使用 on 来指定异常类型,使用 catch 来 捕获异常对象。
抛出异常
// 抛出Exception对象
throw FormatException('抛出一个formatException')
// 抛出Error对象
throw OutMemoryError();
//或自定义异常
throw '数据非法'
捕获异常
catch()
可以指定一个或两个参数来捕获异常,第一个是抛出的异常,第二个是堆栈跟踪,如:
try {
...
} on Error catch (e) { // 捕获异常详细信息
} catch (e, s) { // 堆栈跟踪信息
}
rethrow
把捕获的异常重新抛出
Finally
Dart作为高级语言支持面向对象的很多特性并且支持基于mixin
的继承方式。基于mixin的继承方式是值:一个类可以继承多个父类,相当于其他语言里的多继承,所有的类都有同一个基类Object
。
类定义中所有的变量都会隐式的定义Setter方法,针对非空的变量会额外增加getter方法
dart的构造函数有多种形式,如下:
常规构造函数
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
// 或
User(this.name, this.age);
}
命名的构造函数
class User {
String name;
int age;
User.fromJson(Map json) {
name = json['name'];
age = json['age'];
}
}
使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图。
重定向构造函数
class User {
String name;
int age;
User(this.name, this.age);
User.create(String name): this(name,12);
}
一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。
构造函数初始化列表
class User {
// final 修饰的成员变量,只能通过构造方法进行赋值
final String name;
final int age;
// 初始化列表
User(name, age)
: name = name,
age = age;
}
在构造函数体执行之前可以初始化实例参数。 使用逗号分隔初始化表达式。初始化列表非常适合用来设置 final 变量的值。
调用超类构造函数
在构造方法处使用super
关键字,如:
class User extends Person {
// final 修饰的成员变量,只能通过构造方法进行赋值
final String name;
final int age;
// 初始化列表
User(this.name, this.age):super(name,age);
}
常量构造函数
class User {
// 定义const构造函数要确保所有的成员变量都是final修饰
final String name;
final int age;
static final User user = const User('111', 12);
// const 关键字放在构造函数名前,且不能有函数体
const User(this.name, this.age);
}
工厂构造函数(单例)
class User {
String name;
//工厂构造函数无法访问this,所以这里要用static
static User _user;
//工厂方法构造函数,关键字factory
factory User([String name]) {
return User._user ??= User._(name);
}
//定义一个命名构造函数用来生产实例
User._(this.name);
}
class User {
String name;
User(this.name);
String get username => 'getter ${this.name}';
set username(String name) => this.name = name;
}
采用operator修饰,如:
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
Vector operator +(Vector v) {
return Vector(x + v.x, y + v.y);
}
}
abstract
关键字修饰class
。f类实现 call()
方法可以让类像函数一样能够被调用。
class ClassFunction {
call(String a, String b, String c) => '$a, $b, $c';
}
main() {
var cf = ClassFunction();
var out = cf('a1', 'b1', 'c1');
print('out: $out');
print(cf.runtimeType);
print(out.runtimeType);
print(cf is Function);
}
与java一致,采用enum
修饰。
enum Type {
A, B, C
}
Mixins(混入功能)相当于多继承,使用with关键字来实现Mixins的功能
class S {
a() => print('S.a');
}
class A {
a() => print('A.a');
b() => print('A.b');
}
class T = A with S;
Dart1.21开始可以使用泛型函数。泛型函数可以在以下几个地方使用类型参数:
main() {
K addCache(K key, V value) {
K temp = key;
print('${key}: ${value}');
return temp;
}
var key = addCache('key', 'value');
print(key);
}
要在使用构造函数时指定一个或多个类型,可将类型放在类名称后面的尖括号<…>中:
main() {
var p = Phone('123456');
print(p.mobileNumber);
}
class Phone {
final T mobileNumber;
Phone(this.mobileNumber);
}
实现泛型类型时,您可能希望限制其参数的类型,可以在<>
里面使用extends
。
main() {
var man = Man();
var m = User(man);
m.man.doMassage();
}
class User {
final T man;
User(this.man);
}
class Man {
void doMassage() {}
}
与java一致, 唯一区别:Java泛型是编译时的,在运行时泛型信息会被擦除,Dart的泛型类型是固化的,在运行时也可以判断具体类型。
var names = List();
print(names is List);//true
print(names.runtimeType); // List
通过import语句在一个库中引入另一个库文件:
import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package :utils/utils.dart' ;
当引用的库拥有相互冲突的名字,可以为其中一个或几个指定不一样的前缀。
import 'package:libl/ libl.dart ';
import 'package:lib2 / lib2.dart ' as lib2 ;
如果只需要使用库的一部分内容,可以有选择性地引用,有如下关键字:
// 导入 foo
import 'package:libl/libl.dart' show foo;
// 除了 foo 导入其他所有内容
import 'package:lib2 / lib2.dart' hide foo;
deferred as
导入loadLibrary()
加载库import 'dart:io' deferred as io;
lazyLoad() async {
//使用 await 关键字暂停代码执行一直到库加载完成。
await io.loadLibrary();
}
main() {
lazyLoad();
}
Dart支持异步操作,一般使用async
函数和await
表达式实现异步操作,Dart库提供asynchronous
功能,该功能提供接口来消耗时间的操作,比如文件读写,网络请求。该功能返回Future
或Stream
对象。
可以通过如下的方式来获取asynchronous功能返回的Future对象值:
可以通过如下方式来获取asynchronous功能返回的Stream对象的值:
await关键字必须在async函数内部使用,await表达式可以使用多次
void main(){
getName1();
getName2();
getName3();
}
Future getName1() async {
await getStr1();
await getStr2();
print('getName1’);
}
getStr1() {
print('getStr1’);
}
getStr2() {
print('getStr2’);
}
getName2() {
print('getName2’);
}
getName3() {
print('getName3’);
}
/// 输出结果: getStr1 getName2 getName3 getStr2 getName1
如果需要监听“完毕”这个状态,那么用whenComplete
,需要监听“成功”这个状态,用then
,需要监听“失败”这个状态,用catchError
。
如果重写了test方法,test返回true就可以在catchError的onError方法里捕获到异常,如果test返回false,就把该异常继续抛出而不会在catchError方法里被捕获,如果不写test默认实现一个返回true的test方法
void main() {
Future(() => futureTask()) //异步任务的函数
.then((m) => "result:$m") //任务执行完后的子任务
.then((m) => m.length) //其中m为上个任务执行完后的返回的结果
.then((m) => printLength(m))
.catchError(print) // 拦截错误,如果实现了test方法,只有return true时才会拦截,否则不会
.whenComplete(() => whenTaskCompelete()); //所有任务完成后的回调函数
}
whenTaskCompelete() {
print('task complete');
}
futureTask() async {
return 'future';
}
printLength(dynamic name) {
if (name is! String || name.length < 3 || name.length > 15) {
throw '长度错误';
}
}
Main Isolate
只有一个Event Looper
,但是存在两个Event Queue: Event Queue
以及Microtask Queue
。then
并没有创建新的Event丢到Event Queue
中,而只是一个普通的Function Call
,在FutureTask执行完后,立即开始执行。microtask
队列中,并且该任务会执行then()中注册的回调函数。void main(){
testFuture();
}
void testFuture() {
Future f = new Future(() => print('f1'));
Future f1 = new Future(() => null);
//Future f1 = new Future.delayed(Duration(seconds: 1) ,() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f3.then((_) => print('f2'));
f2.then((_) {
print('f3');
new Future(() => print('f4'));
f1.then((_) {
print('f5');
});
});
f1.then((m) {
print('f6');
});
print('f7');
}
import 'dart:async';
void main(){
testScheduleMicrotask();
}
void testScheduleMicrotask(){
scheduleMicrotask(() => print('s1'));
new Future.delayed(new Duration(seconds: 1), () => print('s2'));
new Future(() => print('s3')).then((_) {
print('s4');
scheduleMicrotask(() => print('s5'));
}).then((_) => print('s6'));
new Future(() => print('s7'));
scheduleMicrotask(() => print('s8'));
print('s9');
}
所有Dart代码都在隔离区内运行,而不是线程。每个隔离区都有自己的内存堆,确保不会从任何其他隔离区访问隔离区的状态。
Dart没有共享内存的并发,没有竞争的可能性所以不需要锁,也就不用担心死锁的问题。
isolate之间没有共享内存,所以他们之间的通信唯一方式只能是通过Port进行,而且Dart中的消息传递总是异步的。
isolate神似Thread,但实际上两者有本质的区别。操作系统内的线程之间是可以有共享内存的而isolate没有,这是最为关键的区别。
使用元数据给代码添加更多的信息。元数据是以@开始的修饰符,在@后面接着编译时的常量或者一个常量构造函数。
元数据可以修饰 library、class、typedef、type parameter、constructor、factory、function、field、parameter、variable declaration。
单行注释以//开头。Dart编译器会忽略//和行尾之间的所有内容。
// 这是单行注释
多行注释以/开头,以/结尾。介于/*和 */两者之间的内容会被编译器忽略(除非该注释是一个文档注释)。
/*
* 这是多行注释
* 这是多行注释
*/
多行注释可以嵌套
文档注释以///或者/**开头。可以通过dartdoc命令导出文档。
/// 这是文档注释
/**
* 这是文档注释
*/