1、安装Dart的 jdk 【此处以Windows系统为例】
点击跳转Dart官网jdk下载地址
选择 稳定版本 或 最新版本 >> 安装 >> 查看是否jdk安装成功 dark --version
【在DOS中运行该指令】
2、安装 VS Code,在插件处添加CodeRunner、Dart 扩展
1、入口方法
main() 或 void main() 【区别在于是否具有返回值】
void main(){
var str = 'Hello Dart';
var myNum = 1234;
print(str);
print(myNum);
}
2、命名规则
3、如何定义一个常量?
(1)可以使用 const 和 final 关键字定义常量
(2)两者间的区别:
const 一开始就需要赋值,final 可以开始不赋值,只能赋值一次
final 还可以接收一个方法的返回值作为常量使用,const 不可以
蓝色波浪线代表未使用,红色波浪线代表系统错误
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0s5KStNX-1666167817408)(attachment:8bb2963e8bbe9238a61c33d93b3c4103)]
1、字符串类型(String类型)
void main(List<String> args) {
//定义字符串
var str1 = 'xiaoming';
var str2 = "xiaohong";
var str3 = """xiaozhang
xiaozhang""";
var str4 = '''
xiaoli
xiaoli
''';
print(str1 + "\t" + str2 + "\t" + str3 + "\t" + str4);
print("*******************************");
//利用String类创建字符串
String s1 = '单引号';
String s2 = "双引号";
String s3 = """
三个双引号
""";
String s4 = '''
三个单引号
''';
print(s1 + "\t" + s2 + "\t" + s3 + "\t" + s4);
}
"$str1 $str2"
的形式拼接void main(List<String> args) {
//定义两个字符串
String s1 = "Hello";
String s2 = "Dart";
//拼接这两个字符串并输出
print("$s1 $s2");
print(s1 + " " + s2);
}
2、数值类型(Number类型)
通过黄色框选部分我们可以发现,可以 将int类型的数据赋值给double,但是不能将double类型的数据赋值给int【低到高可以,高到低不可以】
void main(){
int a = 1;
int b = 2;
print(a + b);
print(a - b);
print(a * b);
print(a / b);
print(a % b);
}
void main() {
int a = 1;
int b = 2;
bool flag = true;
//条件判断语句
if (flag && (a == b)) { //支持 & 和 &&
print("条件成立");
} else {
print("目标条件不成立");
}
}
4、集合类型(List、Set类型)
void main() {
//定义一个集合
var list = [
123,
11.1,
"this is a str",
true,
['子集合', 12, false]
];
//打印
print(list);
}
var list = <指定类型>[...]
void main(List<String> args) {
//定义一个集合
var list = <String>["刘备", "关羽", "张飞"];
var list2 = <int>[1, 2, 3, 4];
print(list);
print(list2);
}
可以通过 add 方法向集合中添加元素,如果已经指明了集合类型,就只能添加同类型的数据list.add(要添加的元素)
在flutter的2.x版本中,还可以使用 var list = new List()
创建一个集合【3.x版本已经废弃】
可以通过var list = List.filled(length, element)
创建固定长度的List集合【length是长度、element是集合内容】
import 'dart:convert';
void main() {
//创建一个静态集合
var list = List.filled(2, "");
print(list);
//可以修改集合内容
list[0] = "newElement"; //此处需要注意,只能修改为与原来类型一种的元素
list[1] = "1111";
print(list);
}
(1)创建这个静态集合也可以指定数据类型var list = List<指定类型>.filled(length, element)
(2)因为长度固定,所以不能添加元素,也不能直接修改集合的长度
(3)但是通过[]
创建的集合可以通过list.length
修改元素的长度的
void main(List<String> args) {
//创建集合
var list = [1, 1.0, "1", true];
print(list.length);
//修改长度
list.length = 2;
print(list);
// list.length = 4;
// print(list);
}
注释掉的代码会出现报错,通过测试发现这个修改长度相当于直接删除了元素,如果想恢复长度是不允许的。【列举的是缩小长度的情况】
4、字典类型(Map类型)
list[key] = value
list[key]
获得对应的value【映射取值】void main(List<String> args) {
//定义一个字典
var person = {
"name": "小赵",
"age": 20,
"work": ['学生', '打工人']
};
//直接输出字典
print(person);
//根据key获取value
print(person['work']);
}
var map = new Map()
创建一个Maps的对象,后续再添加数据void main(List<String> args) {
//定义一个字典
var person = new Map();
person['name'] = "小杨";
person['salery'] = 15000;
print(person);
}
变量 或 常量 is 指定数据类型
,返回结果为布尔类型1、算数运算符
运算符 | 含义 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余 |
~/ | 取整 |
2、关系运算符
符号 | 含义 |
---|---|
== | 相等 |
!= | 不相等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
3、逻辑运算符
符号 | 含义 |
---|---|
! | 取反 |
&& | 短路与(同真为真) |
II | 短路或(同假为假) |
& | 与 |
| | 或 |
4、赋值运算符
符号 | 含义 |
---|---|
a = b |
将b的值赋值给a |
a ??= b |
如果a为空,那么等于b |
a += b |
a = a + b |
a -= b |
a = a - b |
a *= b |
a = a * b |
a /= b |
a = a / b |
a %= b |
a = a % b |
a ~/= b |
a = a ~/ b |
4、条件表达式
(1)条件判断语句 if-else、switch-case 语句
void main() {
bool flag = false;
if (flag) {
//因为这段恒为假,所以会有蓝色波浪线标记
print("true");
} else {
print("false");
}
//switch 分支语句
int num = 2;
switch (num) {
case 1:
print(1);
break;
case 2:
print(2);
break;
default:
print("others number");
break;
}
}
(2)三目运算符【可以替换一些简单的if-else语句】
if (num % 2 == 0) {
num = 10;
} else {
num = 9;
}
可以替换为: num = num % 2 == 0 ? 10 : 9;
(3)??
运算符的使用【如果数据为空,那么用 ?? 后面的内容替换】
void main() {
var a;
var b = a ?? 10;
print(b);
}
str.Empty;
5、Dart类型转换
(1)String类 -> Number类可以使用 具体的Number类型.parse(String类的对象)
void main() {
//创建字符串
String numStr = "123";
String numStr2 = "12.3";
var myNum = int.parse(numStr);
var myNum2 = double.parse(numStr2);
//利用 is 关键字可以判断数据类型
print(myNum is int);
print(myNum2 is double);
}
(2)Number类 -> String类 可以使用 toString() 方法
void main() {
int num = 123;
String s = num.toString();
print(s is String);
}
(3)补充说明:++、– 自增自减
6、循环语句
for(声明变量; 条件判断; 变量改变){
循环体;
}
while(条件表达式){
循环体;
}
do{
循环体;
}while(条件表达式);
do-while 与 while 的区别就在于是先执行循环体还是先判断
break关键字可以跳出当前循环,continue关键字可以跳过当前循环,回到循环起始位置继续循环
1、List 集合
//通过关键字自动识别数据类型
var list1 = ['This', 'is', 'apple'];
//显示声明数据类型
List list2 = ['This', 'is', 'banana'];
//通过add方法添加元素
list1.add('newElement');
//通过length方法可以查看集合大小
print(list1.length);
//创建大小固定的集合
List list3 = List.filled(2, "1", "2");
//通过数组索引可以修改指定元素
list3[0] = "修改后的元素";
void main() {
//创建一个集合
List l = ["some", "fruit"];
//length、isEmpty、isNotEmpty、reversed
print(l.length);
print(l.isEmpty);
print(l.isNotEmpty);
print(l.reversed);
}
方法名 | 作用 |
---|---|
addAll | 拼接数组 |
indexOf | 查找目标值的索引 |
remove(element) | 删除指定元素 |
removeAt(int index) | 删除指定位置的元素 |
fillRange(int start, int end, target) | 将指定片段替换为目标片段 |
insert(int index, element) | 指定位置插入数据 |
insertAll(int index, element) | 指定位置插入多个元素 |
join(‘分割方式’) | 将集合转化为字符串 |
split('分割方式) | 将字符串转化为集合类型 |
2、Set 集合
void main() {
var setNum = new Set();
setNum.addAll([1, 4, 2, 1, 3]);
print(setNum);
}
void main() {
//创建一个set的集合
var set = new Set();
set.addAll([1, 2, 3, 4]);
//利用toList()方法将集合转换成List类型
print(set.toList() is List);
}
3、Map 集合
属性 | 作用 |
---|---|
keys | 获取所有的key值 |
values | 获取所有的value值 |
isEmpty | 判断是否为空 |
isNotEmpty | 判断是否不为空 |
void main() {
//创建一个map的集合
var test = new Map();
//添加数据
test['name'] = "小明";
test['age'] = 18;
//利用keys和values获取对应的值
print(test.keys);
print(test.values);
//判断是否为空
print(test.isEmpty);
print(test.isNotEmpty);
}
方法名 | 作用 |
---|---|
remove | 删除指定key对应的键值对 |
addAll({…}) | 可以添加一些映射 |
containsValue | 可以查看是否包含指定的 key,返回 true / false |
void main() {
//创建一个map的集合
var test = new Map();
//添加数据
test['name'] = "YangJilong";
test['age'] = 22;
test['sex'] = '女';
print(test);
//remove删除指定键值对
test.remove('sex');
print(test);
//一次添加多个键值对
test.addAll({
'work': ["吃饭", "睡觉", "打豆豆"],
'play': ["王者荣耀", "三国杀"]
});
print(test);
//是否含有指定的key
print(test.containsKey('work'));
}
4、集合的遍历:【其中的方法对于三种集合都适用,这里以List类型的集合为例】
(1)通过普通 for 循环遍历
void main() {
//创建一个List类型的集合
var test = ["onesheep", "twosheep", "threesheep"];
//遍历输出test的元素值
for (int i = 0; i < test.length; i++) {
print(test[i]);
}
}
for (var item in test) {
print(item);
}
(3)通过forEach遍历
test.forEach((value) {
print(value);
});
可以替换为test.forEash((value) => print(value))
;
(4)通过where筛序出需要的 value
test.where((value){
return value;
})
因为此处我要输出这三个字符串,如果为Number类型的数据,可以根据表达式筛选数据
(5)通过any来判断集合中是否存在满足条件的数据
test.any((value){
return value == 'onesheep';
})
(6)通过 every 来判断集合中的数据是否全部都满足指定条件
test.any((value){
return value == 'onesheep';
})
1、方法的定义
返回值 方法名(参数列表){
方法体;
}
(1)对于返回值可以为具体的数据类型,也可以为void,同时还可以省略,由系统自动识别
(2)方法如果定义在入口方法上面,代表全局方法【要注意方法的作用域】
(3)方法可以进行嵌套,只不过方法的作用域不同
(4)案例分析:
//指定片段和
int sumNum(int num1, int num2) {
var sum = 0;
for (int i = num1; i <= num2; i++) {
sum += i;
}
return sum;
}
void main() {
var res = sumNum(1, 100);
print(res);
}
String printUserInfo(String username, int age) {
return "姓名: $username, 年龄: $age";
}
void main() {
print(printUserInfo("YangJilong", 22));
}
(1)位置可选参数
使用中括号括起来,代表位置可选参数【是否传递该参数都可以,但是如果传递了实参要注意位置要对应上】
String printUserInfo(String username, [int age]) {
if (age == null) {
return "姓名: $username, 年龄: 保密";
}
return "姓名: $username, 年龄: $age";
}
存在报错:The parameter ‘age’ can’t have a value of ‘null’ because of its type ‘int’, but the implicit default value is ‘null’.
解决办法:
?
,代表如果数据为空的话不进行任何处理//使用命名可选参数的方法,定义的时候通过{}括起来
void printInfo(String name, {int age = 0, String sex = ''}) {
print("姓名:$name, 年龄:$age, 性别:$sex");
}
//使用位置参数的可选方法,定义的时候通过[]括起来
void printInfo2(String name, [int? age, String? sex]) {
print("姓名:$name, 年龄:$age, 性别:$sex");
}
void main() {
printInfo("wang", sex: "男", age: 18);
printInfo2("yang", 15, '女');
}
(2)命名可选参数
变量名:值
3、方法嵌套与匿名函数:【后续会具体说明】
(1)案例一:直接定义两个方法,其中一个方法以另一个方法为参数【方法嵌套】
fun() {
print("内层方法");
}
fun2(f) {
f();
}
void main() {
fun2(fun);
}
(2)案例二:没有声明方法名,而是通过一个变量来接收【属于匿名函数】
内容概述:箭头函数、匿名函数、闭包
4、箭头函数
并没有明确定义,只是由语句的形式来命名的,使用了一个箭头来进行数据的处理
void main() {
//利用forEach语句遍历输出结合
var list = ['apple', 'pear', 'banana'];
list.forEach((element) {
print(element);
});
print("*" * 20);
list.forEach((element) => print(element));
}
通过输出结果我们可以看出 >> 使用箭头函数可以简化代码量
箭头函数只能处理一条语句【就是原来方法体里只有一条语句】
void main() {
//利用map语句将集合中大于2的元素乘2
var list = [1, 2, 3, 4, 5];
print("输出数组的值: $list");
var new_list = list.map((e) {
if (e > 2) {
return e * 2;
} else {
return e;
}
});
print("输出更新后数组的值: $new_list");
}
我们需要注意:通过map不能修改原集合中的元素
我们也可以用箭头函数来实现上述案例,使用了三目运算符代替简单的条件分支语句
void main() {
//利用map语句将集合中大于2的元素乘2
var list = [1, 2, 3, 4, 5];
print("输出数组的值: $list");
//使用了toList将结果转化为List类型的集合
var new_list = list.map((e) => (e > 2 ? e * 2 : e)).toList();
print("输出更新后数组的值: $new_list");
}
5、函数的相互调用
因为我们为了让一个函数去处理一个单独的事情,方便其他需要该功能的部分进行调用
// 判断是否为偶数
bool isEvnNumber(int n) {
return n % 2 == 0;
}
// 打印1-n之间的所有偶数
void printNumber(int n) {
for (int i = 1; i <= n; i++) {
if (isEvnNumber(i)) {
print(i);
}
}
}
void main() {
printNumber(10);
}
6、匿名方法
没有名字的方法我们称之为匿名方法,通常使用一个变量来接收
void main() {
var noName = () {
print("我们一个匿名方法!");
};
noName();
}
结果: 正常输出,也可以传递参数列表
7、自执行方法【不需要调用,自己执行的方法】
void main() {
(() {
print("这是一个自执行方法");
})();
}
自执行方法也可以传递参数,上面的括号传递形参列表,下面的括号传递实参列表
代码格式如下:
void main(){
((形参列表){
方法体;
})(实参列表);
}
8、方法的递归:【方法内部调用自己】
//此处以n的阶乘为例
var sum = 1;
fun(int n) {
if (n == 1) {
return;
}
sum *= n;
//递归
fun(n - 1);
}
void main() {
//计算5的阶乘
fun(5);
print(sum);
}
9、闭包
为什么使用闭包?
如何实现闭包呢?【让局部变量常驻内存】
函数嵌套函数,内部函数会调用外部函数的变量或参数,return 里面的函数,就形成了闭包
fun() {
var a = 123;
return () {
print(a++);
};
}
void main() {
var b = fun();
b();
b();
}
(1)常驻内存是如何体现的呢?
修改数据产生的影响是全局的【案例中a的值调用一次就修改一次】
(2)用变量去接收一个函数,属于匿名函数的形式,但是仍能通过 fun() 去调用该方法
1、背景概述
2、通过class关键字可以自定义类,类名一般采用大驼峰的命名规则
class Person {
String name = '僵小鱼';
int age = 8;
void getInfo() {
print("姓名:${this.name}, 年龄: ${this.age}");
}
}
void main() {
//创建Person类的对象
var person = new Person();
//通过对象名.属性/方法调用
print(person.name);
print(person.age);
person.getInfo();
}
3、类的构造方法【实例化一个类的时候自动触发的一个方法】
与类名通过的方法,可以为无参的构造方法,也可以为传参的构造方法
class Person {
String name;
int age;
// 添加一个构造方法
Person(this.name, this.age);
}
void main() {
//创建Person类的对象
var person = new Person("夏洛克", 20);
//通过对象名.属性/方法调用
print("${person.name}-----${person.age}");
}
4、命名构造函数,属于构造函数的一种
class Person {
String name;
int age;
Person.now(this.name, this.age) {
print("姓名:$name, 年龄: $age");
}
}
void main() {
var p = new Person.now("sun", 20);
}
import 文件位置
5、私有成员
通过变量名或方法前添加下划线,代表是私有成员,并且要把这个类抽离成一个文件
此时外部其他模块中就无法直接调用该类的私有成员
可以在该类中设置共有方法,为外部提供访问私有成员的接口
(1)我们创建一个文件,保存Person类
class Person {
String _name;
int _age;
Person(this._name, this._age);
void printInfo() {
print("姓名: $_name, 年龄: $_age");
}
}
(2)我们在其他文件中导入并实例化这个Person类
通过图片我们可以看出在当前文件中,无法通过Person类的实例调用Person类的私有属性
6、getter 和 setter 方法
(1)getter 用于声明某些方法,在外部可以通过类的实例访问属性的方式访问该方法
class Rect {
//声明类型为数值型
num height;
num width;
Rect(this.height, this.width);
//getter方法
get area {
return height * width;
}
}
void main() {
Rect r = new Rect(3, 4);
print("面积:${r.area}");
}
(2)setter 用于声明某些方法,接收外部传递的一个值,通过该方法传递到类内部的属性【传参使用等号赋值的方式】
class Rect {
//声明类型为数值型
num height;
num width;
Rect(this.height, this.width);
get area {
return height * width;
}
//修改类的属性值
set areaHeight(value) {
this.height = value;
}
}
void main() {
Rect r = new Rect(3, 4);
print("面积:${r.area}");
r.areaHeight = 5;
print("新面积:${r.area}");
}
7、初始化列表
Rect()
: height = 2,
width = 3 {}
8、静态成员
我们可以通过static关键字定义静态属性或静态方法【可以通过类名.成员名
调用】
静态方法不能访问非静态成员,非静态方法既可以访问非静态成员、也可以访问静态成员
在dart语言中,不能通过类的对象调用静态成员
class Person {
static String name = 'KeBi';
int age = 20;
static void show() {
print(name);
}
}
void main() {
print(Person.name);
Person.show();
var p = new Person();
print(p.age);
}
9、对象操作符
符号 | 含义 |
---|---|
? | 条件运算符 |
as | 类型转换 |
is | 类型判断 |
… | 级联操作(连缀) |
?
条件运算符一般用于处理空指针异常问题,如果对象为空,那么就不执行任何操作
class Person {
String name;
int age;
Person(this.name, this.age);
void printInfo() {
print("姓名:${this.name}, 年龄:${this.age}");
}
}
(1)对象为空的时候调用类方法
void main() {
Person p;
p.printInfo();
}
(2)使用?之后【正常应该什么都不会输出,但是我的还是报错】
void main() {
var p;
p?.printInfo();
}
is
用于判断指定对象是否为指定的类型【如果对象类型属于指定的类型的子类,那么也会返回true】Person p = new Person(" 九州", 100);
print(p is Object);
as
用于强制类型转换var str = '';
str = new Person("沈璐", 21);
//如果再老版本里面,直接调用Person类中的方法会报错
(str as Person).printInfo();
..
级联操作用于一条语句执行多个操作,彼此之间使用..
进行分割void main(){
Person p = new Person("沈念", 45);
p
..name = "花千骨"
..age = 31
..printInfo();
}
10、类的继承
Person类:【在案例(1)、(2)、(4)中不会再定义Person类】
class Person {
String name;
int age;
Person(this.name, this.age);
void printInfo() {
print("姓名:${this.name}, 年龄:${this.age}");
}
}
(1)使用super关键字调用父类的构造方法【有两种方式】
//子类
class Teacher extends Person {
// Teacher(super.name, super.age);
Teacher(String name, int age) : super(name, age) {}
}
void main() {
Teacher t1 = new Teacher("张老师", 31);
t1.printInfo();
}
(2)可以为子类添加新的属性和方法
//子类
class Teacher extends Person {
//如果我不初始化,那么在Teacher构造方法处就会报错
String sex = '';
// Teacher(super.name, super.age);
Teacher(String name, int age, String sex) : super(name, age) {
this.sex = sex;
}
void run() {
print("姓名:${this.name}, 年龄:${this.age},性别:${this.sex}");
}
}
void main() {
Teacher t1 = new Teacher("张老师", 31, '女');
t1.run();
}
(3)如果想使用父类是匿名的构造函数,那么也可以通过 **super **直接调用
class Person {
String name;
int age;
//匿名构造函数
Person.xxx(this.name, this.age);
void printInfo() {
print("姓名:${this.name}, 年龄:${this.age}");
}
}
//子类
class Teacher extends Person {
String sex = '';
//super.xxx() 调用了父类的匿名构造函数
Teacher(String name, int age, String sex) : super.xxx(name, age) {
this.sex = sex;
}
void run() {
print("姓名:${this.name}, 年龄:${this.age},性别:${this.sex}");
}
}
void main() {
Teacher t1 = new Teacher("王老师", 21, '男');
t1.run();
}
(4)可以在子类中重写父类方法【推荐在重写部分上方添加@Override
】
class Teacher extends Person {
String sex = '';
Teacher(String name, int age, String sex) : super.xxx(name, age) {
this.sex = sex;
}
//重写了父类的printInfo方法
void printInfo() {
print("姓名:${this.name}, 年龄:${this.age},性别:${this.sex}");
}
}
void main() {
Teacher t1 = new Teacher("大仓", 28, '男');
//此时调用的是子类自己的方法,如果子类没有才会去上一层寻找,直至到Object类
t1.printInfo();
}
11、抽象方法
abstract class Animal {
eat();
love();
}
class Dog extends Animal {
eat() {
print("吃骨头");
}
love() {
print("喜欢听音乐");
}
}
12、多态
允许将子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的结果【子类实例赋值给父类引用】
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现
案例分析:
(1)如果将子类实例赋值给父类引用,属于向上转型,新对象只能调用父类里面有的部分【如果这部分被子类重写了,那么调用了就是子类中对应的成员】
不能调用子类中的特有成员
abstract class Animal {
eat();
}
class Dog extends Animal {
eat() {
print("吃骨头");
}
run() {
print("小狗在跑...");
}
}
void main() {
Animal a = new Dog();
a.eat();
//无法调用子类特有方法
a.run();
}
去掉run后即可正常运行
13、接口
dart 接口没有使用 interface 关键词定义接口,但是使 implements 关键字来实现接口
普通类和抽象类都可以作为接口被使用,推荐使用抽象类定义接口
接口的作用也是用于规范和约束
abstract class DB {
add();
}
// mysql、mssql类去实现接口
class Mysql implements DB {
add() {
print("mysql数据库添加数据");
}
}
class Mssql implements DB {
add() {
print("mssql数据库添加数据");
}
}
void main() {
Mysql my = new Mysql();
Mssql ms = new Mssql();
my.add();
ms.add();
}
import
导入。14、dart 支持多接口
在使用implements实现多个接口时,彼此之间用逗号隔开
要注意实现每个接口中的全部属性和方法【如果是抽象类定义的接口】
abstract class A {
fun1();
}
abstract class B {
fun2();
}
class C implements A,B{
fun1() {
throw UnimplementedError();
}
fun2() {
throw UnimplementedError();
}
15、mixins 类:类似实现了多继承的功能【混合类】
(1)不能有构造函数
(2)不能继承除Object类的其他类
// ignore_for_file: unnecessary_type_check
class A {
fun1() {
print("A中的fun1");
}
}
class B {
fun2() {
print("B中的fun2");
}
}
class C with A, B {}
void main() {
var c = new C();
c.fun1();
c.fun2();
print(c is A);
print(c is B);
print(c is C);
}
(1)也可以使用抽象的mixins类,不过要记得实现所有抽象方法
(2)利用 is 进行类型判断时,如果为该类型的超类,那么也会返回 true
1、泛型
指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。会在编译期检查类型是否错误。
可以减少代码冗余度,代码重用率高,还可以完成类型校验
T
来代表泛型T getData<T>(value){
return value;
}
//通过以下代码调用[此处以字符串类型为例]
getData<String>("abc");
如果将返回值处的T去掉,可以控制只对传入参数校验,不对返回类型进行校验
2、泛型类
目的是为了内部参数的接收是泛型,就将这个类定义为泛型
class MyList<T> {
List list = <T>[];
void addElement(T value) {
list.add(value);
}
List getList() {
return list;
}
}
void main() {
MyList list = new MyList<String>();
list.addElement("a apple");
list.addElement("a banana");
print(list.getList());
MyList list1 = MyList<int>();
list1.addElement(1);
list1.addElement(2);
list1.addElement(3);
print(list1.getList());
MyList list2 = MyList();
list2.addElement(1);
list2.addElement("love");
list2.addElement(true);
list2.addElement([2, 3, 4]);
print(list2.getList());
}
(1)创建了一个泛型类,一个泛型集合,集合中的元素也是泛型的 >> 这三个部分元素类型一致
(2)创建了三个MyList的对象,第一个指定为String类型,第二个为int类型,第三个没有指定类型 >> 意味着第三个集合可以存储任意类型的数据
3、泛型接口
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<T> implements Cache<T> {
getByKey(String key) {
print(key);
}
void setByKey(String key, T value) {
print("我是文件缓存,把${key}和${value}存储到了文件中");
}
}
class MemoryCache<T> implements Cache<T> {
getByKey(String key) {
print(key);
}
void setByKey(String key, T value) {
print("我是内存缓存,把${key}和${value}存储到了内存中");
}
}
void main() {
var f = new FileCache<String>();
f.setByKey("root", "root");
f.getByKey("key已加密");
}
(1)导入自定义库
import 'lib/xxx.dart'; //库所在位置
(2)导入系统内置库【前提是有dart的jdk】
import 'dart:io';
import 'dart:math';
(3)导入第三方库
2、async 和 await
async 的作用是使方法变成异步方法,await是等待异步方法执行完成
只有async方法才能使用await关键字调用方法
如果调用别的async方法必须使用await关键字
案例分析:定义一个异步方法,在主方法中去调用这个异步方法
testAsync() async {
return 'Hello async';
}
test() {
return 'Hello test';
}
void main() async {
print(test());
var res = await testAsync();
print(res);
}
(1)对于普通方法,直接调用即可;但是调用异步方法需要配合await使用
(2)但是如果想使用await关键字,那么当前方法只能为异步方法 >> 将main方法也定义成了异步方法
(3)通过关键字async可以声明为异步方法
(4)如果异步方法中只是打印一些信息,那么也可以直接通过方法名调用
3、使用第三方库的流程
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter
(1)将对应的依赖复制到自己项目的配置文件中【dependencies】[pubspec.yaml]
dependencies:
http: ^0.13.5
(2)在dos中进入到当前项目目录,运行pub get
指令开始安装依赖
(3)找到Example文档,根据文档步骤开始使用第三方库
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main(List<String> arguments) async {
// This example uses the Google Books API to search for books about http.
// https://developers.google.cn/books/docs/overview
var url =
Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'});
// Await the http get response, then decode the json-formatted response.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse =
convert.jsonDecode(response.body) as Map<String, dynamic>;
var itemCount = jsonResponse['totalItems'];
print('Number of books about http: $itemCount.');
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
库名.
来指定那部分的内容4、可以导入库中的部分功能
通过 show 关键字指定引入的功能
通过 hide 关键字隐藏不想引入的功能
import 'Person' show set,get;
import 'dart:math' hide max;
5、延迟加载
1、空安全 【Flutter2.2.0 之后引入了空安全】
通过在数据类型后添加 ?
,就代表这个数据可以为空
void main() {
int? num = null;
print(num);
}
也可以让返回指定数据类型的方法允许为空
String? getData(var value){
if(value == null){
return null;
}else{
return "数据获取成功";
}
}
2、类型断言
如果变量不为空,那么继续执行语句;如果变量为空,那么会抛出异常 【空指针异常】
一般直接用于变量后面
void printLength(String? s) {
print(s!.length);
}
void main() {
printLength(null);
}
3、延迟初始化
class Person {
late String name;
late int age;
setInit(String name, int age) {
this.name = name;
this.age = age;
}
}
4、required关键词
在老版本中 @required 的作用是注解
现在为内置修饰符,用于根据需要标记命名参数(函数或类),使参数不为空
如果在命名可选参数前面使用了required,代表为必须传入的参数
printInfo(String name, {required int age}) {
print("$name --- $age");
}
void main() {
printInfo("wang", age: 20);
}
5、常量优化
6、判断两个对象是否使用一个存储空间?
使用 indentical 方法,可以判断两个对象是否为同一个存储空间的引用
const 关键字在多个地方创建相同的对象时,内存中只保留了一个对象
void main() {
var n1 = const Object();
var n2 = const Object();
var n3 = Object();
var n4 = Object();
print(identical(n1, n2));
print(identical(n3, n4));
}
7、常量构造函数
class Container {
final int weight;
final int height;
const Container({required this.weight, required this.height});
}
void main() {
Container c1 = const Container(weight: 100, height: 100);
Container c2 = const Container(weight: 100, height: 100);
print(identical(c1, c2));
}