最近和移动小伙伴一起重新学习 Dart ,主要是看 CoderWhy 视频教程 讲得挺不错的,顺便做了个笔记

变量声明

/***************  声明变量 ******************/
void defineVariable() {
  // 变量声明(var/final/const)
  // var 声明变量
  var age = 20;
  // age = "abc";
  age = 30;
  // final声明常量
  final height = 1.88;

  // const声明常量
  const address = "深圳市";

  // final和const的区别
  // const date1 = DateTime.now(); 写法错误
  final date2 = DateTime.now();

  // 📢 在Dart 2.0 之后, const可以省略
  const p1 = const Person("hello");
  const p2 = const Person("world");
  const p3 = const Person("zhengzeqin");

  print(identical(p1, p2)); // true
  print(identical(p2, p3)); // false

  // 📢 在Dart 2.12 之后, 默认可选类型需要加 ? 以前默认不加
  String? optionName = null;
  optionName = "zhengzeqin";
}

final 和 const 的区别

  • const 必须赋值常量值(编译期间需要有一个确定的值)
  • final 可以通过计算/函数获取一个值(运行期间来确定一个值)

runtimeType

runtimeType : 用于获取变量当前的类型

var name = 'zhengzeqin';
print(name.runtimeType); // String

dynamic的使用

类似 swift Any 声明动态变量

question

dynamic 与 Object 区别?

// 知识点: Object和dynamic的区别
// 父类应用指向子类对象
// Object和dynamic
// Object调用方法时, 编译时会报错
// dynamic调用方法时, 编译时不报错, 但是运行时会存在安全隐患

// 错误示例
Object obj = "why";
print(obj.substring(1));

// 明确声明
dynamic obj2 = 123;
print(obj2.substring(1));

identical

判断两个变量类型是否一致
补充:const 修饰的常量对象,创建是同一个对象(共享对象),注意类需要定义 const 构造函数

// 在Dart2.0之后, const可以省略
const p1 = const Person("hello");
const p2 = const Person("world");
const p3 = const Person("zhengzeqin");

print(identical(p1, p2)); // true
print(identical(p2, p3)); // false

class Person {
  final String name;
  const Person(this.name);
}

数据类型

数字类型

int double

字符串

/***************  字符串 ******************/
void stringFunction() {
  // 1.定义字符串的方式
  var s1 = 'Hello World';
  var s2 = "Hello Dart";
  var s3 = 'Hello\'Fullter';
  var s4 = "Hello'Fullter";

  // 2.表示多行字符串的方式
  var message1 = '''
  哈哈哈
  呵呵呵
  嘿嘿嘿''';

  print('my name is ${message1}');

  // 3.拼接其他变量
  var name = 'zhengzeqin';
  var age = 18;
  var height = 1.88;
  print('my name is ${name}, age is $age, height is $height');
}

布尔类型

不存在非零即真

dart 声明变量不存在非零即真情况

var flag = "abc";
if (flag) { // 错误示例
  print("执行代码");
}

字符串

  • 支持 “”, ‘’, “”” “”” 三种方式
  • “”” “”” 支持换行
  • 强调: ${变量}, 那么{}可以省略,如果是函数或者表达式计算则需要不可以省略
// 字符串
void stringFunction() {
  // 1.定义字符串的方式
  var s1 = 'Hello World';
  var s2 = "Hello Dart";
  var s3 = 'Hello\'Fullter';
  var s4 = "Hello'Fullter";

  // 2.表示多行字符串的方式
  var message1 = '''
  哈哈哈
  呵呵呵
  嘿嘿嘿''';

  print('my name is ${message1}');

  // 3.拼接其他变量
  var name = 'zhengzeqin';
  var age = 18;
  var height = 1.88;
  print('my name is ${name}, age is $age, height is $height');
}

集合

Dart则内置了最常用的三种:List / Set / Map

  • Set和List最大的两个不同就是:Set是无序的,并且元素是不重复的。
void collectFunction() {
  // List定义
  // 1.使用类型推导定义
  var letters = ['a', 'b', 'c', 'd'];
  print('$letters ${letters.runtimeType}');

  // 2.明确指定类型
  List<int> numbers = [1, 2, 3, 4];
  print('$numbers ${numbers.runtimeType}');

  // Set的定义 Set和List最大的两个不同就是:Set是无序的,并且元素是不重复的。
  // 1.使用类型推导定义
  var lettersSet = {'a', 'b', 'c', 'd'};
  print('$lettersSet ${lettersSet.runtimeType}');

  // 2.明确指定类型
  Set<int> numbersSet = {1, 2, 3, 4};
  print('$numbersSet ${numbersSet.runtimeType}');

  numbers = Set<int>.from(numbers).toList();
  print(numbers);

  // 添加/删除/包含元素
  numbers.add(5);
  numbersSet.add(5);
  print('$numbers $numbersSet');

  numbers.remove(1);
  numbersSet.remove(1);
  print('$numbers $numbersSet');

  print(numbers.contains(2));
  print(numbersSet.contains(2));

  // List根据index删除元素
  numbers.removeAt(3);
  print('$numbers');

  // Map的定义
  // 1.使用类型推导定义
  var infoMap1 = {'name': 'harden', 'age': 18};
  print('$infoMap1 ${infoMap1.runtimeType}');

  // 2.明确指定类型
  Map<String, Object> infoMap2 = {'height': 1.88, 'address': '深圳市'};
  print('$infoMap2 ${infoMap2.runtimeType}');

  // Map的操作
  // 1.根据key获取value
  print(infoMap1['name']); // harden

  // 2.获取所有的entries
  print('${infoMap1.entries} ${infoMap1.entries.runtimeType}'); // (MapEntry(name: why), MapEntry(age: 18)) MappedIterable<String, MapEntry<String, Object>>

  // 3.获取所有的keys
  print('${infoMap1.keys} ${infoMap1.keys.runtimeType}'); // (name, age) _CompactIterable<String>

  // 4.获取所有的values
  print('${infoMap1.values} ${infoMap1.values.runtimeType}'); // (harden, 18) _CompactIterable<Object>

  // 5.判断是否包含某个key或者value
  print('${infoMap1.containsKey('age')} ${infoMap1.containsValue(18)}'); // true true

  // 6.根据key删除元素
  infoMap1.remove('age');
  print('${infoMap1}'); // {name: harden}
}

question

  1. 下面写法有错吗?
  var info<String, Object> = {
    "name": "why",
    "age": 18
  };
```  
> 2. var names = ["abc", "cba", "nba", "cba"]; 这个如果需要指定类型怎么写呢?


## 函数
```dart
返回值 函数的名称(参数列表) {
  函数体
  return 返回值
}

可选参数

可选参数可以分为 命名可选参数位置可选参数

  • 📢 位置可选参数:对应的顺序不可以错
  • dart 中没有函数的重载
命名可选参数: {param1, param2, ...}
位置可选参数: [param1, param2, ...]

// 可选参数: 位置可选参数 - 命名可选参数
// 注意: 只有可选参数才可以有默认值
// 位置可选参数: [int age, double height]
// 实参和形参在进行匹配时, 是根据位置的匹配
void sayHello1(String name, [int age = 10, double height = 2, int money = 0]) {
  print("sayHello1 name: $name  age: $age age: $height money: $money");
}

// 命名可选参数
void sayHello2(String name, {int age = 10, double height = 3.14}) {
  print("sayHello2 name: $name  age: $age age: $height");
}

question

位置可选参数可选参数,一定要按顺序才能传参吗?如果只想传 money 参数,不想传其他参数怎么办呢?

函数是一等公民

可以将函数赋值给一个变量, 也可以将函数作为另外一个函数的参数或者返回值来使用

void firstFunction() {
  // 1.直接找到另外一个定义的函数传进去
  // test(bar);

  // 2.匿名函数 (参数列表) {函数体};
  translationFunc(() {
    print("匿名函数被调用");
    return 10;
  });

  var movies = ['无间道1', '无间道2', '无间道3'];

  //  使用forEach遍历: 有名字的函数
  printElement(item) {
    print(item);
  }

  movies.forEach(printElement);

  // 使用forEach遍历: 匿名函数
  movies.forEach((item) {
    print(item);
  });
  movies.forEach((item) => print(item));

  // 3.箭头函数: 条件, 函数体只有一行代码
  translationFunc(() => print("箭头函数被执行"));

  // 4. 使用别名定义参数 要求: 传入一个函数 & 匿名函数
  calculateFunc((num1, num2) {
    return num1 * num2;
  });

  // 5. 声明函数,调用
  var add = addFunc();
  print(add(20, 30));
}

// 函数可以作为另外一个函数的参数
void translationFunc(Function foo) {
  foo();
}

void bar() {
  print("bar函数被调用");
}

// 使用别名定义函数类型
typedef Calculate = int Function(int num1, int num2);

void calculateFunc(Calculate calc) {
  calc(20, 30);
}

Calculate addFunc() {
  return (num1, num2) {
    return num1 * num2;
  };
}

question

箭头函数支持多行吗?

运算符

条件

  • ??= : 当变量为 null 时,使用后面的内容进行赋值,否则不赋值
  • ?? : 当变量为 null 时,则使用后面的值,否则使用变量的值
void conditionalOperatorFunc() {
  // 1.??=: 
  // 当原来的变量有值时, 那么??=不执行
  // 原来的变量为null, 那么将值赋值给这个变量
  var name = null;
  name ??= "harden";
  print(name);

  // ??:
  // ??前面的数据有值, 那么就使用??前面的数据
  // ??前面的数据为null, 那么就使用后面的值
  var name2 = null;
  var temp = name2 ?? "harden";
  print(temp);
}

级联

  • ... : 类似链式调用
class Animal {
  String? name;

  Animal(this.name);

  void run() {
    print("running");
  }

  void eat() {
    print("eating");
  }
}

// 级联运算符
void cascadeOperatorFunc() {
  // 级联运算符
  var p = Animal("zhengzeqin")
    ..name = "harden"
    ..eat()
    ..run();
}

流程控制

if else
for
while
do-while
break
continue
switch-case
void processControlFunc() {
  for (var i = 0; i < 5; i++) {
    print(i);
  }

  var names = ['harden', 'kobe', 'curry'];
  for (var name in names) {
    print(name);
  }

  var direction = 'east';
  switch (direction) {
    case 'east':
      print('东面');
      break;
    case 'south':
      print('南面');
      break;
    case 'west':
      print('西面');
      break;
    case 'north':
      print('北面');
      break;
    default:
      print('其他方向');
  }
}

类和对象

构造方法

  • 当类中没有明确指定构造方法时,将默认拥有一个无参的构造方法。
    • 📢 在Dart 2.12 之后, 默认可选类型需要加 ? 以前默认不加 否则默认的无参构造函数不能编译通过
  • 📢 当有了自己的构造方法时,默认的构造方法将会失效,不能使用
    • 当然,你可能希望明确的写一个默认的构造方法,但是会和我们自定义的构造方法冲突;
    • 这是因为Dart本身 不支持函数的重载(名称相同, 参数不同的方式)
  • 命名构造方法:自定义命名的构造函数
  • 初始化列表:构造函数体运行之前初始化实例变量,可以使用初始化列表
  • 重定向构造方法:希望在一个构造方法中去调用另外一个构造方法, 这个时候可以使用重定向构造方法
  • 常量构造方法:传入相同值时,我们希望返回同一个对象,这个时候,可以使用常量构造方法 参考 final 部分
  • 工厂构造方法:Dart 提供了 factory 关键字, 用于通过工厂去获取对象
void classFunc() {
  var hunman = Human(12);
  print("hunman: $hunman");
}

class Runner {
  // 📢 在Dart 2.12 之后, 默认可选类型需要加 ? 以前默认不加 否则默认的无参构造函数不能编译通过
  String? name;
  int? age;
}

class Human {
  // 📢 在Dart 2.12 之后, 默认可选类型需要加 ? 以前默认不加 否则默认的无参构造函数不能编译通过
  String? name;
  int? age;

  // Human(String name, int age) {
  //   this.name = name;
  //   this.age = age;
  // }

  // 📢 默认是无惨构造函数,声明参数构造函数,则系统默认无参构造函数就不存在
  // Human(this.name, this.age); // 语法糖等同注释部分

  // 初始化列表页
  Human(this.age) : name = "zhengzeqin age is $age";

  // 命名构造方法
  Human.withArgments(String name, int age) {
    this.name = name;
    this.age = age;
  }

  // 命名一个重定向构造函数
  Human.form(int age) : this(age);

  // 常量构造方法 

  // 工程构造方法, 📢 如果存在了同名构造函数,则无法再实现工厂构造函数
  // factory Human(Int age) {
  //   return Human.withArgments("zhengzeqin", age);
  // }

  // setter && getter
  String? get getName {
    return name;
  }

  set setName(String name) {
    this.name = name;
  }

  
  String toString() {
    return 'name=$name age=$age';
  }
}

抽象类

  • 抽象方法,必须存在于抽象类中
  • 抽象类是使用abstract声明的类
  • 📢 如果有实现体,实现的类可以不实现该抽象方法
// 抽象类
abstract class Shape {
  double getArea();
  // 📢 如果有实现体,实现的类可以不实现该抽象方法
  void getName() {}
}

class Circle extends Shape {
  double? r;

  Circle(this.r);

  
  getArea() {
    final rr = r ?? 0;
    return rr * rr * 3.14;
  }
}

class Reactangle extends Shape {
  double w = 0;
  double h = 0;

  Reactangle(this.w, this.h);

  
  getArea() {
    return w * h;
  }
}

隐式接口

  • Dart 中的接口比较特殊, 没有一个专门的关键字来声明接口.
  • 默认情况下,定义的每个类都相当于默认也声明了一个接口,可以由其他的类来实现(因为Dart不支持多继承)
  • 通过extends 继承类,而 implements 来实现接口
abstract class Player {
  play();
}

abstract class Flyer {
  fly();
}

class SuperManer implements Player, Flyer {
  
  play() {
    print('超人在玩');
  }

  
  fly() {
    print('超人在飞');
  }
}

Mixin

  • 除了可以通过 class 定义类之外,也可以通过 mixin 关键字来定义一个类。
  • 只是通过 mixin 定义的类用于被其他类混入使用,通过 with 关键字来进行混入,等同于多继承
// implements 的方式要求必须对其中的方法进行重新实现, with 则不需要
// 使用 with 方式混入 mixin 类,等同多继承
// on 关键字限定指定类的子类可以使用当前混入(不加默认限定为 Object)
mixin Runing {
  var name = "";
  run() {
    print('在奔跑');
  }
}

mixin Flying {
  fly() {
    print('在飞翔');
  }
}

class SuperWoman with Flying, Runing {
  void sayHello() {
    print("hello my name is $name");
  }
}

泛型

List和Map的泛型

使用者希望使用的是 int 类型,还是double类型, 甚至是一个字符串, 这个时候如何定义呢?

  • 一种方案是使用Object, dynamic 类型, 但是在之后使用时, 非常不方便
  • 另一种方案就是使用泛型.
void genericFunc() {
    // 限制类型
  var names1 = <String>['harden', 'kobe', 'james']; 
  List<String> names2 = ['harden', 'kobe', 'james']; 

 // 对类型进行显示
  Map<String, dynamic> infos1 = {'name': 'harden', 'age': 18};
  var infos2 = <String, dynamic>{'name': 'harden', 'age': 18}; 
}

// 指定 T 是继承 num 类
class Location<T extends num> {
  T x;
  T y;

  Location(this.x, this.y);
}

库的使用

  • import ‘库所在的url’;
  • dart: 前缀表示Dart的标准库,如 dart:io、dart:html、dart:mathimport ‘dart:io’;
  • 当然,你也可以用相对路径或绝对路径的 dart 文件来引用 import ‘lib/student/student.dart’;
  • 库中内容和当前文件中的名字冲突使用 as 起命名空间作用 import ‘lib/student/student.dart’ as Stu;
  • 如果希望只导入库中某些内容,或者刻意隐藏库里面某些内容,可以使用show和hide关键字
    • import ‘lib/student/student.dart’ show Student, Person;
    • import ‘lib/student/student.dart’ hide Person;

export

  • 通过 export 导入多个库

代码示例

参考