Dart学习笔记(曾怀壮志追纯粹,欲守初心辟径专)

120 阅读6分钟

无人扶我鸿蒙志,我自钻研 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);
  });
}

image.png

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');
  });
}

image.png

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);
}

image.png

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 程序,根据给定的购物车数据,计算所有被选中的商品的总价。 要求

  1. 定义一个列表 carts,其中每个元素是一个 Map,表示一个商品的信息。
  2. 每个商品的信息包括:
    1. count:商品的数量(整数)
    2. price:商品的单价(浮点数)
    3. selected:商品是否被选中(布尔值)
  3. 遍历 carts 列表,计算所有被选中的商品的总价。
  4. 输出计算结果。
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;
// }

image.png

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');
}

image.png

5.4 匿名函数

1匿名函数赋值给变量,并调用
2可以作为参数传递给其他函数去调用(回调函数)

void main() {
  // 匿名函数
  // 1. 匿名函数赋值给变量,并调用
  Function f = () {
    print('这是一个匿名函数');
  };
  f();

  // 2. 可以作为参数传递给其他函数去调用(回调函数)
  funcDemo(() {
    print('这个匿名函数是个参数');
  });
}

// 定义一个接收函数作为参数的函数
void funcDemo(Function func) {
  func();
}

image.png

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 '返回的网络数据';
}

代码执行出现堵塞

PixPin_2024-11-21_11-54-19.gif

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 '返回的网络数据';
  });
}

这一次的代码顺序执行,没有出现任何的阻塞现象

PixPin_2024-11-21_11-59-59.gif

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 '返回的网络数据';
  });
}

PixPin_2024-11-21_12-14-49.gif

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;
  });
}

PixPin_2024-11-21_14-59-43.gif

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;
}