这是我参与「第五届青训营 」伴学笔记创作活动的第 12 天
Dart作为Flutter框架使用的语言,虽然近几年不温不火,但随着Flutter框架日益成熟,以及其性能稳定性的不断优化,已经有很多人更倾向于使用Flutter构建跨端应用程序。
Dart 变量
Dart 是一个强大的脚本类语言,可以不预先定义变量类型,自动会类型推断。
Dart 中定义变量可以通过 var 关键字、类型关键字来声明变量。
var str1 = 'this is var';
String str2 = 'this is String';
int num1 = 123;
print('$str1 - $str2'); // 相当于 js 中的 ${str1} - ${str2}
注意: var 后就不要写类型,写了类型就不要 var。
字符串类型
String str = 'hello dart';
print(str);
数字类型
int myNum = 1234;
print(myNum);
常用数据类型
Numbers(数值):
- int
- double
Strings(字符串
- String
Booleans(布尔)
- bool
List(数组)
- 在Dart中,数组是列表对象,所以大多数人只是称它们为列表
Maps(字典)
通常来说,Map 是一个键值对相关的对象。 键和值可以是任何类型的对象。每个 键 只出现一次, 而一个值则可以出现多次
类型校验
var str = '';
// str = 1234; // 报错
print(str);
Dart 常量
永远不改变的量,使用 final 或 const 来修饰。
final 和 const 的区别
- const 值不变,一开始就得赋值;
- final 可以开始不赋值,只能赋一次;
- final 不仅有 const 的编译时敞亮的特性,最重要的是它是运行时常量,并且 final 是惰性初始化,即在运行时第一次使用前才初始化。
Dart 的命名规则
1、变量名称必须由数字、字母、下划线和美元符($)组成;
- 注意:标识符开头不能是数字;
- 标识符不能是保留字和关键字;
- 变量的名字是区分大小写的如: age和Age是不同的变量。在实际的运用中,也建议,不要用一个单词大小写区分两个变量;
- 标识符(变量名称)一定要见名思意 :变量名称建议用名词,方法名称建议用动词*
入口方法
入口方法的两种定义方式
main() {
print('你好 dart');
}
// void 表示 main 方法没有返回值
void main() {
print('你好 dart')
}
类型详解
字符串类型
定义方式
基本:
var str1 = 'this is str1';
var str2 = 'this is str2';
print(str1);
print(str2);
String str1 = 'this is str1';
String str2 = 'this is str2';
print(str1);
print(str2);
进阶:
String str = '''this is str1
this is str2
this is str3
''';
printf(str); // 自带换行和空格
字符串拼接
String str1 = '你好';
String str2 = 'dart';
print("$str1 - $str2");
print(str1 + ' - ' + str2);
数值类型
int
必须是整型
int a = 123;
a = 45;
print(a);
double
既可以是整形,也可以是浮点型
double b = 23.5;
print(b); // 23.5
b = 24;
print(b); // 24.0
运算符
与 JavaScript、c 等语言一致。
布尔类型
bool
bool flag1 = true;
bool flag2 = false;
print(flag1);
print(flat2);
注意: bool flag = 123 等赋值行为是错误的
if 判断语句
与 JavaScript、c 等语言一致。
List 集合(数组)类型
定义方式
1:普通方式
var list1 = ['张三', '李四', 20, true];
print(list1); // [张三, 李四, 20, true]
print(list1[0]); // 张三
2:指定类型
var list2 = <String>['张三', '李四'];
print(list2);
var list3 = <int>[1, 2, 3];
print(list3);
3:追加数据
var list4 = [];
list4.add('张三');
list4.add('李四')
4:指定长度
var list5 = List.filled(2, '');
print(list5); // [,]
list5[0] = '三三';
list5[1] = '思思';
print(list6);
// list5.add('呜呜'); // 报错
5:前缀
List list6 = ['香蕉', '苹果', '西瓜'];
print(list6);
list6.add('草莓');
注意: 通过 List.filled 创建的集合长度是固定的:
var list6 = List.filled(2, '');
// list6.length = 0; // 报错
var list7 = <String>['张三', '李四'];
print(list7);
list7.length = 0; // 可以改变
print(list7); // []
List 常用的属性和方法
常用属性:
| 属性名 | 作用 |
|---|---|
| length | 长度 |
| reversed | 反转 |
| isEmpty | 是否为空 |
| isNotEmpty | 是否不为空 |
List myList = ['香蕉', '苹果', '西瓜'];
print(myList.length);
print(myList.isEmpty);
print(myList.isNotEmpty);
print(myList.reversed); // 对列表倒序排序 (西瓜, 苹果, 香蕉)
var newMyList = myList.reversed.toList();
print(newMyList);
常见方法:
| 方法名 | 作用 | 注意 |
|---|---|---|
| add | 增加 | |
| addAll | 拼接数组 | |
| indexOf | 查找 | 传入具体值 |
| remove | 删除 | 传入具体值 |
| removeAt | 删除 | 传入索引值 |
| fillRange | 修改 | |
| insert(index, value) | 插入 | 指定位置插入数据 |
| insertAll(index, list) | 插入 | 指定位置插入list数据 |
| tolist() | 转换 | 其他类型转换成 List |
| join() | 转换 | List 转换成字符串 |
| split() | 转换 | 字符串转换为List |
| forEach/where/any/every | 遍历 |
List myList = ['香蕉', '苹果', '西瓜'];
// 1. add
myList.add('桃子'); // 增加一个数据
print(myList);
// 2. addAll
myList.addAll(['草莓', '桃子', '菠萝']);
print(myList);
// 3. indexOf
print(myList.indexOf('苹果')); // indexOf 查找数据,查找不到返回 -1,查找到返回索引值
// 4. remove
myList.remove('西瓜');
print(myList);
// 5. removeAt
myList.removeAt(1);
print(myList);
// 6. fiilRange -> 修改
List myList = ['香蕉', '苹果', '西瓜'];
// myList.fillRange(1, 2, 'aaa'); // [1, 2)
myList.fillRange(1, 3, 'aaa'); //
print(myList);
// 7. insert -> 插入一个
myList.insert(1, 'aaa');
print(myList);
// 8. insertAll -> 插入多个
myList.insertAll(1, ['aaa', 'bbb']);
print(myList);
// 7. join -> List 转换为 String
List myList = ['香蕉', '苹果', '西瓜'];
var str = myList.join('-');
print(str); // 小脚-苹果-西瓜
// 8. split -> String 装换为 List
var str = '香蕉-苹果-西瓜';
var list = str.split('-');
print(list); // ['香蕉', '苹果', '西瓜']
Map(字典)
定义方式
1:推荐
var person = {
"name": '张三',
"age": 20,
"work": ['程序员', '送外卖']
};
print(person);
print(person['name']); // 只能通过 [index] 索引
2:new Map()
var p = new Map();
p['name'] = '张三';
p['age'] = 20;
p['work'] = ['程序员', '送外卖'];
3:前缀
Map p = {
"name": "张三",
"age": 20
};
print(p);
5.5.2 Map 常见属性和方法
常用属性:
| 属性名 | 作用 |
|---|---|
| keys | 获取所有的 key值 |
| values | 获取所有的 values值 |
| isEmpty | 是否为空 |
| isNotEmpty | 是否不为空 |
// 1. kyes
Map person = {
"name": '张三',
"age": 30,
"sex": '男'
};
print(person.keys); // (name, age)
print(person.keys.toList()); // [name, age]
print(person.values); // (张三,30)
print(person.values.toList()); // [张三,30]
// 2. isEmpty、isNotEmpty
print(person.isEmpty);
print(person.isNotEmpty);
常用方法:
| 方法名 | 作用 |
|---|---|
| remove(key) | 删除指定 key的数据 |
| addAll({...}) | 合并映射,给映射内添加属性 |
| containsValue | 查看映射内的值,返回 true/false |
| forEach/where/any/every | 遍历 |
Map person = {
"name": '张三',
"age": 30,
"sex": '男'
};
// 3. addAll
person.addAll({
"work": ['敲代码', '送外卖'],
"height": 182
});
print(person);
// 4. remove
person.remove('sex');
print(person);
// 5. containsValue
print(person.containsValue('张三'));
Set
Set 是没有顺序且不能重复的集合,所以不能通过索引去获取值。用它最主要的的功能就是去除数组重复内容。
var s = new Set();
s.add('香蕉');
s.add('苹果');
s.add('苹果');
print(s); // {香蕉, 苹果}
print(s.toList()); // [香蕉, 苹果]
List myList = ['香蕉', '苹果', '西瓜','香蕉', '苹果', '西瓜','香蕉', '苹果', '西瓜'];
var s = new Set();
s.adAll(myList);
print(s); // {'香蕉', '苹果', '西瓜'}
print(s.toList()); // ['香蕉','苹果','西瓜']
List、Map、Set 公有方法
forEach、map、where、any、every
List
List myList = ['香蕉', '苹果', '西瓜'];
// for
// forEach
for(var i = 0; i < myList.length; i++) {
print(myList[i]);
};
for(var item in myList) {
print(item)
};
myList.forEach((value) {
print(value); // 香蕉\n苹果\n西瓜
})
// map
List newList = [1, 3, 4];
var newList = myList.map((value) {
return value * 2
});
print(newList); // (2, 6, 8)
print(newList.toList()); // [2, 6, 8]
// where
List myList = [1, 3, 4, 5, 6, 7, 8, 9];
var newList = myList.where((value) {
return value > 5;
});
print(myList.toList()); // [6, 7, 8, 9]
// any
List myList = [1, 3, 4, 5, 6, 7, 8, 9];
var f = myList.any((value) {
return value > 5; // 只要集合里面有满足条件的就返回 true
});
print(myList); // true
// every
var f2 = myList.every((value) {
return value > 5; // 集合里面的每一个元素都要满足条件才会返回 true
});
print(f2); // false
Map、Set
var s = new Set();
a.addAll([1, 22, 33]);
s.forEach((value) => print(value)); // 1\n22\n33
Map person = {
"name": '张三',
"age": 20
};
person.forEach((key, value) {
print('$key -- $value'); // name -- 张三
})
类型判断
is关键字判断类型
var str = '1234';
if(str is String) {
print('String');
} else if(str is int) {
printf('int');
} else {
print('others');
}
Dart 函数
函数定义、传参
// main 函数外部调用 - 全局函数
void printInfo() {
print('自定义方法')
}
String printUserInfo() {
return 'this is str';
}
List getList() {
return ['这是', 'List', '类型']
}
// void 表示没有返回值
void main() {
print('调用系统内置的方法');
printInfo();
// main 函数内部定义
int getNum() {
var myNum = 123;
return myNum;
}
var n = getNum();
print(n);
var str = printUserInfo();
print(str);
print(getList());
void xxx() {
aaa() { // 外部无法访问,局部作用域
print('aaa');
}
aaa();
}
}
实例
1-n 之和
int sum(int n) {
var sum = 0;
for(var i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
void main() {
print(sum(15));
}
可选参数
String printUserInfo(String username, [int? age, String? sex]) {
if (age != null) {
return '姓名:$username --- 年龄:$age';
}
return '姓名:$username --- 年龄:保密';
}
void main() {
print(printUserInfo('郭兴俊'));
}
实参带默认参数
String printUserInfo(String username, [int? age, String? sex = '男']) {
if (age != null) {
return '姓名:$username --- 年龄:$age --- $sex';
}
return '姓名:$username --- 年龄:保密 --- $sex';
}
void main() {
print(printUserInfo('郭兴俊'));
print(printUserInfo('cyq', 18));
print(printUserInfo('wz', 18, '女'));
}
定义一个命名参数的方法
String printUserInfo(String username, { int? age, String? sex = '男' }) {
if (age != null) {
return '姓名:$username --- 年龄:$age --- $sex';
}
return '姓名:$username --- 年龄:保密 --- $sex';
}
void main() {
print(printUserInfo('张三', age: 20)); // 不能直接写20,要写 age: 20
}
把方法当做参数
void main() {
fn1() {
print('fn1');
}
fn2(Function fn) {
fn();
}
fn2(fn1);
}
匿名函数
void main() {
var fn = () {
print('我是一个匿名方法');
}
fn();
}
6.2 箭头函数
List list = ['苹果', '香蕉', '西瓜'];
list.forEach((value) {
print(value);
});
list.forEach((value) => print(value)}) // 没有分号,只能写一句
list.forEach((value) => {
print(value); // 只能写一行
});
6.3 自执行方法
((int n) {
print('我是自执行方法$n');
})(12);
6.4 闭包
闭包:函数嵌套函数, 内部函数会调用外部函数的变量或参数, 变量或参数不会被系统回收(不会释放内存)
闭包写法:函数嵌套函数,并 return 里面的函数,这样就形成了闭包。
为什么使用闭包:
- 全局变量特点:全局变量常驻内存、污染全局
- 局部变量特点:不常驻内存,会被垃圾回收机制回收、不会污染全局
使用闭包可以常驻内存而又不污染全局:
fn() {
var a = 123; /* 不会污染全局、常驻内存 */
return () {
a++;
print(a);
};
}
Dart 类、对象
面向对象编程(OOP)的三个基本特征:封装、继承、多态
- 封装:封装是对象和类概念的主要特性。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象调用,而一部分属性和方法则隐藏。
- 继承:面向对象编程(OOP)语言的一个主要功能就是"继承"。继承是指一种能力:它可以使用现有类的功能,并在无须重新编写原来的类的情况下对这些功能进行扩展。
- 多态:允许将子类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果。
Dart 所有的东西都是对象,所有的对象都继承自 Object类。
Dart 是一门使用类和单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是 Object 的子类。
一个类通常由 属性 和 方法 组成。
class 声明
class Person {
Stream name = '张三';
int age = 23;
void getInfo() {
// print('类中的方法');
// print('$name -- $age');
print('${this.name} -- ${this.age}');
}
void setInfo(age) {
this.age = age;
}
}
void main() {
// 实例化
// var p1 = new Person();
Person p1 = new Person();
print(p1.name);
print(p1.getInfo());
p1.setInfo(25); // 传入年龄
}
构造函数
class Person {
String name;
int age;
/*
Person(String name, int age) {
print('这个方法在Person对象实例化后触发');
this.name = name;
this.age = age;
}
*/
// => 构造函数简写
Person(this.name, this.age);
void getInfo() {
// print('类中的方法');
// print('$name -- $age');
print('${this.name} -- ${this.age}');
}
}
void main() {
Person p1 = new Person('王五', 25);
p1.getInfo();
}
命名构造函数
构造函数可以写多个
class Person {
Stream name = '张三';
int age = 23;
Person(this.name, this.age);
Person.now() {
print('我是命名构造函数');
}
Person.setInfo(String name, int age) {
this.name = name;
this.age = age;
}
}
void main() {
Person p1 = new Person(); // 默认实例化类的时候调用的是,默认构造函数
Person p2 = new Person.now('王五', 55); // 命名构造函数,
}
将类单独抽离成一个模块
Dart 中的私有方法和私有属性
Dart 和其他面向对象语言不一样,Data中没有 public、private、protected 这些访问修饰符,但是我们可以使用 _ 把一个属性或方法定义成私有。
但是必须抽离为 单个文件 才可以生效。
class Animal {
String _name;
int age;
Animal(this._name, this.age);
void printInfo() {
print('${this._name} -- ${this.age}');
}
String getName() {
return this._name;
}
void _run() {
print('这是一个私有方法');
}
execRun() {
this._run(); // 类里面方法的互相调用
}
}
void main() {
Animal a = new Animal('小狗', 3);
// print(a._name); // 错误
print(a.getName());
a._run(); // 错误
a.execRun(); // 间接地调用私有方法
}
get 和 set 修饰符
需求:获取正方形面积
普通实现
class Rect {
num height;
num width;
area() {
return this.height * this.width;
}
Rect(this.height, this.width);
}
void main() {
Rect r = new Rect(10, 4);
print('面积:${r.area()}');
}
get 和 set 实现
class Rect {
num height;
num width;
get area {
return this.height * this.width;
}
set areaHeight(value) {
this.height = value;
}
Rect(this.height, this.width);
}
void main() {
Rect r = new Rect(10, 4);
// print('面积:${r.area}'); // 注意调用调用直接通过访问属性的方式访问 area
r.areaHeight = 2;
}
Dart 类中的初始化列表
class Rect {
num height;
num width;
Rect():height=2,width=10 {
print('${this.height} -- ${this.width}');
}
getArea() {
return this.height * this.width;
}
}
void main() {
Rect r = new Rect();
print('面积:${r.getArea()}');
}
静态成员
Dart 中的静态成员:
- 使用 static 关键字来实现类级别的变量和函数;
- 静态方法不能访问非静态成员,非静态方法可以访问静态成员。
class Person {
static String name = '张三';
static void show() {
print(name);
}
}
main() {
var p = new Person();
// p.show(); // 报错
print(Person.name); // 访问静态成员
Person.show();
}
class Person {
static String name = '张三';
int age = 20;
static void show() {
print(name);
}
void printInfo() { // 既可以访问静态成员,也可以访问非静态成员
print(name); // 访问静态成员
print(this.age); // 访问非静态成员
show(); // 调用静态方法
}
static void printUserInfo() {
print(name); // 静态属性
show(); // 静态方法
// print(this.age); // 静态方法没法访问非静态的属性
// print(age); // 静态方法没法访问非静态的属性
}
}
main() {
Person p = new Person();
P.printInfo();
}
Dart 对象操作符
Dart 中的对象操作符:
| 操作符 | 作用 | 备注 |
|---|---|---|
| ? | 条件运算符 | 了解 |
| as | 类型转换 | |
| is | 类型判断 | |
| .. | 级联操作 | 连缀(重要) |
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print('${this.name} -- ${this.age}');
}
}
main() {
// 1. ?
Person? p;
p?.printInfo();
// 2. is
if(p is Person) {
p.name = '李四';
}
if(p is Object) {
p.name = '李四';
}
p.printInfo();
// 3.as
var p1;
p1 = '';
p1 = new Person('张三', 20);
(p1 as Person).printInfo();
// 4. ..
/*
Person p1 = new Person('张三', 20);
p1.printInfo();
p1.name = '李四';
p1.age = 50;
p1.printInfo();
*/
p1..name = '李四'
..age = 50
..printInfo();
}
继承
面向对象的三大特征: 封装、继承、多态
Dart 种类的继承:
- 子类使用 extends 关键词来继承父类;
- 子类会继承父类里面可见的属性和方法,但是不会继承构造函数;
- 子类能复写父类的方法 getter 和 setter。
class Person {
String name = '张三';
num age = 20;
void printInfo() {
print('${this.name} --- ${this.age}');
}
}
class Web extends Person {
}
main() {
Web w = new Web();
print(w.name);
}
super 关键词
class Person {
String name;
num age;
Person(this.name, this.age);
Person.xxx(this.name, this.age);
void printInfo() {
print('${this.name} -- ${this.age}');
}
}
class Web extends Person {
// Web(super.name, super.age);
String sex = '';
Web(String name, num age, String sex): super(name, age) {
this.sex = sex;
}
// 如果想为Person某个静态初始化成员赋值
/*
Web(String name, num age, String sex): super.xxx(name, age) {
this.sex = sex;
}
*/
run() {
print('${this.name} -- ${this.age} -- ${this.sex}');
}
}
main() {
Person p = new Person('李四', 20);
p.printInfo();
Person p1 = new Person('李四', 20);
p1.printInfo();
Web w = new Web('张三', 12);
w.printInfo();
w.run(); // 自己方法有 run,则运行子类的 run方法;如果子类没有 run方法,则从父类中找 run方法。
}
子类复写父类
class Person {
String name;
num age;
Person(this.name, this.age);
void printInfo() {
print('${this.name} -- ${this.age}');
}
work() {
print('${this.name}在工作......');
}
}
class Web extends Person {
Web(String name, num age): super(name, age);
run() {
print('run');
}
// 复写父类的方法
@override // 可以写也可以不谢
void printInfo() {
print('姓名:${this.name} -- 年龄:${this.age}');
}
run() {
print('run');
super.work(); // 子类调用父类的方法
}
}
main() {
Web w = new Web('李四', 20);
w.printInfo();
}
Dart 中的抽象类、多态和接口
抽象类
Dart 中抽象类:Dart 抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
- 抽象类通过 abstract 关键字来定义;
- Dart 中的抽象方法不能用 abstract 声明,Dart 中没有方法体的方法我们称为抽象方法;
- 如果子类继承抽象类必须得实现里面的抽象方法;
- 如果把抽象类当做接口实现的话,必须得实现抽象类里面定义的所有属性和方法;
- 抽象类不能被实例化,只有继承它的子类可以。
extends 抽象类 和 implements 的区别:
- 如果要复用抽象类里面的方法,并且要用抽象方法
约束子类的话,我们就用 extends 继承抽象类。 - 如果只是把抽象类当做标准的话,我们就用 implements 实现抽象类。
案例: 定义一个 Animal 类,要求它的子类必须包含 eat 方法。
abstract class Animal {
eat(); // 抽象方法
run(); // 抽象方法
// 抽象类无法被实例化,只有继承它的子类才能实例化
/*
printInfo() {
print('我是一个抽象类的方法')
}
*/
}
class Dog extends Animal {
@override
eat() {
print('小狗在啃骨头');
}
@override
run() {
print('小狗在跑');
}
}
class Cat extends Animal {
@override
eat() {
print('小猫在吃鱼');
}
@override
run() {
print('小猫在跑');
}
}
main() {
Dog d = new Dog();
d.eat();
Cat c = new Cat();
c.eat();
}
多态
允许将子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果。
子类的实例赋值给父类的引用。
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
abstract class Animal {
eat();
}
class Dog extends Animal {
@override
eat() {
print('小狗在啃骨头');
}
}
class Cat extends Animal {
@override
eat() {
print('小猫在吃鱼');
}
run() {
print('小猫在跑');
}
}
main() {
Animal d = new Dog();
d.eat();
Animal c = new Cat();
// c.run(); // 虽然Cat 有run 方法,但是 Animal 没有,所以不能调用
}
接口
接口:就是约定、规范
和 Java 一样,dart 也有接口,但是和 Java 还是有区别的。
- dart 的接口没有 Interface 关键字定义接口,而是普通类或抽象类都可以作为接口被实现。。 同样适用 implements 关键字进行实现。
- 但是 dart 的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
- 因为抽象类可以定义抽象方法,普通类不可以,所以如果要实现像 Java 接口那样的方式,一般会使用抽象类。
- 建议使用抽象类定义接口。
abstract class Db {
String uri = ''; // 数据库的链接地址
add(String data);
save();
delete();
}
class Mysql implements Db {
Mysql(this.uri);
@override
add(String data) {
print('这是mysql的data方法' + data);
}
save() { return null; }
delete() { return null; }
@override
late String uri;
}
class Mssql implements Db {
Mssql(this.uri);
@override
add(String data) { return null; }
save() { return null; }
delete() { return null; }
@override
late String uri;
}
main() {
Mysql mysql = new Mysql('xxxx');
mysql.add('1234');
}
文件抽离
Dart 中 implements 实现多个接口
abstract classA {
String name;
printA();
}
abstract classB {
printB();
}
class C implements A, B {
@override
String name;
@override
printA() {
print('printA');
}
@override
printB() {
print('printB');
}
}
void main() {
C c = new C();
C.printA();
}
Dart 中的 mixins
mixins 的中文意思是混入,就是在类中混入其他功能。
在 Dart 中可以使用 mixins 实现类似多继承的功能。
因为 mixins 适用的条件,以下是 Dart2.x 中使用 mixins 的条件:
- 作为 mixins 的类只能继承自 Object,不能继承其他类;
- 作为 mixins 的类
不能有构造函数; - 一个类可以 mixins 多个 mixins 类;
- mixins 绝不是继承,也不是接口,而是一种全新的特性。
class A {
String info = 'this is A';
void printA() {
print('A');
}
void run() {
print('A Run');
}
}
class B {
void printB() {
print('B');
print('B Run');
}
}
class Person {
Object name;
num age;
Person(this.name, this.age);
void printPerson() {
print('这是 Person 类\n ${this.name} --- ${this.age}');
}
void run() {
print('Person run');
}
}
class C extends Person with A, B { // A 先,所以下面 c.run() 先执行 A中的 run(),但是 Person 中的 run不会执行
C(String name, num age): super(name, age);
}
void main() {
var c = new C('张三', 20);
c.printA();
c.printB();
print(c.info);
c.printPerson();
c.run();
}
class A {
String info = 'this is A';
void printA() {
print('A');
}
}
class B {
void printB() {
print('B');
}
}
class C with A, B {}
void main() {
var c = new C();
print(c is C); // true
print(c is B); // true
print(c is A); // true
var a = new A();
print(a is Object); // true => 所有的类都继承于 Objet 类
}
Dart 泛型、泛型方法、泛型类、泛型接口
通俗理解:泛型就是解决 类、接口、方法 的复用性、以及对不特定数据类型的支持(类型校验)
// 代码冗余
String getData1(String value) {
return value;
}
int getData2(int value) {
retun value;
}
// 不指定类型放弃了类型检查。我们现在想实现的是传入什么就返回什么。比如:传入 number 类型必须返回 number
getData(value) {
return value;
}
// 传入和返回类型都会校验
T getData<T>(T value) {
return value;
}
// 传入校验,返回不校验
getData<T>(T value) {
return value;
}
void main() {
print(getData(21));
print(getData<String>('12345'));
print(getData<int>(12));
}
泛型类
Mylist 中的 list 不仅可以增加 int类型,也可以增加 String类型
class MyList<T> {
List list = <T>[];
void add(T value) {
this.list.add(value);
}
List getList() {
return list;
}
}
main() {
MyList l1 = new MyList<String>();
l1.add('张三');
// l1.add(12);
print(l1.getList());
}
泛型接口
Dart中的泛型接口:
实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
- 定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)
- 要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
// 代码冗余
abstract class ObjectCache {
getByKey(String key);
void setByKey(String key, Object value);
}
abstract class StringCache {
getByKey(String key);
void setByKey(String key, String value);
}
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<T> implements Cache<T> {
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print('我是文件缓存, 把 key=${key} vlue=${value} 写入到了内存中');
}
}
class MemoryCache<T> implements Cache<T> {
@override
getByKey(String key) {
return null;
}
@override
void setByKey(String key, T value) {
print('我是内存缓存, 把 key=${key} vlue=${value} 写入到了内存中');
}
}
void main() {
// MemoryCache m = new MemoryCache<String>();
MemoryCache m = new MemoryCache<Map>();
m.setByKey('index', { "name": '张三', "age": 20 });
}
Dart 中的库、自定义库、系统库、第三方库
在 Dart 中,库的使用是通过 import 关键字引入的。
library 指令可以创建一个库,每个 Dart 文件就是一个库,即使没有使用 library 指令来指定。
Dart 中的库主要有三种:
| 库名 | 说明 |
|---|---|
| 自定义库 | import 'lib/xxx.dart' |
| 系统内置库 | import 'dart:math'; import 'dart:io'; import 'dart:convert' |
| Pub 包管理系统中的库 | pub.dev/packages / pub.flutter-io.cn/packages / pub.dartlang.org/flutter/ |
-
需要在自己的项目创建一个 pubspec.yaml,内容如下;
name: 'xxx'description: A new flutter module project.dependencies:http: ^0.12.0+2data_format: ^1.0.6 -
在 pubspec.yaml 文件配置名称。描述、依赖等信息;
-
然后运行 pub get 获取包下载到本地;
-
项目中引入库 import 'package:http/http.dart' as http; 看文档使用。
引入库的方法
导入自己的库
import 'lib/Animal.dart';
导入系统内置库
import 'dart:math';
导入系统内置库实现请求数据
async 和 await
这两个关键字的使用只需要记住两点:
只有async方法才能使用await关键字调用方法
如果调用别的async方法必须使用await关键字
async是让方法变成异步。
await是等待异步方法执行完成。
import 'dart:io';
import 'dart:convert';
void main() async{
var result = await getDataFromZhihuAPI();
print(result);
}
//api接口: http://news-at.zhihu.com/api/3/stories/latest
getDataFromZhihuAPI() async{
//1、创建HttpClient对象
var httpClient = new HttpClient();
//2、创建Uri对象
var uri = new Uri.http('news-at.zhihu.com','/api/3/stories/latest');
//3、发起请求,等待请求
var request = await httpClient.getUrl(uri);
//4、关闭请求,等待响应
var response = await request.close();
//5、解码响应的内容
return await response.transform(utf8.decoder).join();
}
Dart 导入 Pub 包管理系统中的库
pub包管理系统:
- 从下面网址找到要用的库
- 创建一个pubspec.yaml文件,内容如下
name: xxx
description: A new flutter module project.
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
`http: ^0.13.5`
- 配置dependencies
- 运行pub get 获取远程库
- 看文档引入库使用
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';
Dart 库的重命名(解决命名冲突)
冲突解决:
当引入两个库中有相同名称标识符的时候,如果是java通常我们通过写上完整的包名路径来指定使用的具体标识符,甚至不用import都可以,但是Dart里面是必须import的。当冲突的时候,可以使用as关键字来指定库的前缀。如下例子所示:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
部分导入
如果只需要导入库的一部分,有两种模式:
模式一:只导入需要的部分,使用show关键字,如下例子所示:
import 'package:lib1/lib1.dart' show foo;
模式二:隐藏不需要的部分,使用hide关键字,如下例子所示:
import 'package:lib2/lib2.dart' hide foo;
延迟加载
也称为懒加载,可以在需要的时候再进行加载。
懒加载的最大好处是可以减少APP的启动时间。
懒加载使用deferred as关键字来指定,如下例子所示:
import 'package:deferred/hello.dart' deferred as hello;
当需要使用的时候,需要使用loadLibrary()方法来加载:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
Null safety 以及可控类型、非空断言
Null safety 翻译成中文的意思是空安全。
Null safety 可以帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能。
Flutter2.2.0 (2021年5月19日发布),之后的版本都要求使用 null safety
| 类型 | 作用 |
|---|---|
| ? | 可空类型 |
| ! | 类型断言 |
可空类型
void main() {
String? username = '张三'; // String? 表示 username 是一个可空类型
username = null;
print(username);
}
List<String>? list = ['1', '2'];
list = null;
print(list);
String? getData(apiUrl) {
if(apiUrl != null) {
return 'this is server data';
}
return null
}
void main() {
print(getData('xxx'));
print(getData(null));
}
类型断言
String? str = 'this is str';
str = null;
print(str!.length); // 类型断言:如果str不等于null,会答应str的长度,如果str等于null,抛出异常
late 关键字
late 关键词主要用于延迟初始化。
class Person {
late String name; // 不加 late,namename必须有初始值
late int age;
void setName(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return '${this.name} -- ${this.age}';
}
}
void main() {
Person p = new Person();
p.setName('张三', 20);
print(p.getName());
}
required 关键词
最开始 @required 是注解
现在它已经作为内置修饰符。
主要用于允许根据需要标记任何命名参数(函数或类),使得它们不为空。因为可选参数中必须有个 required
String printUserInfo(String username, { required int age, required String sex }) { // 形参,也可以不加required,写成 int age = 10 指定默认值。
if(age != 0) {
return '姓名:$username -- 性别:$sex -- 年龄:$age';
}
return '姓名:$username -- 性别:$sex -- 年龄:保密';
}
void main() {
print(printUserInfo('张三', age: 20, sex: '未知' ));
}
// name可以传入可以不传入 age必须传入
class Person {
String? name; // 表示可空属性
late int age;
Person({ this.name, required this.age });
String getName() {
return '${this.name} -- ${this.age}';
}
}
void main() {
Person p = new Person(
name: '张三',
age: 20
);
print(p.getName());
}