文章内容是我在学习Flutter过程中对知识点的梳理和总结。如有不对的地方,欢迎指出。
本文承接Dart语言基础,看完这篇文章就够了(一),进一步了解Dart语法知识。
1 流程控制语句
- if else
- for, forEach, for-in
- while , do-whild
- break,continue
- switch case
var collection = [0, 1, 2];
//forEach
collection.forEach((item) => print('forEach: $item'));
//for-in遍历元素
for (var item in collection) {
print('for-in: $item');
}
2 异常
- Dart 提供了 Exception 和 Error 类型, 以及一些子类型。还可以定义自己的异常类型。但是,Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。
2.1 异常-类型
- Exception类型
- Error类型
2.2 异常-抛出 throw
- 所有的 Dart 异常是非检查异常。 方法不一定声明了他们所抛出的异常, 并且你不要求捕获任何异常。
- Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。
// 抛出Exception 对象
// throw new FormatException(‘格式异常');
// 抛出Error 对象
// throw new OutOfMemoryError();
// 抛出任意非null对象
// throw '这是一个异常';
2.3 异常-捕获 try catch
- 可以使用on 或者 catch 来声明捕获语句,也可以 同时使用。使用 on 来指定异常类型,使用 catch 来 捕获异常对象。
- catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 (一个 StackTrace 对象)。
- 可以使用rethrow把捕获的异常重新抛出。
try {
throw new NullThrownError();
// throw new OutOfMemoryError();
} on OutOfMemoryError {
//on 指定异常类型
print('没有内存了');
// rethrow; //把捕获的异常给 重新抛出
} on Error {
//捕获Error类型
print('Unknown error catched');
} on Exception catch (e) {
//捕获Exception类型
print('Unknown exception catched');
} catch (e, s) {
//catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为StackTrace对象堆栈信息
print(e);
print(s);
}
}
3 类
3.1 普通构造函数
Java中写法
class Point {
double x;
double y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Dart建议写法
class Point {
num x;
num y;
Point(this.x, this.y);
}
var p = new Point(1, 1); //new 可省略 var point = Point(1, 2);
print(p.x);
// Point p1;
// print(p1.runtimeType); //可以使用Object类的runtimeType属性,获取对象的类型
3.2 命名构造函数
- 使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图。
class Point {
int x;
int y;
Point(this.x, this.y);
//命名构造函数
Point.mingMing(Map map) {
this.x = map['x'];
this.y = map['y'];
}
}
调用如下:
p = Point.mingMing({'x': 2, 'y': 2});
print(p.x);
3.3 重定向构造函数
- 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。
class Point {
int x;
int y;
Point(this.x, this.y);
Point.mingMing(Map map) {
this.x = map['x'];
this.y = map['y'];
}
//重定向构造函数
Point.chongDingXiang(num x) : this(x, 0);
}
调用如下:
var p = Point.chongDingXiang(3);
print(p.x);
3.4 初始化列表
- 在构造函数体执行之前可以初始化实例参数。 使用逗号分隔初始化表达式。
- 初始化列表非常适合用来设置 final 变量的值。
import 'dart:math';
class Point {
//final变量不能被修改,必须被构造函数初始化
final num x;
final num y;
final num distanceFromOrigin;
//初始化列表
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
3.5 调用超类构造函数
- 超类命名构造函数不会传递,如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数;
- 如果超类没有默认构造函数, 则你需要手动的调用超类的其他构造函数;
- 调用超类构造函数的参数无法访问 this。
- 在构造函数的初始化列表中使用 super(),需要把它放到最后。
class Parent {
int x;
int y;
Parent.f(x, y)
: x = x,
y = y {
print("父类命名构造函数");
}
}
class Child extends Parent {
int x;
int y;
Child(x, y)
: x = x,
y = y,
super.f(x, y) {
print('子类默认构造函数');
}
Child.f(x, y)
: x = x,
y = y,
super.f(x, y) {
print("子类命名构造函数");
}
}
调用:
var c = Child(1, 2);
print(c);
运行结果:
3.6 常量构造函数
- 定义const构造函数要确保所有实例变量都是final。
- const关键字放在构造函数名称之前。
class Point2 {
//定义const构造函数要确保所有实例变量都是final
final num x;
final num y;
static final Point2 origin = const Point2(0, 0);
//const关键字放在构造函数名称之前,且不能有函数体
const Point2(this.x, this.y);
}
调用:
Point2 p =const Point2(1, 2);
var p2 =const Point2(1, 2);
print(identical(p, p2));
输出结果:true
3.7 工厂构造函数
- 工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象。
- 如果一个构造函数并不总是返回一个新的对象,则使用 factory 来定义这个构造函数。
- 工厂构造函数无法访问this。
class Singleton{
String name;
//工厂构造函数无法访问this,所以这里要用static
static Singleton _cache;
//工厂方法构造函数,关键字factory
// factory Singleton({String name = 'name'}){
// if(Singleton._cache == null){
// Singleton._cache = Singleton._newObject(name);
// }
// return Singleton._cache;
// }
//注释内容简写
factory Singleton([String name = 'name']) =>Singleton._cache ??=Singleton._newObject(name);
//定义一个命名构造函数用来生产实例
Singleton._newObject(this.name);
}
调用:
var s = Singleton('555');
var s2 = Singleton('666');
print(identical(s, s2));
运行结果:true
3.7 Setter和Getter
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
调用:
var rect = new Rectangle(1, 1, 10, 10);
print(rect.left);
rect.right = 12; //调用set
print(rect.left);//调用get
3.8 抽象类
- 不能被实例化,除非定义一个工厂构造函数。
- 抽象类通常用来定义接口, 以及部分实现。
- 抽象类通常具有抽象方法,抽象方法不需要关键字,以分号结束即可。
- 接口方式使用时,需要重写抽象类的成员变量和方法,包括私有的。
- 一个类可以implement一个普通类。Dart任何一个类都是接口。
- 一个类可以implement多个接口。
两种实现方式:
- 工厂模式implement示例如下:
void main() {
var footM = new Massage("foot");
var backM = new Massage("back");
var specialM = new Massage("special");
footM.doMassage();
backM.doMassage();
specialM.doMassage();
}
abstract class Massage {
//工厂模式
factory Massage(String type) {
switch (type) {
case "foot":
return FootM();
case "back":
return BackM();
case "special":
return specialM();
}
}
void doMassage();
}
class FootM implements Massage {
@override
void doMassage() {
print("FootM ---- doMassage()");
}
}
class BackM implements Massage {
@override
void doMassage() {
print("BackM ---- doMassage()");
}
}
class specialM implements Massage {
@override
void doMassage() {
print("specialM ---- doMassage()");
}
}
- 顶级函数extends示例如下:
void main() {
var footM = massageFactory("foot");
footM.doMassage();
}
//顶级函数
Massage massageFactory(String type) {
switch (type) {
case "foot":
return FootM();
case "back":
return BackM();
case "special":
return specialM();
}
}
//抽象类
abstract class Massage {
void doMassage() {
print("Massage");
}
}
class FootM extends Massage {
@override
void doMassage() {
print("FootM ---- doMassage()");
}
}
class BackM extends Massage {
@override
void doMassage() {
print("BackM ---- doMassage()");
}
}
class specialM extends Massage {
@override
void doMassage() {
print("specialM ---- doMassage()");
}
}
3.9 可调用类
实现call() 方法可以让类像函数一样能够被调用。
void main(){
var cf = new ClassFunction();
var out = cf("tutou","flutter","laowang");
print('$out');
print(cf.runtimeType);
print(out.runtimeType);
print(cf is Function);
}
class ClassFunction {
call(String a, String b, String c) => '$a $b $c!';
}
运行结果:
3.10 重载操作符
- 关键字operator
示例如下:
void main() {
var p1 = Person("小王", 18, 170);
var p2 = Person("老王", 28, 180);
if (p2 > p1) {
print("老王比小王年龄大");
}
if (p1 < p2) {
print("老王比小王高");
}
print('老王比小王大${p2 - p1}岁');
print("老王和小王的年龄和${p1 + p2}");
}
class Person {
String name;
int age;
int height;
Person(this.name, this.age, this.height);
bool operator >(Person p) {
return this.age > p.age;
}
bool operator <(Person p) {
return this.height < p.height;
}
int operator +(Person p) {
return this.age + p.age;
}
int operator -(Person p) {
return this.age - p.age;
}
}
运行结果如下:
4 Mixin
关键字: with
- 顺序问题
1.如果2个或多个超类拥有相同签名的A方法,那么子类会以继承的最后一个超类中的A方法为准;
2.当然这是子类没有重写A方法的前提下,如果子类自己重写了A方法则以本身的A方法为准;
示例如下:
void main() {
print(AB().getMessage());
print(BA().getMessage());
print(C().getMessage());
print(CC().getMessage());
}
class A {
String getMessage() => 'A';
}
class B {
String getMessage() => 'B';
}
class P {
String getMessage() => 'P';
}
class AB extends P with A, B {}
class BA extends P with B, A {}
class C extends P with B, A {
String getMessage() => 'C'; //优先级最高的是在具体类中的方法。
}
class CC extends P with B implements A {
} //这里的implement只是表明要实现A的方法,这个时候具体实现是再B中mixin了具体实现
运行结果:
5 泛型
- Dart1.21开始可以使用泛型函数。
- 泛型函数可以在以下几个地方使用类型参数:
①函数的返回值类型;
②参数的类型;
③局部变量的类型;
5.1 泛型-与java区别
- Java中的泛型信息是编译时的,泛型信息在运行时是不存在的;
- Dart的泛型类型是固化的,在运行时也有可以判断的具体类型;
示例如下:
var names = List<String>();
print(names is List<String>);//true
print(names.runtimeType); // List<String>
注:在Java中,可以测试对象是否为List,但无法测试它是否是List<String>。
5.2 使用泛型,很多的容器对象,在创建对象时都可以定义泛型类型,跟java一样
var list = List<String>();
list.add('aaa');
list.add('bbb');
list.add('ccc');
print(list);
var map = Map<int, String>();
map[1] = 'aaaa';
map[2] = 'bbbb';
map[3] = 'cccc';
print(map);
5.3 泛型函数
void main() {
V addCache<K, V>(K key, V value) {
V temp = value;
print('key:$key--------------value:$value');
return temp;
}
var v = addCache("秃头", "老王");
}
5.4 构造函数泛型
main() {
var p = Phone<String>('123456');
print(p.mobileNumber);
}
class Phone<T> {
final T mobileNumber;
Phone(this.mobileNumber);
}
5.5 泛型限制, 通过 extends 关键字限定可泛型使用的类型
main() {
var footMassage = FootMassage();
var m = Massage<FootMassage>(footMassage);
m.massage.doMassage();
}
class Massage<T extends FootMassage > {
final T massage;
Massage(this.massage);
}
class FootMassage {
void doMassage() {
print('脚底按摩');
}
}
6 库
6.1 载入核心库
- import 后的必须参数为库 的 URI。(Uniform Resource Identifier统一资源标识符)
- 对于内置的库,URI 使用特殊的 dart: scheme。
- 对于其他的库,你可以使用文件系统路径或者 package: scheme。
示例如下图:
6.2 载入第三方库
- pubspec.yaml声明需要引用的库,使用Packages get进行拉取。
Dart开源包
调用:
6.3 载入文件
1.新建dart文件
2.载入文件
6.4 指定库前缀
-
关键字: as
-
如果两个库有冲突的标识符,可以为其中一个或两个库都指定前缀;
示例如下:
指定前缀:
6.5 选择性载入
- 关键字:show、hide
1.新建dart文件
2.选择性导入(只使用库中一部分,或者隐藏库中一部分)
6.6 延迟载入
- 使用deferred as导入;
- 使用标识符调用loadLibrary()加载库;
示例如下:
- 使用 await 关键字暂停代码执行一直到库加载完成。
- 可提高程序启动速度。
- 用在不常使用的功能。
- 用在载入时间过长的包。
- 执行 A/B 测试,例如 尝试各种算法的 不同实现。
6.7 载入自定义库
- part 可以把一个库分开到多个 Dart 文件中。
- 或者我们想让某一些库共享它们的私有对象的时候,可以需要使用part。
- import不会完全共享作用域,而part之间是完全共享的。如果说在A库中import了B库,B库import了C库,A库是没有办法直接使用C库的对象的。而B,C若是A的part,那么三者共享所有对象。并且包含所有导入。
6.8 Flutter 一些常用库
- 字体图标生成 http://fluttericon.com/
- Flutter中文网 https://flutterchina.club
- Flutter官网 https://flutter.io
- Flutter中文开发者论坛 http://flutter-dev.cn/
- Flutter|Dart语言中文社区 http://www.cndartlang.com/flutter
- Dart开源包 https://pub.dartlang.org/packages
- Dart SDK文档 https://api.dartlang.org/stable/1.24.3/index.html
- 学习资料 https://marcinszalek.pl/
- Flutter布局控件 https://juejin.im/post/5bab35ff5188255c3272c228
- Flutter开发者 http://flutter.link/
- Flutter开源APP https://itsallwidgets.com/
- 深入理解(Flutter Platform Channel )https://www.jianshu.com/p/39575a90e820
- 简书 - 闲鱼技术 https://www.jianshu.com/u/cf5c0e4b1111