无人扶我鸿蒙志,我自钻研 Dart 辛。就算时乖难遂愿,孤帆亦可越千滩。
基于现在大环境,Flutter适配鸿蒙。
本想一心搞纯血,奈何前端太卷。
曾怀壮志追纯粹,欲守初心辟径专。
可叹风潮多变幻,只能顺势拓新天。
且把豪情融技艺,暂抛执念向峰巅。
虽非本意仍勤勉,盼在潮头立寸田。
Dart基础语法
1.变量和常量(存储数据)
1.1 变量
var
void main() {
var name = '张三';
var age = 18;
print(name);
print(age);
age = 19;
print('$name修改后的age: ${age}');
}
1.2 常量
const 和 final
void main() {
const name1 = '张三';
const name2 = '李四';
print(name1);
print(name2);
var a = 1;
var b = 2;
// const sun1 = a + b; // const 编译期常量 值在编译时赋值 不能动态赋值
// print(sun1);
final sun2 = a + b; // final 运行期常量 值在运行时赋值
print(sun2);
/* 常量:建议优先使用 const 编译不允许的话替换 final */
}
2.数据类型(可存储数据类型)
2.1 num(数字)
num、int、double
void main() {
num a = 1;
print(a);
num b = 1.5;
print(b);
int count = 100;
print(count);
double price = 12.5;
print(price);
// 建议 :如果确定是整数,使用int类型,如果确定是浮点数,使用double类型
}
2.2 String(字符串)
String
void main() {
String str = 'hello world';
print(str);
// \n 换行符
str = 'hello rhj \n 666';
print('当前字符串为$str');
// 字符串换行
str = '''
hello
world
rhj
''';
print(str);
}
2.3 bool(布尔)
bool
void main() {
// 登录成功:true
bool isLogin = true;
print(isLogin); // true
// 登录失败:false
isLogin = false;
print(isLogin); // false
}
2.4 List(列表)
List 类型: List<dynamic>
列表中可以存储任意类型的数据
void main() {
// 列表 相当于数组
List list = [1, 2, ['item',20], false, '张三'];
print(list);
// 访问列表元素
print(list[0]);
// 列表长度
print(list.length);
// 最后一项
print(list[list.length - 1]);
print(list.last);
}
使用列表:查改增删
○查询列表长度
○查询指定的元素
○修改:列表[索引] = 新值
○新增:列表.add(新元素)、列表.addAll(新列表)
○指定位置添加:列表.insert(索引, 内容');
○删除:使用元素删除、使用索引删除
○遍历列表:读取出列表中每一个元素
void main() {
List names = ['张三', '李四', '王五', '赵六', '钱七'];
print(names);
// 列表长度
print('列表长度$names.length');
// 获取列表第一个元素
print('列表第一个元素${names.first}');
// 获取列表最后一个元素
print('列表最后一个元素${names.last}');
// 修改列表元素
names[2] = '王二';
print('修改后列表$names');
// 添加元素
names.add('小红');
print('添加元素后$names');
// 添加多个元素
names.addAll(['小明', '小刚']);
print('添加多个元素后$names');
// 插入元素 insert(索引,元素)
names.insert(2, '小花');
print('插入元素后$names');
// 删除元素 remove(元素)
bool res = names.remove('小花');
print(res ? '删除成功' : '删除失败');
print('remove(元素)-删除元素后$names');
// 删除元素 removeAt(索引)
names.removeAt(2);
print('removeAt(索引)-删除元素后$names');
// 删除元素 removeRange(开始索引,结束索引)
names.removeRange(2, 4);
print('removeRange(开始索引,结束索引)-删除元素后$names');
// 遍历列表
names.forEach((item) => print(item));
names.forEach((item) {
print(item);
});
}
2.5 Map(字典)
Map
void main() {
Map category = {
'name': '手机',
'color': 'white',
'brand': '华为',
};
// 访问元素
print(category);
print(category['name']);
// 修改元素
category['name'] = '电脑';
print(category);
// 添加元素
category['price'] = 9999.9999;
print(category);
// 输出所有键
print(category.keys);
// 遍历键值对
category.forEach((key, value) {
print('$key----$value');
});
}
2.6 Dart空安全机制
1无法正常执行的代码:在代码编译期就会报错
2解决办法:使用 ? 显示的指定变量可以为空
3使用可以为空的变量
void main() {
// 1. 可以正常执行的代码
String name1 = 'hello world';
print(name1.length);
// 2. 无法正常执行的代码:在代码编译期就会报错
// String name2;
// The non-nullable local variable 'name2' must be assigned before it can be used.
// 报错:Null check operator used on a null value
// print(name2.length);
// 3. 解决办法:使用 ? 显示的指定变量可以为空
String? name3 = null;
print(name3);
// 4. 使用可以为空的变量
// name3? : 表示非空检查,如果name3为空,不去调用属性或方法,如果name3不为空,就去调用属性或方法
print(name3?.length);
}
3.运算符(数据如何做运算)
3.1 算术运算符
void main() {
int n1 = 10;
int n2 = 3;
// 加 +
print(n1 + n2); // 13
// 减 -
print(n1 - n2); // 7
// 乘 *
print(n1 * n2); // 30
// 除 /
print(n1 / n2); // 3.3333333333333335
// 取整:取除法结果的整数部分 ~/
print(n1 ~/ n2); // 3
// 取模:取除法结果的余数 %
print(n1 % n2); // 1
double n3 = 0.1 + 0.2;
print(n3); // 0.30000000000000004
}
3.2 赋值运算符
void main() {
// 等于 =
int n1 = 10;
// 加等于 +=
// n1 = n1 + 5;
n1 += 5;
print(n1); // 15
// 减等于 -=
n1 -= 5;
print(n1); // 10
// 乘等于 *=
n1 *= 5;
print(n1); // 50
// 除等于 /=
// 注意:double类型的数据才能做除等于的操作
// A value of type 'double' can't be assigned to a variable of type 'int'.
// n1 /= 5;
double n2 = 50;
n2 /= 5;
print(n2); // 10.0
// 取余等于 %=
int n3 = 10;
n3 %= 3;
print(n3); // 1
// 自增:在原有数值上加1 ++
int a = 10;
a++;
print(a); // 11
// 自减:在原有数值上减1 --
int b = 20;
b--;
print(b); // 19
}
3.3 比较运算符
void main() {
int n1 = 10;
int n2 = 20;
// 大于 >
print(n1 > n2); // false
// 小于 <
print(n1 < n2); // true
// 大于等于 >=
print(n1 >= n2); // false
// 小于等于 <=
print(n1 <= n2); // true
// 等于 ==
print(n1 == n2); // false
print('hello' == 'helloo'); // false
// 不等于 !=
print(n1 != n2); // true
print('hello' != 'helloo'); // true
}
3.4 逻辑运算符
void main() {
// 年龄
int age = 33;
// 工作年限
int years = 10;
// 1. 逻辑与:一假则假
// 年龄大于28岁,并且工作年限大于4年
bool ret1 = age > 35 && years > 4;
print(ret1); // false
// 2. 逻辑或:一真则真
// 年龄大于23岁,或者工作年限大于2年
bool ret2 = age > 35 || years > 2;
print(ret2); // true
// 3. 逻辑非:真变假,假变真
print(!true); // false
print(!false); // true
// 工作年限不小于9年
bool ret3 = years >= 9;
// bool ret3 = !(years < 9);
print(ret3); // true
}
4.流程控制(选择或重复执行)
4.1 if分支语句
void main() {
// 1. if单分支语句
// 准备高考成绩,如果分数大于等于700分,则输出 '恭喜考入牛马大学'
int score1 = 699;
if (score1 >= 700) {
print('恭喜考入牛马大学');
}
// 2. if双分支语句
// 准备高考成绩,如果分数大于等于700分,则输出 '恭喜考入牛马大学',反之,则输出 '继续努力'
int score2 = 699;
if (score2 >= 700) {
print('恭喜考入牛马大学');
} else {
print('继续努力');
}
// 3. if多分支语句
// 根据学生分数划分学生成绩等级:
// 优秀:分数大于等于90分
// 良好:分数小于90分,且大于等于80分
// 中等:分数小于80分,且大于等于60分
// 不及格:分数小于60分
int score3 = 58;
if (score3 >= 90) {
print('优秀');
} else if (score3 >= 80) {
print('良好');
} else if (score3 >= 60) {
print('中等');
} else {
print('不及格');
}
}
4.2 三元运算符
void main() {
// 需求:准备高考成绩,如果分数大于等于700分,则输出 '恭喜考入牛马大学',反之,则输出 '继续努力'
// 思考:以下代码可以简化吗?
int score1 = 699;
// if (score1 >= 700) {
// print('恭喜考入牛马大学');
// } else {
// print('继续努力');
// }
// 1. 使用三元运算符简化if双语句:条件表达式 ? 表达式1 : 表达式2
score1 >= 700 ? print('恭喜考入牛马大学') : print('继续努力');
// 2. 思考:以下代码适合使用三元运算符改写吗?
int score2 = 88;
if (score2 >= 90) {
print('优秀');
} else if (score2 >= 80) {
print('良好');
} else if (score2 >= 60) {
print('中等');
} else {
print('不及格');
}
}
4.3 switch case 语句
void main() {
// 根据订单状态,打印出订单状态描述信息
// 订单状态:1为待付款、2为待发货、3为待收货、4为待评价
int orderState = 3;
switch (orderState) {
case 1:
print('待付款');
break;
case 2:
print('待发货');
break;
case 3:
print('待收货');
break;
case 4:
print('待评价');
break;
default:
print('其他');
}
}
4.4 循环语句
void main() {
// 1. while循环
// 重复打印10次 '月薪过万'
int n = 0;
while (n < 10) {
print('$n -- 月薪过万');
n++;
}
// 2. for循环
// 重复打印5次 '李白姓白'
for (var i = 0; i < 5; i++) {
print('$i -- 李白姓白');
}
// 3. 使用循环遍历列表
// 3.1 遍历列表:for循环
List categories = ['居家', '美食', '服饰'];
for (var i = 0; i < categories.length; i++) {
String name = categories[i];
print(name);
}
// 3.2 遍历列表:for ... in
for (var item in categories) {
// item就是遍历出来的元素
print(item);
}
// 4. 终止循环
// 4.1 break:中断整个循环
for (var i = 0; i < 5; i++) {
if (i == 2) {
// 吃到第三个苹果发现了虫子,剩下的苹果没胃口都不吃了
break;
}
print('我把第 ${i + 1} 个苹果吃了');
}
// 4.2 continue:跳过本次循环直接进入下一次循环
for (var i = 0; i < 5; i++) {
if (i == 2) {
// 吃到第三个桃子发现了虫子,第三个桃子不吃了,剩下的桃子接着吃
continue;
}
print('我把第 ${i + 1} 个桃子吃了');
}
}
4.5 综合案例
编写一个 Dart 程序,根据给定的购物车数据,计算所有被选中的商品的总价。 要求
- 定义一个列表 carts,其中每个元素是一个 Map,表示一个商品的信息。
- 每个商品的信息包括:
- count:商品的数量(整数)
- price:商品的单价(浮点数)
- selected:商品是否被选中(布尔值)
- 遍历 carts 列表,计算所有被选中的商品的总价。
- 输出计算结果。
void main() {
// 准备购物车数据
List carts = [
{"count": 2, "price": 10.0, "selected": true},
{"count": 1, "price": 30.0, "selected": false},
{"count": 5, "price": 20.0, "selected": true}
];
// 记录总金额
double totalPrice = 0.0;
// 遍历购物车数据
for (var item in carts) {
// 读取商品的勾选状态
bool selected = item['selected'];
// 如果商品被勾选 ,读取该商品的单价和数量,并计算价格小计
if (selected) {
// 累加价格小计,计算总价
totalPrice += item['count'] * item['price'];
}
}
print(totalPrice); // 120.0
}
5.函数(复用代码)
5.1 函数的定义
1定义函数:无参数无返回值函数
2定义函数:有参数有返回值函数
3函数的特点:返回值类型和参数类型是可以省略的
void main() {
sayHello();
double sum = add(1, 2);
print('add(1, 2)运算结果为: $sum'); // add(1, 2)运算结果为: 3.0
// print('add2(1, 2)运算结果为: ${add2(1, '2')}');
}
void sayHello() {
print('hello');
}
double add(double a, double b) {
return a + b;
}
// add2(a, b) {
// return a + b;
// }
5.2 函数的参数
函数的参数可以分为:
必传参数(位置参数)、可选参数(关键字参数)
注意点:必传参数不能为空,可选参数可以为空,且参数都可以设置默认值
注意事项
可选参数需要使用 { } 包起来
void main() {
printString('张三丰'); // 张三丰 - null - 中国
printString('李四', age: 18); // 李四 - 18 - 中国
printString('王五', location: '中国'); // 王五 - null - 中国
printString('赵六', age: 18, location: '台湾'); // 赵六 - 18 - 台湾
}
/**
* name:必传参数
* age:可选参数
* location:可选参数,并有默认值
*/
void printString(String name, {int? age, String? location = '中国'}) {
print('$name - $age - $location');
}
5.3 函数对象
void main() {
// 1.2 定义一个变量接收函数
var f = funcDemo1;
f();
Function f1 = funcDemo1;
f1();
// 2.2 函数作为参数
funcDemo2(funcDemo3);
}
// 1.1 函数可以作为对象赋值给其他变量
void funcDemo1() {
print('funcDemo1');
}
// 2.1 函数可以作为参数传递给其他函数
void funcDemo2(Function func) {
// 调用外界传入的函数
func();
}
// 定义作为参数的函数: 把funcDemo3传入到funcDemo2
void funcDemo3() {
print('funcDemo3');
}
5.4 匿名函数
1匿名函数赋值给变量,并调用
2可以作为参数传递给其他函数去调用(回调函数)
void main() {
// 匿名函数
// 1. 匿名函数赋值给变量,并调用
Function f = () {
print('这是一个匿名函数');
};
f();
// 2. 可以作为参数传递给其他函数去调用(回调函数)
funcDemo(() {
print('这个匿名函数是个参数');
});
}
// 定义一个接收函数作为参数的函数
void funcDemo(Function func) {
func();
}
5.5 箭头函数
当函数体只有一行代码时,可以使用箭头函数简写
void main() {
int ret1 = sum1(10, 20);
print(ret1); // 30
int ret2 = sum2(30, 40);
print(ret2); // 70
}
// 思考:以下代码可以简写吗?
sum1(a, b) {
return a + b; // 函数体只有一行代码
}
// 箭头函数简写函数体:简写只有一行代码的函数体
sum2(a, b) => a + b;
5.6 综合案例
编写一个 Dart 程序,根据给定的购物车数据,计算是否全选封装一个函数
void main() {
// 准备购物车数据
List carts = [
{"count": 2, "price": 10.0, "selected": true},
{"count": 1, "price": 30.0, "selected": false},
{"count": 5, "price": 20.0, "selected": true}
];
// 调用封装的函数
bool isSelectedAll = getSelectedState(carts);
if (isSelectedAll) {
print('全选');
} else {
print('非全选');
}
}
// 核心逻辑:只要有任何一个商品是未勾选的,那么就是非全选
bool getSelectedState(List carts) {
// 购物车初始的状态:假设默认是全选
bool isSelectedAll = true;
for (var item in carts) {
bool selected = item['selected'];
// 核心代码:只要有任何一个商品是非勾选的,则购物车就是非全选
if (selected == false) {
isSelectedAll = selected;
break;
}
}
// 返回是否全选结果
return isSelectedAll;
}
6.类(面向对象编程)
6.1 类的定义
void main() {
final p1 = Person();
p1.name = '张三';
p1.age = 20;
print('${p1.name}---${p1.age}');
p1.eat();
}
// 类的作用:定义对象的属性和行为
class Person {
String? name;
int? age;
void eat() {
print('吃东西');
}
}
6.2 构造函数
void main() {
final p = new Person('张三', 20);
print(p.name);
final p2 = new Person.withInfo('李四', 30);
print(p2.name);
}
class Person {
String? name;
int? age;
// 构造函数
// Person(String name, int age) {
// this.name = name;
// this.age = age;
// }
// 构造函数简写
Person(this.name, this.age);
// 命名构造函数 类名.方法(参数...)
Person.withInfo(this.name, this.age);
// 方法
void eat() {
print('我是干饭人');
}
}
6.3 私有属性和方法
私有属性和方法无法从外部文件访问
import './27-dog.dart';
void main() {
Dog dog = Dog();
dog.name = '旺财';
print(dog.name);
//dog._age = 3; // 报错 The setter '_age' isn't defined for the type 'Dog'
dog.printAge();
}
class Dog {
// 公有属性
String? name;
// 私有属性
int? _age = 18;
// 公有方法
void eat() {
print('dog eat');
}
// 私有方法
void _run() {
print('dog run');
}
void printAge() {
print('age:$_age');
}
}
6.4 继承
无法继承父类的构造函数
void main() {
// 创建男人对象
Man man = Man('李雷', 13);
print(man.name);
man.eat();
man.fight();
// 创建女人对象
Woman woman = Woman('韩梅梅', 14);
print(woman.name);
woman.eat();
}
class Person {
String? name;
int? age;
Person(this.name, this.age);
void eat() {
print('$name -- eat');
}
}
// 男人类
class Man extends Person {
Man(super.name, super.age);
void fight() {
print('$name -- fight');
}
// 重写父类的方法
@override
void eat() {
print('我是$name,我爱吃肉');
}
}
// 女人类
class Woman extends Person {
Woman(super.name, super.age);
// 重写父类的方法
@override
void eat() {
print('我是$name,我爱吃蔬菜');
}
}
6.5 Mixin扩展
void main() {
// 创建男人对象
Man man = Man('李雷', 13);
print(man.name);
man.eat();
man.fight();
// 创建女人对象
Woman woman = Woman('韩梅梅', 14);
print(woman.name);
woman.eat();
// mixin 混入
woman.shopping(woman.name);
}
class Person {
String? name;
int? age;
Person(this.name, this.age);
void eat() {
print('$name -- eat');
}
}
// 男人类
class Man extends Person {
Man(super.name, super.age);
void fight() {
print('$name -- fight');
}
// 重写父类的方法
@override
void eat() {
print('我是$name,我爱吃肉');
}
}
// 女人类
class Woman extends Person with Shopping {
Woman(super.name, super.age);
// 重写父类的方法
@override
void eat() {
print('我是$name,我爱吃蔬菜');
}
}
// mixin 混入(解耦)
mixin Shopping {
void shopping(String? name) {
print('$name -- shopping');
}
}
7.异步编程
7.1 Dart是单线程的
7.2 Future
7.2.1 同步网络请求
import 'dart:io';
void main() {
print('开始执行main函数');
print(getNetworkData());
print('这是不能被阻塞的代码');
}
String getNetworkData() {
print('加载网络数据中...');
sleep(Duration(seconds: 5));
return '返回的网络数据';
}
代码执行出现堵塞
7.2.2 Future基本使用
import 'dart:io';
void main() {
print('开始执行main函数');
getNetworkData().then((res) {
print(res);
});
print('这是不能被阻塞的代码');
}
Future<String> getNetworkData() {
return Future<String>(() {
print('网络数据加载中');
sleep(Duration(seconds: 5));
return '返回的网络数据';
});
}
这一次的代码顺序执行,没有出现任何的阻塞现象
7.2.3 Future链式调用
.then成功时执行
.catchError失败时执行
.whenComplete完成时执行(不管成功和失败都执行)
import 'dart:io';
void main() {
print('开始执行main函数');
getNetworkData().then((res) {
print(res);
}).catchError((e) {
print('执行catchError');
}).whenComplete(() {
print('执行whenComplete');
});
print('这是不能被阻塞的代码');
}
Future<String> getNetworkData() {
return Future<String>(() {
print('网络数据加载中');
sleep(Duration(seconds: 5));
throw new Exception('网络请求异常');
return '返回的网络数据';
});
}
7.3 async和await
没有使用async和await时,代码比较繁琐
// 用户先登录,登录成功之后拿到token,然后再保存token到本地
import 'dart:io';
void main() {
print('开始执行main函数');
login().then((token) {
setToken(token).then((res) {
if (res == 'ok') {
print('本地存储token成功, token是$token');
}
}).catchError((e) {
print(e);
});
}).catchError((e) {
print(e);
});
print('这是不能被阻塞的代码');
}
// 1. 模拟耗时的登录操作
Future<String> login() {
return Future<String>(() {
sleep(Duration(seconds: 3));
print('登录成功');
String token = '10086';
return token;
});
}
// 2. 模拟耗时的本地存储操作
Future<String> setToken(String token) {
return Future<String>(() {
sleep(Duration(seconds: 3));
print('本地存储token');
return 'ok';
});
}
使用async和await
// 用户先登录,登录成功之后拿到token,然后再保存token到本地
import 'dart:io';
void main() {
print('开始执行main函数');
// login().then((token) {
// setToken(token).then((res) {
// if (res == 'ok') {
// print('本地存储token成功, token是$token');
// }
// }).catchError((e) {
// print(e);
// });
// }).catchError((e) {
// print(e);
// });
test2();
print('这是不能被阻塞的代码');
}
test2() async {
try {
String token = await login();
bool res = await setToken(token);
if (res) {
print('登录并保存token成功');
} else {
print('保存token失败');
}
} catch (error) {
print(error);
}
}
// 1. 模拟耗时的登录操作
Future<String> login() {
return Future<String>(() {
sleep(Duration(seconds: 3));
print('登录成功');
String token = '10086';
return token;
});
}
// 2. 模拟耗时的本地存储操作
Future<bool> setToken(String token) {
return Future<bool>(() {
print('正在设置token...');
sleep(Duration(seconds: 2));
print('$token保存成功');
return true;
});
}
8.泛型
void main() {
// 列表泛型
List<String> list = ['张三', '李四'];
print(list);
// 字典泛型 dart不支持联合类型
Map<String, dynamic> person = {
'name': '张三',
'age': 18,
'height': 1.88,
};
print(person);
var a = demo(1);
var b = demo('张三');
var c = demo(true);
print(a);
print(b);
print(c);
}
// 没有指定类型,书写不会报错,但是运行有风险
// demo(value) {
// return value;
// }
// 基于泛型封装
T demo<T>(T value) {
return value;
}