一、dart概述
Dart简介
- Dart 是谷歌开发的,类型安全的,面向对象的编程语言,被应用于Web、服务器、移动应用和物联网等领域。
- Dart 诞生于 2011 年 10 月 10 日
- Dart简单易学(类似TypeScript, 是强类型的语言)
- 运行方式
- 原生虚拟机(Dart 代码可以运行在 Windows, Mac、 Linux 上)
- JavaScript 引擎(Dart代码可以转成 JS代码,然后运行在浏览器上)
dart环境搭建
- 从flutter 1.21版本开始,flutter SDK会同时包含完整的dart SDK
- 官网介绍:https://dart.cn/get-dart
Dart环境搭建-绑定环境变量
- 先确定 Flutter SDK 的安装路径
- 例如:我的 Flutter SDK 安装路径是 D:\flutter
- D:\flutter\bin
- Flutter 和 Dart 的相关的执行命令
- D:\flutter\bin\cache\dart-sdk\bin
- Dart SDK的相关命令的执行路径
- D:\flutter. pub-cache\bin
- Pub 下载的公共模块的执行路径
Dart资源网站
官网
英文 https://dart.dev/
中文 https://dart.cn/
在线运行代码
https://dartpad. dartlang. org
https://dartpad.cn/
生态
• https://pub.dev/
二、dart 基础语法
语法基础
- 注释语法与 JS 一致
- 声明函数不需要关键字 (Js 中通过 function 关键字来声明函数)
- 函数和参数前面都有类型声明,void表示没有返回值,int是整型数
- 字打印使用 print ( Js 使用 console. log() )
- 每行代码结束时,必须写结束分号(😉
- 字符串通过引号包起来,支持模板字符串
- main 是入口函数, Dart应用程序总是从 main 函数开始执行
- 用 var 声明的变量,其数据类型是动态的
语法基础-运行
-
在命令行中运行
- dart hello.dart
-
Dart 执行文件中的main函数
-
输出Hello,World
语法基础 - 注释
单行注释
// 我是单行注释
·多行注释
/* 我是多行注释*/
文档注释
/// 我是文档注释
可以通过 dartdoc将注释转成文档(文档注释支持 markdown 语法)
语法基础-变量
- 变量是一个引用,Dart万物皆对象,变量存储的是对象的引用声明变量
- 明确指定类型: int age = 18;
- 不明确类型: var age = 18; 或 dynamic age = 18;
- 变量名大小写敏感(age与 Age是两个不同的变量)
- 变量默认值是 null (Js 中变量默认值是 undefined)
- Dart变量的值不会进行隐式转换(null不会自动转成false)
语法基础-常量
- 常量就是值不可变的变量(一旦声明,其值不能更改)
- 声明常量
- const age = 18;
- final age = 18;
- const 与 final 的区别
- const time = DateTime. now(); // 报错 -无法将运行时的值分配给const 变量
- final time = DateTime.now(); / 成功 - 可以将运行时的值分配给
三、数据类型 Number
- Dart 中的数字由三个关键字描述
- num 数字类型(既可以是整数,也可以是小数)
- int 表示整数(必须是整数)
- double 表示浮点数(既可以是整数,也可以是小数)
- 常用API
- https://api. dart. cn/stable/dart-core/num-class. html
- https://api. dart. cn/stable/dart-core/int-class. html
- https://api. dart.cn/stable/dart-core/double-class. html
void main() {
// 声明整数
int count = 3;
print(count);
// 声明浮点数
double price = 3.7;
print(price);
// 声明数值类型
num n1 = 3.7;
print(n1);
// 类型转换
print(n1.toString());
print(3.8.toInt()); // 向下取整
// 四舍五入
print(3.1415926.round());
print(3.1415926.toStringAsFixed(4));
// 返回余数
print(10.remainder(4));
// 数字比较: 0: 相同,1:大于,-1:小于
print(10.compareTo(12));
// 返回最大公约数
print(12.gcd(18));
// 科学计数法
print(1000.toStringAsExponential(1));
}
四、Dart 数据类型 - String
- 声明字符串 (String)
- 单引号、双引号都可以
- 三个引号可以声明包含换行符的字符串
- 常见API
- https://api dart.cn/stable/dart-core/String-class. html
- 正则表达式
- RegExp (r’正则表达式’)
- RegExp (r’\d+')
void main() {
// 声明字符串
// var str1 = 'Hello, World'; // 单引号
// print(str1);
// var str2 = "你好,世界"; // 双引号
// print(str2);
String str1 = 'Hello, World'; // 单引号
print(str1);
String str2 = "你好,世界"; // 双引号
print(str2);
// 通过三个引号声明字符串
String str3 = '''Hello
World
''';
print(str3);
// 常见操作
// 字符串拼接
print(str1 + str2);
print("$str1 $str2");
// 字符串的分隔
print(str1.split(''));
// 字符串的裁切
print(' abc '.trim());
// 判断字符串是否为空
print(''.isEmpty);
print(''.isNotEmpty);
// 字符串替换
print(str1.replaceAll('World', 'Dart'));
// 支持正则替换
print('h1k2d3n4n5n'.replaceAll(RegExp(r'\d+'), '_'));
// 通过正则匹配手机号
var isPhone = RegExp(r'^1\d{10}$');
print(isPhone.hasMatch('13333333333'));
print(isPhone.hasMatch('1333333333'));
// 查找字符串
print(str1.contains('e'));
// 定位字符串
print(str1.indexOf('e'));
}
五、数据类型-boolean
void main() {
// 声明布尔类型
bool flag1 = true;
print(flag1);
bool flag2 = false;
print(flag2);
// 显式地进行判断
var flag3;
if (flag3 == null) {
print('真');
} else {
print('假');
}
// 一些特殊的判断场景
var n1 = 0 / 0;
print(n1);
print(n1.isNaN);
}
六、数据类型-list
- Dart中的数组,由List对象表示。List有两种声明方式
- 字面量方式
- List list = []; // 不限定元素的数据类型
- List list = []; // 限定元素的数据类型是 int
- 字面量方式
- 构造函数方式
- List list = new List. empty(growable: true); // 不限制长度的空列
- List list = new List.filled(3, 0); // 声明指定长度的填充列表
- 扩展操作符(…)
- var list = [1, 2, 3];
- var list2 = [0, …list]; // [0, 1, 2, 3]
- 常用AP
- Ihttps://api. dart. cn/stable/dart-core/List-class. html
void main() {
// 声明List - 字面量
List l1 = ['a', 'b', 'c', 1, 2, 3];
print(l1);
List l2 = <int>[1, 2, 3]; // 限定元素类型的声明方式
print(l2);
// 通过构造函数的声明方式
var l3 = new List.empty(growable: true);
l3.add(1);
print(l3);
var l4 = new List.filled(3, 6);
print(l4);
// 扩展操作符
var l5 = [0, ...l4];
print(l5);
var l6;
var l7 = [7, ...?l6];
print(l7);
// 返回列表的长度
print(l1.length);
// 列表的反转
print(l1.reversed.toList());
// 添加元素
l3.addAll([4,5,6]);
print(l3);
// 删除元素
l3.remove(6);
print(l3);
// 根据下标删除元素
l3.removeAt(1);
print(l3);
// 在指定的位置添加元素
l3.insert(1, 9);
print(l3);
// 清空
l3.clear();
print(l3.length);
print(l3.isEmpty);
// 合并元素
List words = ['Hello', 'World'];
print(words.join('-'));
}
七、Dart数据类型 遍历List
- forEach 遍历列表
- map 遍历并处理元素,然后生成新的列表
- where () 返回满足条件的数据
- any 只要有一项满足条件,即返回 true
- every () 判断是否每一项都满足条件,都满足条件才返回 true
void main() {
var nums = [1, 2, 3];
// for 循环进行遍历
for (var i = 0; i < nums.length; i++) {
print(nums[i]);
}
// for ... in
for (var item in nums) {
print(item);
}
// forEach
nums.forEach((element) {
print(element);
});
// var newNums = [];
// for (var i = 0; i < nums.length; i++) {
// newNums.add(nums[i] * nums[i]);
// }
// print(newNums);
// map 循环之后会得到一个处理后的列表
var newNums = nums.map((e) {
return e * e;
});
print(newNums.toList());
// where() 返回符合条件的元素
// 判断数字是否是奇数
bool isOdd(n) => n % 2 == 1;
var oddNum = nums.where((element) => isOdd(element));
print(oddNum.toList());
// 使用 any() 来检测是否有奇数(至少一个)
print(nums.any(isOdd));
// 使用 every() 来判断是否都是奇数
print(nums.every(isOdd));
// 扩展
var pairs = [[1, 2], [3, 4]];
var flattened = pairs.expand((element) => element).toList();
print(flattened); // [1, 2, 3, 4]
// 折叠 - 对列表中的每一个元素,做一个累计操作
int result = nums.fold(2, (p, element) => p * element); // 2 * (1*2*3)
print(result);
}
八、数据类型-set
- Set 是一个无序的,元素唯一的集合
- Set有字面量和构造函数两种声明方式(字面量中用大括号)
- 无法通过下标取值
- 具有集合特有的操作
- 例如:求交集,并集、差集等
- 常用API
- https://api. dart. cn/stable/dart-core/Set-class. html
void main() {
// 字面量
var nums = <int>{1, 2, 3};
print(nums);
// 构造函数
var fruits = new Set();
fruits.add('香蕉');
fruits.add('苹果');
fruits.add('橘子');
print(fruits);
print(fruits.toList());
List myNums = [1, 2, 3, 3, 4];
print(myNums.toSet()); // 可以将重复的元素过滤掉
// 集合特有的操作
var caocao = new Set();
caocao.addAll(['张辽', '司马懿', '关羽']);
var liubei = new Set();
liubei.addAll(['关羽', '张飞', '诸葛亮']);
// 求交集
print(caocao.intersection(liubei));
// 求并集
print(caocao.union(liubei));
// 求差集
print(caocao.difference(liubei));
// 返回第一个元素
print(caocao.first);
// 返回最后一个
print(caocao.last);
// 集合不能通过下标取值
// print(caocao[1]);
}
九、Dart 数据类型 - Map
- Map是一个无序的键值对(key-value)映射。通常被称作哈希或字典。
- 声明方式
- var map key1: value1, key2: value2};
- var map = new Map () ;
- map[‘key’] = value;
- 常用API
- https://api. dart.cn/stable/dart-core/Map-class. htm
void main() {
// 字面量
var person = {
'name': '张三',
'age': 20
};
print(person);
// 构造函数
var p = Map();
p['name'] = '李四';
p['age'] = 22;
print(p);
// 访问属性
print(p['name']);
// 判断 Map 中的 key 是否存在
print(p.containsKey('name'));
print(p.containsKey('aaa'));
// 赋值
// 如果 key 不存在,我们才赋值(如果key已经存在,则不赋值)
p.putIfAbsent('gender', () => '男');
print(p);
p.putIfAbsent('gender', () => '女');
print(p);
// 获取 Map 中所有的 key
print(p.keys);
// 获取 Map 中所有的 value
print(p.values);
// 根据条件进行删除
p.removeWhere((key, value) => key == 'gender');
print(p);
}
十、Dart数据类型 - 其他
- Runes (符文)
- Runes 对象是一个32位字符对象。它可以把文字转换成符号表情或特定的文字
- print(‘\u{1f44d}’) =>
- https://copychar.cc/
- Symbol
- 在Dart 中符号用 #开头来表示的标识符
- dynamic
- 动态数据类型
void main() {
var str = '😀';
print(str);
print(str.length); // UTF-16
print(str.runes.length); // UTF-32
// Runes 可以将 UTF-32 字符集表示的内容转成符号
Runes input = new Runes('\u{1f680}');
print(new String.fromCharCodes(input));
// Symbol
var a = #abc;
print(a);
var b = new Symbol('abc');
print(b);
print(#abc == new Symbol('abc'));
// 声明动态类型的变量
dynamic foo = 'bar';
foo = 123;
print(foo);
}
十一、Dart运算符
- 地板除(~/)
- 类型判断运算符(is | is!)
- 避空运算符(?? | ??=)
- 条件属性访问(?.)
- 级联运算符(.)
- myObject. myMethod () ; // 返回 myMethod 的返回
- myObject.myMethod (); // 返回 myObject 对象的引用
void main() {
// 地板除
print(7 ~/ 4);
// 类型判断运算符
List list = [];
if (list is List) {
print('list is List');
} else {
print('list is not List');
}
if (list is! List) {
print('不是列表');
} else {
print('是列表');
}
// 避空运算符
print(1 ?? 3); // 返回 1
print(null ?? 12); // 返回 12
var foo;
// foo = 6;
print(foo ?? 0); // 如果 foo 是 null,则返回 0
var a;
// if (a == null) {
// a = 3;
// }
a ??= 3;
print(a);
a ??= 6; // 如果 a 不是 null。则赋值失败
print(a);
// 条件属性运算符(保护可能为空的属性)
var m = new Map();
print(m.length);
var obj;
// print(obj.length); // The getter 'length' was called on null.
print(obj?.length);
// 级联运算符
// Set s = new Set();
// s.add(1);
// s.add(2);
// s.add(3);
// s.remove(2);
// print(s);
Set s = new Set();
s..add('a')..add('b')
..add('c')
..remove('b');
print(s);
}
十二、Dart函数-声明函数
- 直接声明
- Dart 中声明函数不需要 function 关键字
- 箭头函数
- Dart中的箭头函数中,函数体只能写一行且不能带有结束的分
- Dart 中的箭头函数,只是函数的一种简写形式
- 匿名函数
- 立即执行函数
// Dart 中声明函数,不需要 function 关键字
void printInfo() {
print('Hello, World');
}
// 返回值,与函数声明的类型要一致
int getNum() {
// return 'Hello'; // 不能返回字符串类型
return 1;
}
void main() {
// 调用函数
printInfo();
print(getNum());
// 匿名函数
var myPrint = (value) {
print(value);
};
List fruits = ['苹果', '香蕉', '猕猴桃'];
fruits.forEach(myPrint);
// 箭头函数
fruits.forEach((element) => {
print(element) // 箭头函数中,不能写结束的分号(;)
});
fruits.forEach((element) => print(element));
// 立即执行函数
((int n){
print(n);
}) (17);
}
十三、Dart函数- 函数参数
- 必填参数
- 参数类型 参数名称
- 可选参数
- 放在必选参数后面
- 通过中括号包裹起来
- 带默认值的可选参数
- 命名参数
- 用大括号包裹起来
- 调用函数时,命名参数的名称与声明函数中的名称保持一致
- 函数参数
void main() {
// 必填参数
// String userInfo(String name) {
// return '你好:$name';
// }
// String res = userInfo('张三');
// 可选参数
// String userInfo(String name, [int age = 0]) {
// return '你好:$name, 年龄:$age';
// }
// String res = userInfo('张三', 20);
// print(res);
// 命名参数
String userInfo(String name, {int age = 0}) {
return '你好:$name, 年龄:$age';
}
// 命名参数调用时,需要与声明时的形参一致
String res = userInfo('张三', age: 20);
print(res);
// 函数参数
var myPrint = (value) {
print(value);
};
List fruits = ['苹果', '香蕉', '猕猴桃'];
// 将匿名函数 myPrint 传递给函数 forEach
fruits.forEach(myPrint);
}
十四、Dart 函数 - 作用域
- Dart 中闭包的实现方式与 JavaScript 中完全一致
- 使用时机:即能重用变量,又保护变量不被污染
- 实现原理:外层函数被调用后,外层函数的作用域对象(A0)被内层函数引用着,导致外层函数的作用域对象无法释放,从而形成闭包
// 全局变量
var globalNum = 100;
void main() {
printInfo() {
// 局部变量
var localNum = 10;
localNum--;
print(localNum);
print(globalNum); // 我们可以在函数作用域中,访问全局变量
}
printInfo();
// print(localNum); // 不能在全局作用域中,访问局部变量
// 闭包
printInfo();
printInfo();
parent() {
var money = 1000;
return () {
money -= 100;
print(money);
};
}
var p = parent();
p();
p();
p();
}
十五、Dart 函数- 异步函数
- JavaScript 中,异步调用通过Promise来实现
- async 函数返回一个 Promise。 await 用于等待 Promise
- Dart 中,异步调用通过 Future 来实现
- async 函数返回一个 Future, await 用于等待 Future
- Future详情
- https://api. dart. dev/stable/dart-async/Future-class. html
import 'package:http/http.dart' as http; // http请求包
import 'dart:convert'; // 解析json包
// https://httpbin.org/ip 返回 IP 地址
Future getIPAddress() {
final url = 'https://httpbin.org/ip';
return http.get(url).then((response) {
// print(response.body);
String ip = jsonDecode(response.body)['origin'];
return ip;
});
}
void main() {
// 调用 getIPAddress
getIPAddress()
.then((ip) => print(ip))
.catchError((error) => print(error));
}
async
import 'package:http/http.dart' as http;
import 'dart:convert';
// https://httpbin.org/ip 返回 IP 地址
Future getIPAddress() async {
final url = 'https://httpbin.org/ip';
final response = await http.get(url);
String ip = jsonDecode(response.body)['origin'];
return ip;
}
void main() async {
// 调用 getIPAddress
try {
final ip = await getIPAddress();
print(ip);
} catch (error) {
print(error);
}
}
十六、类-简介
- 类是通过 class 声明的代码段,包含属性和方法。
- 属性:用来描述类的变量
- 方法:类中的函数称为类的方法
- 对象是类的实例化结果 (var obj = new MyClass())
- 类是通过class 声明的代码段,包含属性和方法。
- 属性:用来描述类的变量
- 方法:类中的函数称为类的方法
- 对象是类的实例化结果(var obj = new MyClass ())
- 编程方式
- 面向对象编程(00P)
- 面向过程编程(POP)
// 声明类
class Person {
// 类的属性
String name = '张三';
// 类的方法
void getInfo() {
print('我是 $name');
}
}
void main() {
// 实例化类,然后得到一个对象
Person p = new Person();
// 访问类中的属性
print(p.name);
// 访问类的方法
p.getInfo();
// Dart 中所有的内容都是对象
Map m = new Map();
print(m.length);
m.addAll({'name': '李四', 'age': 20});
print(m.length);
}
十七、构造器(构造函数)
- 默认构造函数
- 与类同名的函数,在实例化时,自动被调用
- 命名构造函数
- 在类中使用命名构造器(类名.函数名)实现多个构造器,可以提供额外的清晰度
- 常量构造函数
- 如果类生成的对象不会改变,您可以通过常量构造函数使这些对象成为编译时常量
- 工厂构造函数
- 通过factory声明,工厂函数不会自动生成实例,而是通过代码来决定返回的实例
默认构造函数
// class Point {
// // num x;
// // num y;
// num x, y;
// // 声明普通构造函数
// Point() {
// print('我是默认构造函数,实例化时,会第一个被调用');
// // this 可以省略
// // x = 0;
// // y = 0;
// // 当命名指向有歧义时,this 不能省略
// this.x = 0;
// this.y = 0;
// }
// }
// class Point {
// num x, y;
// // 声明普通构造函数
// Point(num x, num y) {
// // 当命名指向有歧义时,this 不能省略
// this.x = x;
// this.y = y;
// }
// }
class Point {
num x, y;
// 声明普通构造函数
Point(this.x, this.y);
}
void main() {
// var p = new Point();
// print(p.x);
var p = new Point(3, 4);
print(p.y);
}
命名构造函数
class Point {
num x, y;
Point(this.x, this.y);
// 命名构造函数
Point.origin() {
x = 0;
y = 0;
}
// 命名构造函数
Point.fromJson({x: 0, y: 0}) {
this.x = x;
this.y = y;
}
}
void main() {
// 默认坐标
Point p1 = new Point.origin();
print(p1.x);
// 手动设置坐标
Point p2 = new Point.fromJson(x: 6, y: 6);
print(p2.x);
}
常量构造函数
class Point {
num x;
num y;
Point(this.x, this.y);
}
class ImmutablePoint {
// 属性必须通过 final 声明
final num x;
final num y;
// 常量构造函数,必须通过 const 声明
const ImmutablePoint(this.x, this.y);
}
void main() {
var p1 = new Point(1, 2);
var p2 = new Point(1, 2);
print(p1 == p2); // false
// 常量构造函数,可以当做普通构造函数使用。
// var p3 = new ImmutablePoint(1, 2);
// var p4 = new ImmutablePoint(1, 2);
// print(p3 == p4); // false
// // 声明不可变对象,必须通过 const 关键字
// var p5 = const ImmutablePoint(1, 2);
// var p6 = const ImmutablePoint(1, 2);
// print(p5 == p6); // true
// // 实例化时,new 关键字,可以省略。
// var p7 = ImmutablePoint(1, 2);
// var p8 = ImmutablePoint(1, 2);
// print(p7 == p8); // false
}
工厂构造函数 也是单例模式
class Person {
String name;
static Person instance;
// 工厂构造函数
factory Person([String name = '刘备']) {
// 工厂构造函数中,不能使用 this 关键字
// print(this.name);
if (Person.instance == null) {
// 第一次实例化
Person.instance = new Person.newSelf(name);
}
// 非第一次实例化
return Person.instance;
}
// 命名构造函数
Person.newSelf(this.name);
}
void main() {
// 实例化操作
Person p1 = new Person('关羽');
print(p1.name);
Person p2 = new Person('张飞');
print(p2.name);
print(p1 == p2); // true
}
十八、访问修饰
- Dart 与TypeScript不同,没有访问修饰符(public、protected, private)
- Dart类中,默认的访问修饰符是公开的(即 public)
- 如果属性或方法以 _ (下划线)开头,则表示私有(即 private)
- 只有把类单独抽离出去,私有属性和方法才起作用
- lib/Person. dart
- import ‘lib/Person. dart’
lib/Person. dart
class Person {
String name;
// 声明私有属性
num _money = 100;
Person(this.name);
num getMoney() {
return this._money;
}
// 声明私有方法
void _wife() {
print('我是 $name 的老婆');
}
}
外面引入调用
import 'lib/Person.dart';
void main() {
var p = new Person('张三');
print(p.name);
// 访问私有属性
// print(p._money);
print(p.getMoney());
// 访问私有方法
// print(p._wife());
}
十九、Getter Setter
- Getter (获取器)是通过 get关键字修饰的方法
- 函数没有小括号,访问时也没有小括号(像访问属性一样访问方法)
- Setter (修改器)是通过 set关键字修饰的方法
- 访问时,像设置属性一样给函数传参
class Circle {
final double PI = 3.1415;
num r;
Circle(this.r);
// num area() {
// return this.PI * this.r * this.r;
// }
// 使用 get 声明的方法,不能有小括号
num get area {
return this.PI * this.r * this.r;
}
// Setter
set setR(value) {
this.r = value;
}
}
void main() {
var c = new Circle(10);
// print(c.area());
// 通过 Setter 修改属性
c.setR = 20;
print(c.area);
}
二十、初始化列表
class Rect {
int height;
int width;
// Rect(this.height, this.width);
// Rect([int height = 2, int width = 10]) {
// this.height = height;
// this.width = width;
// print('${this.height} -- ${this.width}');
// }
// Rect({int height = 2, int width = 10}) {
// this.height = height;
// this.width = width;
// print('${this.height} -- ${this.width}');
// }
// 初始化列表
Rect() : height = 4, width = 20 {
print('${this.height} -- ${this.width}');
}
}
class Point {
double x, y, z;
Point(this.x, this.y, this.z);
// 初始化列表的特殊用法(重定向构造函数)
Point.twoD(double x, double y) : this(x, y, 0);
}
void main() {
var r = new Rect();
// 实例化点
var p = new Point(1,2,3);
print(p.z);
var p2 = new Point.twoD(4, 5);
print(p2.z);
}
二十一、static
- static关键字用来指定静态成员
- 通过 static 修饰的属性是静态属性
- 通过 static 修饰的方法是静态方法
- 静态成员可以通过类名称直接访问(不需要实例化)
- 实例化是比较消耗资源的,声明静态成员,可以提高程序性能
- 静态方法不能访问非静态成员,非静态方法可以访问静态成员
- 静态方法中不能使用 this 关键字
- 不能使用 this 关键字,访问静态属性
class Person {
static String name = '张三';
int age = 18;
static printInfo() {
// print(this.name); // 不能通过 this 关键字,访问静态属性
print(name);
// 静态方法中不能访问非静态属性
// print(age);
// 静态方法,不能访问非静态方法
// printUserInfo();
}
printUserInfo() {
// 非静态方法,可以访问静态属性
print(name);
print(age);
printInfo(); // 非静态方法,可以访问静态方法
}
}
void main() {
// 静态成员,可以通过类名称直接访问
print(Person.name);
print(Person.printInfo());
// 不能通过类名称,直接访问非静态方法。
// print(Person.printUserInfo());
var p = new Person();
// print(p.name); // 不能通过实例化对象去访问静态属性
print(p.printUserInfo());
}
二十二、元数据
- 元数据以 @ 开头,可以给代码标记一些额外的信息
- 元数据可以用来库,类,构造器,函数,字段,参数或变量声明的前
- @override (重写)
- 某方法添加该注解后,表示重写了父类中的同名方法
- @required (必填)
- 可以通过 @required 来注解 Dart 中的命名参数,用来指示它是必填参
- @deprecated (弃用)
- 若某类或某方法加上该注解之后,表示此方法或类不再建议使用
class Phone {
// 这是旧版本中的开机方法,会在将来的版本中移除
activate() {
turnOn();
}
turnOn() {
print('开机');
}
}
void main() {
var p = new Phone();
p.activate();
p.turnOn();
}
二十三、Dart类与对象 - 继承
- 根据类的先后顺序,可以将类分成父类和子类
- 子类通过 extends 关键字 继承 父类
- 继承后,子类可以使用父类中,可见的内容(属性或方法)
- 子类中,可以通过 @override 元数据来标记“覆写”方法
- “覆写”方法:子类中与父类中同名的方法
- 子类中,可以通过 super 关键字来引用父类中,可见的内容
- 属性
- 方法(普通构造函数,命名构造函数)
lib/father.dart
class Father {
String name = '刘备';
num _money = 10000;
String job;
Father(this.job);
// 命名构造函数
Father.origin(this.job);
say() {
print('我是 $name');
}
get getMoney {
return this._money;
}
}
lib/son.dart
import 'Father.dart';
class Son extends Father {
String name = "刘禅";
// 通过 super 继承父类的普通构造函数
Son(String job) : super(job);
// 继承命名构造函数
// Son(String job) : super.origin(job);
Son.origin(String job) : super.origin(job);
say() {
super.say();
print('我是 刘禅, 我爹是 ${super.name},他的工作${super.job}');
}
}
import 'lib/Father.dart';
import 'lib/Son.dart';
void main() {
var f = new Father('皇帝');
print(f.name);
// var s = new Son('皇帝');
var s = new Son.origin('卖草鞋的');
print(s.name);
// print(s._money); // 子类不能访问父类中的私有内容
print(s.getMoney);
s.say();
}
二十四、Dart类与对象- 抽象类
- 抽象类是用 abstract 关键字修饰的类
- 抽象类的作用是充当普通类的模板,约定一些必要的属性和方法
- 抽象方法是指没有方法体的方法
- 抽象类中一般都有抽象方法,也可以没有抽象方法
- 普通类中,不能有抽象方法
- 抽象类不能被实例化(不能被 new)
- 抽象类可以被普通类继承(extends)
- 如果普通类继承抽象类,必须实现抽象类中所有的抽象方法
- 抽象类还可以充当接口被实现(implements)
- 如果把抽象类当做接口实现的话,普通类必须得实现抽象类里面定义的所有属性和方法。
// 1. 抽象类,必须通过 abstract 关键字声明
// 2. 抽象类中,可以有抽象方法,也可以没有抽象方法,一般来说,抽象类都有抽象方法。
abstract class Phone {
// 声明抽象方法
void processor(); // 手机的处理器
void camera(); // 手机的摄像头
void info() {
print('我是抽象类中的一个普通方法');
}
}
class Xiaomi extends Phone {
// 普通类继承了抽象类,就必须实现抽象类中所有的抽象方法
void processor() {
print('骁龙888');
}
void camera() {
print('三星摄像头');
}
// 普通类中,不能有抽象方法
// void aaa();
}
class Huawei extends Phone {
// 普通类继承了抽象类,就必须实现抽象类中所有的抽象方法
void processor() {
print('麒麟990');
}
void camera() {
print('徕卡摄像头');
}
}
void main() {
// 抽象类,不能被实例化
// var p1 = new Phone();
Xiaomi m = new Xiaomi();
m.processor();
m.camera();
m.info();
Huawei h = new Huawei();
h.processor();
h.camera();
h.info();
}
二十五、Dart类与对象-接口
- 接口在Dart 中就是一个类(只是用法不同)
- 与 Java 不同, Java 中的接口需要用 interface 关键字声明; Dart 中不需要
- 接口可以是任意类,但一般使用抽象类做接口
- 一个类可以实现(implements)多个接口,多个接口用逗号分隔
- class MyClass implements Interface1, Interface2 . )
- 接口可以看成一个个小零件。类实现接口就相当于组装零件
- 普通类实现接口后,必须重写接口中所有的属性和方法
// 手机的处理器
abstract class Processor {
String cores; // 内核:2核,4核
arch(String name); // 芯片制程:7nm, 5nm
}
// 手机的摄像头
abstract class Camera {
String resolution; // 分辨率:1000万,3000万
brand(String name); // 品牌:三星,徕卡,蔡司
}
// 通过普通类实现接口
class Phone implements Processor, Camera {
String cores;
String resolution;
Phone(this.cores, this.resolution);
arch(String name) {
print('芯片制程:'+name);
}
brand(String name) {
print('相机品牌:'+name);
}
}
void main() {
Phone p = new Phone('4核', '3000万');
p.arch('5nm');
p.brand('徕卡');
}
二十六、Dart类与对象 - 混入 (Mixin)
- 混入(Mixin)是一段公共代码。混入有两种声明方式:
- 将类当作混入 class MixinA { … }
- 作为 Mixin 的类只能继承自 Object,不能继承其他类
- 作为Mixin 的类不能有构造函数
- 使用 mixin 关键字声明 mixin MixinB { . }
- 将类当作混入 class MixinA { … }
- 混入(Mixin)可以提高代码复用的效率,普通类可以通过with来使用混入
- class MyClass with MixinA, MixinB { . }
- 使用多个混入时,后引入的混入会覆盖之前混入中的重复的内容
- MixinA 和 MixinB 中都有 hellb()'方法, Myelass 会使用 MixinB 中的
class Father {
}
// class MixinA extends Father { // 用作混入的类,不能继承除了 Object 以外的其他类
// class MixinA extends Object {
class MixinA {
String name = 'MixinA';
// MixinA(); // 用作混入的类,不能拥有构造函数
void printA() {
print('A');
}
void run() {
print('A is running');
}
}
mixin MixinB {
String name = 'MixinB';
void printB() {
print('B');
}
void run() {
print('B is running');
}
}
class MyClass with MixinA, MixinB {
}
void main() {
var c = new MyClass();
c.printA();
c.printB();
// 后引入的混入,会覆盖之前引入的混入中重复的内容
print(c.name);
c.run();
}
二十七、泛型
- 泛型是在函数、类、接口中指定宽 数据类 的语法
- 泛型函数
- 泛型类
- 泛型接口
- 通常,在尖括号中,使用一个字母来代表类型, 例如 E,T,S,K,和这回簧型 函数名<输入类型>(参数类型 参数)
- 作用:使用泛型可以减少重复的代码,
// String getData(String value) {
// return value;
// }
// int getData(int value) {
// return value;
// }
// 不指定数据类型的函数
// getData(value) {
// return value;
// }
// 泛型函数
// T getData<T>(T value) {
// return value;
// }
// 只约定参数类型,不约定函数返回值的类型
getData<T>(T value) {
return value;
}
void main() {
// print(getData('Hello'));
// print(getData(10));
// 调用泛型函数
print(getData<int>(20));
print(getData<String>('Hello'));
}
class CommonClass {
Set s = new Set<int>();
void add(int value) {
this.s.add(value);
}
void info() {
print(this.s);
}
}
// 泛型类
class GenericsClass<T> {
Set s = new Set<T>();
void add(T value) {
this.s.add(value);
}
void info() {
print(this.s);
}
}
void main() {
CommonClass c = new CommonClass();
c.add(1);
c.add(2);
c.info();
// 实例化泛型类
GenericsClass g = new GenericsClass<int>();
g.add(1);
g.add(2);
g.info();
GenericsClass g1 = new GenericsClass<String>();
g1.add('Hello');
g1.add('World');
g1.info();
// Set s = new Set();
// s.add(1);
// s.add('hello');
// print(s);
// Set s = new Set<int>();
// s.add(1);
// // s.add('hello'); // 报错
// s.add(3);
// print(s);
// 字面量形式的泛型
Set s = <int>{};
s.add(1);
// s.add('hello'); // 报错
s.add(3);
print(s);
}
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> {
getBykey(String key) {
return null;
}
void setByKey(String key, T value) {
print('文件缓存: key=${key} value=${value}');
}
}
// 内存缓存
class MemoryCache<T> implements Cache<T> {
getBykey(String key) {
return null;
}
void setByKey(String key, T value) {
print('内存缓存: key=${key} value=${value}');
}
}
void main() {
// 文件缓存 - 缓存字符串
// FileCache fc = new FileCache<String>();
// fc.setByKey('foo', 'bar');
// fc.setByKey('f', 2); // 报错
// 文件缓存 - 缓存 Map
FileCache fc = new FileCache<Map>();
fc.setByKey('index', {"name": "张三丰", "age": 218});
// 内存缓存 - 缓存字符串
// MemoryCache mc = new MemoryCache<String>();
// mc.setByKey('foo', 'bar');
// 内存缓存 - 缓存集合
MemoryCache mc = new MemoryCache<Set>();
mc.setByKey('home', {1,2,3});
}
范型类型限制
class SomeBaseClass {
// ...
}
class Foo<T extends SomeBaseClass> {
String toString() => "Instance of 'Foo<$T>'";
}
// 子类
class Extender extends SomeBaseClass {
}
class AnotherClass {
// ...
}
void main() {
var someBaseClassFoo = Foo<SomeBaseClass>();
print(someBaseClassFoo);
var extenderFoo = Foo<Extender>();
print(extenderFoo);
var foo = Foo();
print(foo);
// var f = Foo<AnotherClass>(); // 类型不对,报错
// print(f);
}
二十八、枚举
- 枚举是数量固定的常量值,通过enum关键字声明
- enum Color fred, green, blue}
- 枚举的 values 常量, 可以获取所有枚举值列表
- Listcolors = Color. values;
- 可以通过 index 获取值的索引
- assert (Color. green. index == 1);
enum Color { red, green, blue}
void main() {
// 通过 index 返回枚举中具体常量的值
print(Color.green.index);
// 通过 values 返回常量列表
print(Color.values);
List<Color> colors = Color.values;
print(colors);
// 通过下标,访问列表中的内容
print(colors[0]);
// 通过 forEach 去遍历列表的内容
colors.forEach((element) {
print('value: $element, index: ${element.index}');
});
}
二十九、Dart 库与生态
- Dart 中的库就是具有特定功能的模块
- 可能包含单个文件,也可能包含多个文件。
- 按照库的作者进行划分,库可以分成三类
- 自定义库(工程师自己写的)
- 系统库(Dart 中自带的)
- 第三方库(Dart 生态中的)
- Dart 生态
- https://pub. dev/
- pub命令 (D:\flutter\bin\cache\dart-sdk\bin)
三十、Dart库与生态
1.通过 library 来声明库
2.通过 import 来引入库
3.通过 part 和 part of 来组装库
自定义库通过libarary来声明库
自定义库-通过import来引入库
- 不同类型的库,引入方式不同
- 自定义库(import ‘库的位置/库名称.dart’)
- 系统库(import ‘dart:库名称’)
- 第三方库(后面单独讲)
- 引入部分库(仅引入需要的内容)
- 包含引入(show)
- 排除引入(hide)
- 指定库的前缀
- 当库名冲突时,可以通过 as 关键字,给库声明一个前缀
- 延迟引入(懒加载)
- 使用 deferred as 关键字来标识需要延时加载的库
自定义库 lib/MyCustom.dart
// library MyCustom;
library my_custom; // 建议写成小写字母+下划线的形式
class MyCustom {
String name = "MyCustom";
static num version = 1.0;
void info() {
print('我是自定义库');
}
}
import 'lib/MyCustom.dart';
void main() {
MyCustom mc = new MyCustom();
mc.info();
print(MyCustom.version);
}
三十一、系统库的引入
import 'dart:math';
// import 'dart:core'; // core 库会被默认引入
void main() {
print(pi);
print(min(3,6));
print(max(3,6));
}
引入部分库
lib/common.dart
void f1() {
print('f1 is running');
}
void f2() {
print('f2 is running');
}
void f3() {
print('f3 is running');
}
show.dart
// show 后面指定包含引入的内容
import 'lib/common.dart' show f1, f3;
void main() {
f1();
// f2();
f3();
}
hide.dart
// hide 会隐藏后面的内容
import 'lib/common.dart' hide f1, f3;
void main() {
// f1();
f2();
// f3();
}
引入名字冲突问题
import 'lib/common.dart';
import 'lib/function.dart' as func; // 给库添加前缀,解决命名冲突问题
void main() {
f1();
func.f1();
}
异步引入
import 'lib/function.dart' deferred as func;
void main() {
// func.hello();
print('1');
greet();
print('2');
print('3');
}
Future greet() async {// Future 表示异步
await func.loadLibrary();
func.hello();
}
三十二、part 与 part of
Camera.dart
// 与主库建立联系
part of phone;
class Camera {
String name = "摄像头";
void info() {
print('我是摄像头');
}
}
Processor.dart
// 与主库建立联系
part of phone;
class Processor {
String name = "处理器";
void info() {
print('我是处理器');
}
}
main.dart
library phone;
import 'dart:math';
// 与分库建立联系
part 'Camera.dart';
part 'Processor.dart';
void main() {
Camera c = new Camera();
c.info();
Processor p = new Processor();
p.info();
print(pi);
}
使用
_part.dart
import 'lib/phone/main.dart' as phone;
void main() {
phone.main();
}
三十三、系统库
- 系统库(也叫核心库)是Dart提供的常用内置库
- 不需要单独下载,就可以直接使用
- 引入
- import ‘dart:库名’;
- import ‘dart:core’; //会自动引入(无需手动引入)
- 系统库列表
- https://dart.cn/guides/libraries
// import 'dart:core'; // 自动引入
void main() {
// 创建当前时间
var now = new DateTime.now();
print(now);
// 通过普通构造函数创建时间
var d = new DateTime(2021, 1, 20, 9, 30);
print(d);
// 创建标准时间
var d1 = DateTime.parse('2021-01-20 12:30:30');
print(d1);
var d2 = DateTime.parse('2021-01-20 12:30:30+0800');
print(d2);
// 时间增量
print(now.add(new Duration(hours: 2)));
print(now.add(new Duration(hours: -3)));
// 时间比较
print(d1.isAfter(d2)); // d1 是否在 d2 之后
print(d1.isBefore(d2)); // d1 是否在 d2 之前
print(d1.isAtSameMomentAs(d2)); // d1 与 d2 是否相同
// 时间差
var d3 = new DateTime(2021, 1, 1);
var d4 = new DateTime(2021, 1, 4);
var difference = d3.difference(d4);
print([difference.inDays, difference.inHours]); // d1与d2相差的天数与小时
// 时间戳
print(now.millisecondsSinceEpoch); // 单位毫秒,13 位时间戳
print(now.microsecondsSinceEpoch); // 单位微秒, 16 位时间戳
// 格式化
print(now.month.toString().padLeft(2, '0'));
String timestamp = "${now.year.toString()}-${now.month.toString().padLeft(2,'0')}-${now.day.toString().padLeft(2,'0')} ${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}";
print(timestamp);
}
三十四、第三方库
- 来源
- https://pub.dev https://pub. flutter-io. cn/packages
- https://pub. dartlang. org/flutter
- 使用
- 在项目目录下创建 pubspec. yam
- 在 pubspec. yaml 中声明第三方库(依赖)
- 命令行中进入 pubspec.yaml 所在目录,执行 pub get 进行安装
- 项目中引入已安装的第三方库(import ‘package:xxxxx/xxxxx.dart’)
第三方库
- 第三方库的结构
- 一个第三方库,必须包含一个 pubspec.yaml
- pubspec.yaml
- 详情 : https://dart.cn/tools/pub/pubspec
三十五、extension (扩展)
- extension 关键字在 Dart 2.7 及其以上才支持
- sdk: “>=2.7.0 ❤️.0.0”
- extension 可以扩展对象的内容
- extension StringExtension on String {// 扩展的内容}
- 扩展不仅可以定义方法,还可以定义 setter, getter, operator
- 使用
- 声明扩展
- 引入扩展
- 使用扩展(String.扩展内容)
三十六、call
- 在类中可以声明 call方法(方法名不能变)
- 当我们将类的实例,用作函数时,会自动调用call方法
三十七、noSuchMethod
- 当我们调用了一个类的,未定义的方法时,Dart 会自动调noSuchMethod
- 使用前提
- 类中声明了 noSuchMethod (否则调默认的 noSuchMethod)
- 实例化对象必须用 dynamic 来修饰
- dynamic p = Person();
- 调用的是未定义方法(p.undefinedMethod())
三十八、hashCode
- hashCode 是 Dart 对象的唯一标识
- hashCode 表现为一串数字
- Dart 中的每个对象都有hashCode
- 我们可以通过 hashCode 来判断两个对象是否相等