Starter
笔记:
- dart2: https://www.kancloud.cn/marswill/dark2_document/709088
- coderwhy: https://toutiao.io/u/482181?f=new , https://segmentfault.com/u/coderwhy
基础数据类型
明确的声明
String str1 = 'Hello';
String str2 = "It's me";
String str3 = """Hello
World
""";
print("$str1,$str2,$str3");
var name = "Tom";
var age = 10;
var height = 120;
var msg1 = "my name is ${name}, age is ${age}, height is ${height}";
var msg2 = "name is ${name}, type is ${name.runtimeType}";
print(msg1);
print(msg2);
- 数字类型:
int
,double
可表示的范围并不是固定的,取决于运行Dart的平台 - 字符类型:
String
可使用${expression}
实现字符串和其他变量或表达式拼接,${变量}
,则{}
可省略,即$exp
布尔类型:
bool
取值为true/false,没有非0/非空即true的规则,即不能使用if(非布尔类型的变量)
,assert(非布尔类型的变量)
之类的代码var flag = true; if(flag){ // right !! print(flag); } var message="Hello"; if(message){ // wrong !! print(message); }
集合类型:
List
/Set
/Map
var nameList = ["Tom", "Lucy", "Susan"]; nameList.add("Jack"); nameList.remove("Lucy"); print( "$nameList, len: ${nameList.length}, contain Tom: ${nameList.contains('Tom')}"); var movieSet = {"a", "b", "c"}; print( "$movieSet,len: ${movieSet.length}, contain b: ${movieSet.contains('b')}"); var infoMap = {"name": "Tom", "age": 18}; print( "$infoMap,entries: ${infoMap.entries},keys: ${infoMap.keys},infoMap['age']=${infoMap['age']}"); // 去重: var charList = ['a', 'b', 'b', 'c', 'a']; var noDupCharList = Set<String>.from(charList).toList(); print( "charList:$charList, set: ${Set.from(charList)}, dup removed list: ${Set.from(charList).toList()}, noDupCharList:$noDupCharList");
类:
class
(dart 中是没有关键字来定义接口的,默认所有的class都是隐式接口)class Person { String name; Person(String name) { this.name = name; } } class SingletonPerson { final String name; const SingletonPerson(this.name); }
类型推导 (var
/ final
/ const
): 虽然没有明确指定变量类型,但变量还是有自己明确的类型的
- 声明变量 var
var score = 20; // 如果后面再设置 ` score="abc"; ` 会报错 score = 30;
声明常量 final / const
// final 运行期间确定一个值,即可以通过计算/函数获取一个值 // const 编译期间确定一个值,即须直接赋予一个值 final date = DateTime.now(); // 正确 const date = DateTime.now(); // 错误 final price = 1.88; // 如果后面再设置 ` price=2.00; ` 会报错 const address = "SuZhou"; // 如果后面再设置 ` address="ShangHai"; ` 会报错 final p1 = Person("Tom"); // = new Person("Tom"); final p2 = Person("Tom"); // = new Person("Tom"); print(identical(p1, p2)); // => false p1和p2是不同的对象 const s1 = SingletonPerson("Tom"); // = const SingletonPerson("Tom"); const s2 = SingletonPerson("Tom"); // = const SingletonPerson("Tom"); const s3 = SingletonPerson("Lucy"); // = const SingletonPerson("Lucy"); print(identical(s1, s2)); // => true s1和s2是同一个对象 print(identical(s1, s3)); // => false s1和s3不是同一个对象
object vs dynamic vs var
// 注: 关键字(var/const/final/...) vs 类型 (String,Object,dynamic,...)
Object a = "Hello";
// print("$a,${a.substring(3)}"); // 无法调用 String的substring 方法,编译时即报错了
dynamic b = "Hello";
print("$b, ${b.substring(3)}"); // 可调用 String的substring 方法,编译时不报错,但存在安全隐患,可能调用了一个不存在的方法,运行时报错
//print("$b, ${b.say()}"); // 编译时不报错,运行时报错
var c = "Hello";
print("$c,${c.substring(3)}"); // 可调用 String的substring 方法
// print("$c,${c.say()}"); // 编译时就报错了
函数
函数的参数
函数的 必选参数 & 可选参数(位置可选参数,命名可选参数) 注:dart中无函数重载,即不可以声明两个同名函数
必选参数(必传,且无法设置默认参数)
void sayHello1(String name) { print(name); } sayHello1("Tom");
可选参数
位置可选参数
[]
void sayHello2(String name, [int age, double height]) { print("$name,$age,$height"); } sayHello2("Lucy"); // Lucy,null,null sayHello2("Lucy", 18); // Lucy,18,null sayHello2("Lucy", 18, 165.5); // Lucy,18,165.5
命名可选参数
{}
void sayHello3(String name, {int age, double height}) { print("$name,$age,$height"); } sayHello3("Lucy"); // Lucy,null,null sayHello3("Lucy", height: 165.5); // Lucy,null,165.5 sayHello3("Lucy", age: 18, height: 165.5); // Lucy,18,165.5
可设置默认参数
void sayHello4(String name, {int age = 16, double height = 120}) { print("$name,$age,$height"); } sayHello4("Lucy", height: 165.5); // Lucy,16,165.5
函数作为参数/返回值
函数是一等公民,即函数可以赋给一个变量,作为参数/返回值来回传递(在一般的面向对象的语言中,如Java,一般class/object是第一公民,可作为参数/返回值来回传递)
函数作为另一个函数的参数
直接使用Function -- 对函数无限制
void test1(Function foo) { foo(); // foo(20,30),...对函数无限制 } void bar([String name = "Hi"]) { print(name); } // 传入函数 - 直接使用某个定义的函数 test1(bar); // 传入函数 - 使用匿名函数 test1(() { print("test 匿名函数"); }); // 传入函数 - 使用箭头函数(这种函数体必须只有一行代码) test1(() => print("test 箭头函数"));
- 传入声明式函数,注意:对函数有限制,如参数,返回值等
// void test2(int foo(int a, int b)) { // 太长,阅读性差 // var result = foo(20, 30); // print(result); // } typedef Calcuate = int Function(int a, int b); // 函数签名 void test2(Calcuate cal) { var result = cal(20, 30); print(result); }
函数作为返回值
Calcuate getTestFunc() { return (a, b) { return a + b; }; } Function getTestFunc2() { return (a, b) { return a + b; }; } // 函数作为返回值 var ts = getTestFunc(); print(ts(20, 30)); var ts2 = getTestFunc2(); print(ts2(20, 50));
操作符
??=
void test1([String name]) {
name ??= "Lucy"; // name = (name!=null? name:"Lucy") 或等同于 [String name="Lucy"]
print(name);
}
test1();
test1("Tom");
??
void test2([String name]) {
var temp = name ?? "Lucy"; // temp = ( name!=null? name:"Lucy")
print(temp);
}
test2();
test2("Tom");
::
级联运算符
class Person {
String name;
void run() {
print("$name running");
}
void eat() {
print("$name eating");
}
}
main(List<String> args) {
var p1 = Person();
p1.name = "Tom";
p1.run();
p1.eat();
print("---------------------");
var p2 = Person()
..name = "Lucy"
..run()
..eat();
}
类
构造,初始化
构造方法
- 没有明确指定构造方法时,将默认拥有一个无参的构造方法
- 有了自己的构造方法时,默认的构造方法将会失效
Dart本身不支持函数的重载 => 创建相同名称的构造方法 => Solution: 命名构造方法
class Person { String name; int age; double height; // 1. 构造函数语法糖 // Person(String name, int age) { // this.name = name; // this.age = age; // } // => 等同于: Person(this.name, this.age); // 2. 命名构造方法(名称可自定义) Person.withArguments(this.name, this.age, this.height); Person.fromMap(Map<String, dynamic> map) { this.name = map['name']; this.age = map['age']; this.height = map['height']; } // 3. 重定向构造函数:在一个构造方法中去调用另外一个构造方法,冒号后面使用this调用 Person.fromName(String name) : this(name, 20); Person.fromName2(String name) : this.withArguments(name, 30, 166.8); // 重写toString方法 @override String toString() { return "$name,$age,$height"; } } main(List<String> args) { var p1 = Person("Tom", 18); var p2 = Person.withArguments("Lucy", 16, 165); // var p3 = Person.fromMap({"height": 170); // wrong !! Unhandled exception: type 'int' is not a subtype of type 'double' var p3 = Person.fromMap( {"age": 20, "name": "Susan", "height": 170.5, "sex": "female"}); print("p1 : $p1 , p2 : $p2 , p3 : $p3 "); var p4 = Person.fromName("Kelly1"); var p5 = Person.fromName2("Kelly2"); print("p4 : $p4 , p5 : $p5"); }
初始化列表(Initializer list)
class FinalPerson { final String name; final int age; // FinalPerson(this.name) { // this.age = 10; // wrong !! age是final类型,编译不通过 // } // => 正确写法: FinalPerson(this.name) : this.age = 10; //FinalPerson.withOptional(this.name, {this.age = 10}); // right !!,可使用表达式 FinalPerson.withOptional(this.name, {int age}) : this.age = age ?? 10; // 推荐,后面可跟多条语句,`,`分隔 // 重写toString方法 @override String toString() { return "$name,$age"; } } var fp1 = FinalPerson("Tom"); print("fp1 : $fp1"); var fp2 = FinalPerson.withOptional("Lucy"); print("fp2 : $fp2"); var fp3 = FinalPerson.withOptional("Susan", age: 20); print("fp3 : $fp3");
常量构造
/* 注意: 1. 拥有常量构造方法的类中,所有的成员变量必须是final修饰 2. 为了可以通过常量构造方法,创建出相同的对象,不再使用 new关键字,而是使用const关键字 (如果是将结果赋值给const修饰的标识符时,const可以省略) */ class ConstPerson { final String name; const ConstPerson(this.name); } // var cp1 = const ConstPerson("Jack"); // var cp2 = const ConstPerson("Jack"); // print(identical(cp1, cp2)); // => true const cp1 = ConstPerson("Jack"); // 等同于 var cp1 = const ConstPerson("Jack"); const cp2 = ConstPerson("Jack"); print(identical(cp1, cp2)); // => true
工厂构造
factory
可以手动返回一个对象(普通的构造函数不能手动返回)class FactoryPerson { String name; String color; FactoryPerson(this.name, this.color); // eg 需求: 希望name或者color相同时返回的是同一个对象 static final Map<String, FactoryPerson> _nameCache = {}; static final Map<String, FactoryPerson> _colorCache = {}; factory FactoryPerson.withName(String name) { if (_nameCache.containsKey(name)) return _nameCache[name]; else { final p = FactoryPerson(name, "default"); _nameCache[name] = p; return p; } } factory FactoryPerson.withColor(String color) { if (_colorCache.containsKey(color)) return _colorCache[color]; else { final p = FactoryPerson("default", color); _colorCache[color] = p; return p; } } } var facP1 = FactoryPerson.withName("Mark"); var facP2 = FactoryPerson.withName("Mark"); print(identical(facP1, facP2)); // => true
getter/setter
Dart中类定义的属性是可以直接被外界访问的,但通过使用setter和getter,可以监控这个类的属性被访问的过程
// _xxx 下划线,当前模块中可使用
class Person {
String name;
// setter - `set`
// set setName(String name) {
// this.name = name;
// }
set setName(String name) => this.name = name;
// getter - `get`
// String get getName {
// return this.name;
// }
String get getName => this.name;
}
final p = Person();
p.name = "Tom";
print(p.name);
p.setName = "Jack";
print(p.getName);
继承,抽象,接口,混入类
继承类 extends
class Animal { int age; Animal(this.age); void eating() { print("Animal is eating"); } } class Cat extends Animal { String name; Cat(this.name, int age) : super(age); @override void eating() { print("$name is eating"); } } var c = Cat("Miao", 2); c.eating();
抽象类 abstract
//(不能实例化,但可通过factory构造函数实例化) // 参看 Map抽象类, 有个extenal factory Map(); // external: 将方法的声明和实现分离,用个注解@patch在其他地方对方法进行实现 => 好处: 针对不同平台,可有不同的实现 // map_patch.dart abstract class Shape { // 抽象方法 (无方法体) void getArea(); // 普通方法(有方法体) void getInfo() { print("Shape"); } } // Dart 只支持单继承,不能多继承,即只能extends一个类 class Rectangle extends Shape { // 必须实现抽象方法 @override void getArea() { print("Rectangle getArea!"); } } var s = Rectangle(); s.getArea();
接口 implements(Dart中,默认情况下所有类都是接口)
class Runner { void running() { print("Running"); } } // Dart中可以implements多个类,但必须重新实现@override其中的所有方法 class Wind implements Runner { @override void running() { print("Wind is running"); } } var w = Wind(); w.running();
混入类(Mixin定义类,with混入类)
// 可复用之前类的原有实现方案 mixin Swimer { void swiming() { print("Swiming"); } } mixin Flier { void flying() { print("Flying"); } } class SpaceShip with Swimer, Flier { @override void swiming() { print("SpaceShip can swiming!"); } } var ss = SpaceShip(); ss.flying(); ss.swiming();
枚举
/*
常见属性:
index: 用于表示每个枚举常量的索引, 从0开始
values: 包含每个枚举值的List.
注意:
不能子类化、混合或实现枚举
不能显式实例化一个枚举
*/
enum Colors { red, green, blue }
print(Colors.blue);
print(Colors.green.index);
print(Colors.values);
泛型
class Location<T extends num> {
T x;
T y;
Location(this.x, this.y);
@override
String toString() {
return "$x,$y";
}
}
T getListFirst<T>(List<T> list) {
return list[0];
}
var names = ["Tom", "Lucy"];
var first = getListFirst(names);
print("$first,${first.runtimeType}");
var ages = [20, 30];
var a = getListFirst(ages);
print("$a,${a.runtimeType}");
print("---------------------------");
var l1 = Location(10, 20);
print(l1);
var l2 = Location<int>(20, 30);
print(l2);
var l3 = Location<double>(20.5, 30.9);
print(l3);
//var l4 = Location<String>("20", "39"); // wrong !!
库
Dart中任何一个dart文件都是一个库,即使没有用libary声明(库拆分: part/export - 推荐使用export)
// mathUtils.dart: int sum(int num1, int num2) { return num1 + num2; } // dateUtils.dart: String dateFormat(DateTime date) { return "2020-12-12"; } // utils.dart: library utils; export "mathUtils.dart"; export "dateUtils.dart"; // test_libary.dart: import "lib/utils.dart"; main(List<String> args) { print(sum(10, 20)); print(dateFormat(DateTime.now())); }
导入库 import (Dart中任何一个dart文件都是一个库)
import '库所在的uri';
- 库 URI 三种不同的形式:
- dart标准库 eg: 'dart:io','dart:html','dart:math' ('dart:core'默认已导入)
- 相对路径 eg: 'lib/student/student.dart'
- Pub包管理工具管理的库(包括自己的配置以及一些第三方的库) eg: 'package:flutter/material.dart'
show
/hide
显示/隐藏某个成员 eg: import 'lib/student/student.dart' show Student, Person;as
命名空间,解决命名冲突 eg: import 'lib/student/student.dart' as Stu; Stu.Student s = new Stu.Student();
future
https://www.imooc.com/article/305495
Future<String> testFuture() {
// return Future.value("Hello");
return Future.error("haha 创造个error");// => onError会打印【代码中有onError回调,catchError不会执行】
// throw "an Error"; // => 代码会直接异常,不会走 catchError方法,因为throw返回的数据类型不是Future<T>类型
}
testFuture().then((value){
print(value);
},onError: (e) { // onError为可选参数,代码异常时会被调用【代码中有onError回调,catchError不会执行】
print("onError: $e");
}).catchError((e){
print("catchError: $e");
});
// 类似于java中try{}catch(){}finally{}异常捕获的finally
Future.error("haha make a error")
.then(print)
.catchError(print)
.whenComplete(() => print("Done!!!"));
//执行结果:
//haha make a error
//Done!!!
testAsync() async {
var result = await Future.delayed(Duration(milliseconds: 2000), ()=>Future.value("hahha"));
print("time = ${DateTime.now()}");
print(result);
}
print("time start = ${DateTime.now()}");
testAsync();
print("time end= ${DateTime.now()}");
//执行结果:
//time start = 2019-05-15 19:24:14.187961
//time end= 2019-05-15 19:24:14.200480
//time = 2019-05-15 19:24:16.213213
//hahha
Future.delayed(Duration(milliseconds: 3000), () => "hate")
.timeout(Duration(milliseconds: 2000))
.then(print)
.catchError(print);
//TimeoutException after 0:00:00.002000: Future not completed