一文搞懂Dart

140 阅读7分钟

Dart

不建议第一门语言学习Dart,如果你像我一样是Java开发者,学Java的默认全栈,js肯定也会,那Dart的学习成本对你来说非常低,不要对Dart望而生畏,觉得又得多学一门语言,相信我,Dart非常简单!

Dart函数

Dart拥有类型推断,对于最开始编写的程序,main()函数作为应用入口,语句以;结尾,按End键可以定位到语句末尾,Home键定位到语句句首

main(){
  print('hello world');
}

dart将会推断为

void main(){
  print('hello world');
}

void代表该函数没有返回值

Dart注释

在vscode中

void main() {
  /*print('hello world');*/     ///ctrl+shift+/
  // print('hello world');        ///ctrl+/
  ///print('hello world');
}

Dart命名规则

  • 变量名称必须是由数字、字母、下划线和美元符组成
  • 变量名区分大小写
  • 标识符开头不能是数字
  • 标识符不能是保留字和关键字
  • 变量的名字遵循小驼峰式命名(myNum)
  • 类名遵循大驼峰式命名(MyNum)

Dart变量

指定声明变量

可以使用var声明变量,dart自动推断为String类型

void main() {
  var intNum = 2024;
  print(intNum);
  var doubleNum = 22.15;
  print(doubluNum);
  var str1 = '字符串1';
  print(str1);
  var isTrue = false;
  print(isTrue);
}

结果

2024
22.15
字符串1
false

也可以直接用类型声明,建议直接指定类型,可读性更强

void main() {
  int intNum = 2024;
  print(intNum);
  double doubleNum = 22.14;
  print(doubleNum);
  String str1 = '字符串1';
  print(str1);
  bool isTrue = true;
  print(isTrue);
}

注意大小写区别

对于已经声明的类型,不能改变其类型,num被推断为double类型,将num修改为String

main() {
  var num = 22.15;
  num = 'string';
}

报错:A value of type 'String' can't be assigned to a variable of type 'double'.\nTry changing the type of the variable, or casting the right-hand type to 'double'.`

意思是String类型的值不能赋值给double类型的变量。\n尝试改变变量的类型,或者将右变量类型转换为'double'

未指定声明变量

dynamic可以存储任意类型,变量被声明为dynamic后,编译器则会关闭对此变量的类型检查,这种用法不安全

main() {
  dynamic str = '张三';
  print(str);
  str = 1;
  print(str);
}

结果

张三
1

Dart常量

常量赋值后不能被修改

main() {
  const PI = 3.1415926;
  PI = 3;
  print(PI);
  final PI2 = 3.1415926;
  PI2 = 3;
  print(PI2);
}

报错:

Error: Can't assign to the const variable 'PI'

Error: Can't assign to the final variable 'PI2'

区别:

  • const编译时得到常量值,赋值后不能被修改
  • final运行时常量,可以先不赋值,在运行时赋值一次,赋值后不能被修改
main() {
  const timeNow = new DateTime.now();//错误
  print(timeNow);
  final timeNow2 = new DateTime.now();//运行时才赋值
  print(timeNow2);
}

Dart数据类型

聚合类型

看起来好多好复杂,其实复制到你自己的编辑器里运行一遍就会发现很简单,不想写代码就克隆这个库

Numbers (int, double)

import 'dart:math';
void main() {
  /* 运算符 */
  int intNum = 123;
  double doubleNum = 1.43;
  print(intNum + doubleNum);
  //124.43
  print(intNum - doubleNum);
  //121.57
  print(intNum * doubleNum);
  //175.89
  print(intNum / doubleNum);
  //86.01398601398601
  print(intNum += 8);
  //131
  /* 位运算 */
  print(1 << 1); //左移扩大二倍,相当于*2
  //2
  print(1 >> 1); //右移缩小二倍,相当于/2
  //0
  print(3 | 4); //0011 | 0100 == 0111
  //7
  print(3 & 4); // 0011 & 0100 == 0000
  print(-2 >>>
      1); //无符号右移1111111111111111111111111111111111111111111111111111111111111110->0111111111111111111111111111111111111111111111111111111111111111
  //0
/* 字符串转数字 */
  var intNum2 = int.parse('1111111111111111');
  print(intNum2);
  //1111111111111111
  var doubleNum2 = double.parse("2.3232");
  print(doubleNum2);
  //2.3232
  double lnValue = ln10;
  print(lnValue);
  //2.302585092994046
  double PI = pi;
  print(PI);
  // 3.141592653589793
  int hex = 0xDEADBEEF;
  print(hex);
  // 3735928559
  double exponents = 1.42e5;
  print(exponents);
  // 142000.0
  /* 随机数 */
  var intValue = Random().nextInt(10);
  print(intValue); // Value is >= 0 and < 10.
  var doubleValue = Random().nextDouble();
  print(doubleValue); // Value is >= 0.0 and < 1.0.
  var boolValue = Random().nextBool();
  print(boolValue);
  /* 方法 */
  int a = 1;
  int b = 2;
  int c = 30;
  print(max(a, b));
  // 2
  print(min(a, b));
  //1
  print(sqrt(c));
  //5.477225575051661
}

运算符用法

Strings (String)

main() {
  // Strings (String) UTF-16
  var str1 = '张三';
  var str2 = "李四";
  String str3 = "hello world";
  /* 字符串拼接 */
  print(str1 + "和" + str2);
  //张三和李四
  print("$str1 $str2");
  //张三 李四
  print("括号里使用表达式${str3.toUpperCase()}");
  // 括号里使用表达式HELLO WORLD
  print(str3.toUpperCase());
  //HELLO WORLD

  String str4 = '''
  多行字符串
    第二行
  第三行
  ''';
  print(str4);
  //多行字符串
  //  第二行
  //第三行

  var str5 = '这是 '
      '一段使用'
      " 多个引号的文字";
  print(str5);
  // 这是 一段使用 多个引号的文字

  String str6 = '这不是原始字符串\n有换行\n';
  String str7 = r'这是原始字符串\n没换行\n';
  print(str6);
  //这不是原始字符串
  //有换行
  //
  print(str7);
  //这是原始字符串\n没换行\n
  /* 数字转字符串 */
  String str8 = 1.toString();
  print(str8);
  //1
  String str9 = 3.14159.toStringAsFixed(2);
  print(str9);
  //3.14
}

Booleans (bool)

main() {
  bool flag = true;
  print(flag);
  //true
  var fullName = '';
  if (fullName.isEmpty) {
    print("空");
    //空
  } else {
    print("非空");
  }
}

Records ((value1, value2))(dart3.0后才有的特性,类似于python里的元组)

记录是一种匿名的、不可变的聚合类型,记录对象的数据不允许被修改

位置字段用$访问,命名字段直接.

main() {
  var record =
      ('first', a: 2, b: true, 'last'); //'first' 和 'last' 是位置字段,而 a 和 b 是命名字段
  //(first, last, a: 2, b: true)
  print(record.runtimeType); //运行时类型
  //(String, String, {int a, bool b})
  print(record.$1); //位置字段没有冒号
  //first
  print(record.$2); //位置字段没有冒号
  //last
  print(record.a); //命名字段有冒号
  // 2
  print(record.b); //命名字段有冒号
  // true

  /* 指定位置类型 */
  (int x, int y) recordXY = (3, 4);
  print(recordXY);
  //(3, 4)
  print(recordXY.$1); //位置字段
  //3
  /* 指定命名类型 */
  ({int x, int y}) recordXY2 = (x: 3, y: 4);
  print(recordXY2);
  //(x: 3, y: 4)
  print(recordXY2.x); //命名字段
  //3

  (int x, int y, int z) point = (1, 2, 3);
  (int r, int g, int b) color = (1, 2, 3);

  print(point == color); //两个记录具有相同的形状(字段集),并且其对应字段具有相同的值,则它们相等
  //true
}

Lists (List, 数组)

main() {
  var l1 = ["张三", 21, true];
  print(l1);
  //[张三, 21, true]
  print(l1[0]);
  //张三
  print(l1.length);
  //3
  print(l1.isEmpty);
  //false
  print(l1.isNotEmpty);
  //true
  print(l1.reversed); //反转显示列表
  // (true, 21, 张三)
  print(l1.reversed.toList()); //反转并转换成列表
  // [true, 21, 张三]
  l1.fillRange(1, 2, '左闭右开');
  print(l1);
  //[张三, 左闭右开, true]
  String str = l1.join(','); //以,连接转换为字符串
  print(str);
  //张三,左闭右开,true
  l1 = str.split(','); //以,分割为列表
  // [张三, 左闭右开, true]
  print(l1);
  List l2 = <String>["这", "是", "字符串"];
  print(l2);
  //[这, 是, 字符串]

  List l3 = [];
  print(l3);
  //[]
  l3.add("我是");
  l3.add("添加");
  l3.addAll(['多条', '数组']);
  print(l3);
  //[我是, 添加, 多条, 数组]
  // l3.remove('数组');//与removeAt效果相同
  l3.removeAt(3);
  print(l3);
  //[我是, 添加, 多条]
  l3.clear();
  print(l3);
  //[]

  /* 常量列表 */
  List l4 = const [1, 2, 3];
  // l4.add(1);//检查时不报错,运行时常量不能修改而报错
  print(l4);

  List l5 = <int>[];
  l5 = List<int>.filled(5, 4); //5个4,固定长度无法再修改
  // l5.add(1);//检查时不报错,运行时报错Cannot add to a fixed-length list
  print(l5);
  // [4, 4, 4, 4, 4]

  List l6 = <int>[1, 2, 3, 4, 5, 6];
  print(l6.indexOf(2)); //找参数的索引,没找到返回-1
  //1

  List<int> l7 = [5, -1, 4, -3, 2];
  l7.sort((a, b) => a.abs().compareTo(b.abs())); //abs返回绝对值,按绝对值排序
  print(l7);
  // [-1, 2, -3, 4, 5]
  l7.sort((a, b) => a.compareTo(b)); //按小到大排序
  print(l7);
  //[-3, -1, 2, 4, 5]
  l7.sort((a, b) => b.compareTo(a)); //按大到小排序
  print(l7);
}

方法

Sets (Set)

无序集合,不重复的元素的集合

main() {
  var names = <String>{
    '张三',
    '李四',
    '王五',
    '赵六',
    '孙七',
  };
  names.addAll({'周八', '吴九'});
  names.add('郑十');
  print(names);
  //{张三, 李四, 王五, 赵六, 孙七, 周八, 吴九, 郑十}
  print(names.length);
  //8
  names.remove("郑十");
  print(names);
  //{张三, 李四, 王五, 赵六, 孙七, 周八, 吴九}
  print(names.contains('王八')); //是否包含
  // false
  print(names.containsAll(['周八', '吴九'])); //是否包含
  //true

  var name1 = Set<String>();
  name1.addAll(['jack', 'tony', 'max']);

  var name2 = Set.from(['tony', 'bily']);
  print(name2);
  //{tony, bily}
  var intersection = name1.intersection(name2);//查看交集
  print(intersection);
  //{tony}
}

Maps (Map)

映射

main() {
  Map week = {
    "monday": 1,
    "tuesday": 2,
    "wednesday": 3,
    "thursday": 4,
    "friday": 5,
    "saturday": 6,
    "sunday": 7
  };
  print(week);
  //{monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 7}
  print(week["monday"]);
  //1
  print(week.length);
  //7
  print(week.containsKey("monday")); //是否存在
  //true
  week.remove("monday"); //移除
  print(week);
  //{tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 7}
  week.putIfAbsent("monday", () => 10); //如果指定的键不存在,则将键值对添加到 Map 中
  print(week);
//{tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 7, monday: 10}

  var kv = Map<int, String>();
  kv[0] = 'hello';
  kv[1] = 'world';
  kv[2] = 'ha';
  print(kv);
  //{0: hello, 1: world, 2: ha}
}

map用法

Dart算术运算符

main() {
  int a = 13;
  int b = 5;
  print(a + b); //加
  print(a - b); //减
  print(a * b); //乘
  print(a / b); //除
  print(a % b); //其余
  print(a ~/ b); //取整
}

Dart关系运算符

main() {
  int a = 5;
  int b = 3;
  print(a == b); //判断是否相等
  print(a != b); //判断是否不等
  print(a > b); //判断是否大于
  print(a < b); //判断是否小于
  print(a >= b); //判断是否大于等于
  print(a <= b); //判断是否小于等于
}

Dart逻辑运算符

main() {
  bool a = true;
  bool b = false;

  print(!a); //取反
  print(a && b); //与
  print(a || b); //或,全为flase输出false
}

Dart赋值运算符

main() {
  double a = 4;
  double b = 5;
  print(a += b);//a=a+b
  print(a -= b);
  print(a *= b);
  print(a /= 2);
  print(a %= b);
}

Dart条件表达式

main() {
  var flag = true;
  if (flag) {
    print("true");
  } else {
    print(false);
  }
/* 三目运算 */
  String str = flag ? 'true' : 'false';
/* ?? */
  var a;
  var b = a ?? 10; //a有值则是原值,空则赋值
  print(b);
}

Dart流程控制

main() {
  for (int i = 0; i < 10; i++) {
    print(i);
  }
  /* 计5的阶乘 */
  int sum = 1;
  for (int j = 1; j <= 5; j++) {
    sum *= j;
  }
  print(sum);
  /* while */
  int i = 0;
  while (i<10) {
    i += 1;
    print("while");
  }
}

do while、break、continue和其他语言一样

Dart遍历

遍历方法其他聚类set、map也适用

main() {
  List l1 = ['相机', '内存卡', '电池', '镜头'];
/* for */
  for (int i = 0; i < l1.length; i++) {
    print(l1[i]);
  }
/* for in */
  for (var item in l1) {
    print(item);
  }
/* forEach */ //forEach内部是一个没有名字的函数,一行代码可以用=>
  l1.forEach((value)=>print(value));
  /* map */
  List l2 = [1, 2, 3, 4, 5, 6];
  var l3 = l2.map((value) {
    return value * 2;
  });
  print(l3);
  //(2, 4, 6, 8, 10, 12)
  /* where 筛选满足条件的元素*/
  var l4 = l2.where((val) {
    return val > 5;
  });
  print(l4);
  //(6)
  /* any 任何一个满足条件就返回true*/
  var l5 = l2.any((v) {
    return v > 5;
  });
  print(l5);
  //true
  /* every 每一个都满足条件返回true*/
  var l6 = l2.every((v) {
    return v > 5;
  });
  print(l6);
  //false
}

Dart函数

int add(int a, int b) {
  return a * b;
}

main() {
  print("自定义方法" + add(1, 2).toString());
  //自定义方法2
  fn2(fn1); //将fn1函数作为参数传入fn2
  //方法1
}

fn1() {
  print("方法1");
}

fn2(fn) {
  fn(); //执行传入的方法
}
main() {
  str();
  /* 自执行方法 */
  //(匿名方法)(),也可以传参((接收参数){})(传入参数)
  (() {
    print('world');
  })();
}
/* 匿名方法:没有名字的方法 */
//匿名方法的返回值赋值给str
var str = () {
  return "hello";
};

闭包

/* 2、匿名方法:没有名字的方法 */
//匿名方法的返回值赋值给str
var str = () {
  return "hello";
};
main() {
  str();
  /* 1、自执行方法 */
  //(匿名方法)(),也可以传参((接收参数){})(传入参数)
  (() {
    print('world');
  })();
  /* 
3、闭包
全局变量:常驻内存,污染全局
局部变量:不常驻内存,会被垃圾回收,不污染全局
闭包:函数嵌套函数,内部函数调用外部函数的变量或者参数,变量或参数不会被系统回收
做到了常驻内存但不污染全局
写法:函数嵌套函数并返回里面的函数
 */
  fn() {
    int a = 1;
    return () {
      //这里是一个匿名函数
      a++;
      print(a);
    };
  }

  var b = fn();
  b();
  b();
  b();
}

Dart class类

//late显式声明一个非空的变量,但不初始化
// late延迟初始化变量
class Person {
  int id = 1;
  String name = '张三';
  late int age;
  void getInfo() {
    print("$id***$name");
    print("${this.id}***${this.name}"); //this指这个类
  }
  void setInfo(int age) {
    this.age = age;
  }
}
main() {
  //实例化
  var p = new Person();
  print(p.name);
  //张三
  p.getInfo();
  // 1***张三
  // 1***张三
  p.setInfo(21); //age初始化延迟到了实例化之后,如果不初始化,p.age会报错age没有初始化
  print(p.age);
  //21
}

Dart构造函数

无参构造

class Person {
  int id = 1;
  String name = '张三';
  late int age;

/* 构造函数 */
  Person() {
     print('这是默认构造函数,实例化时触发');
   }
}

main() {
  Person p = new Person();
  //这是默认构造函数,实例化时触发
}

有参构造

class Person {
  int id = 1;
  String name = '张三';
  late int age;
  Person(int age) {
    this.age = age;
    print('有参构造');
  }
  /* 简写 */
  // Person(this.age);
}
main() {
  Person p = new Person(21);
  //有参构造
  print(p.age);
  //21
}

命名构造函数

class Person {
  late int id; //late延迟初始化
  late String name;
  late int age;
  Person(this.id, this.name, this.age);

  /* 命名构造函数 */
  Person.setName(String name) {
    this.name = name;
  }
  Person.setAge(int age) {
    this.age = age;
  }
}

main() {
  Person p1 = new Person(1, '王五', 21);
  print(p1.id);
  //1
  Person p2 = new Person.setName('王五');
  print(p2.name);//其他属性没有初始化,访问会报错没有初始化
  //王五
  var p3 = new Person.setAge(21);
  print(p3.age);
}

Dart计算属性get/set

import 'dart:math';

class Circle {
  double radius;

  Circle(this.radius);

  double get diameter => radius * 2;

  set diameter(double value) {//这个方法不需要调用就计算出了结果
    radius = value / 2;
  }

  double get area => pi * radius * radius;//这个方法不需要调用就计算出了结果
}

void main() {
  var circle = Circle(5);
  print('半径:${circle.radius}');
  print('直径:${circle.diameter}');
  print('面积:${circle.area}');

  circle.diameter = 10;
  print('新半径:${circle.radius}');
  print('新直径:${circle.diameter}');
  print('新面积:${circle.area}');
}

Dart面向对象编程(oop)

封装:将一些属性和相关方法封装在一个对象中,对外隐藏内部具体实现细节

可见性:Dart只有public和private,默认为public,设置private保证谁实例谁用

定义私有缺一不可

  • 需在属性或者方法前加下划线_
  • 且将class类抽离成单独的文件存放
  • 访问私有方法通过共有方法间接访问
import 'dart26.dart';//导入class
main() {
  Person p1 = new Person(1, '王五', 21);
  print(p1.getName());
}

dart26.dart

class Person {
  late int _id; //late延迟初始化
  late String _name;
  late int _age;
  Person(this._id, this._name, this._age);
  getName() {
    return this._name;
  }
}

错误写法

class Child {
  late int _age;
  late String _name;
}

main() {
  Child c = new Child();
  /* 这种放在一个文件里的做法虽然封装了属性,但没有保证安全性,只有将class抽离成单个文件才实现了真正的private */
  c._age = 21;
  print(c._age);
  // 21
}

继承:一个子类”拥有“父类的属性,实现代码复用

通过extends关键字实现,只继承属性和方法,不继承构造函数

class Father {
  int age=40;
  String name='张三';
  void printInfo() {
    print("${this.age}-----${this.name}");
  }
}
class Child extends Father {

}
main() {
  Child c = new Child();
  c.printInfo();
  //40-----张三
}
  • 构造函数不能被继承,super调用父类构造函数,通过.xxx可以调用命名构造函数

  • 子类可以重写父类方法

  • 子类可以添加额外的方法和属性

class Father {
  int age;
  String name;
  Father(this.age, this.name);
    //命名构造函数
  Father.setInfo(this.age, this.name);
  void printInfo() {
    print("${this.age}-----${this.name}");
  }

  void run() {
    print('父类方法');
  }
}

class Child extends Father {
  Child(super.age, super.name); //构造函数不能继承,通过super关键字给父类构造函数传参
  Child.setInfo(super.age, super.name);
  //重写父类方法
  @override
  void run() {
    print('重写父类方法');
  }
}

main() {
  Father f = new Father(40, '张三');
  f.printInfo();
  // 40-----张三F
  f.run();
  //父类方法
  Father f2 = new Father.setInfo(30, '老王');
  f2.printInfo();
  //30-----老王
  Child c = new Child(20, '李四');
  c.printInfo();
  //20-----李四
  c.run();
  //重写父类方法
}

学java的看到这里会感到非常亲切

多态:去调用同一函数,产生了不同的行为

class Animal {
  void makeSound() {
    print('动物发出声音');
  }
}
class Dog extends Animal {
  @override
  void makeSound() {
    print('狗发出汪汪的声音');
  }
}
class Cat extends Animal {
  @override
  void makeSound() {
    print('猫发出喵喵的声音');
  }
}
/* 调用这一个函数传入不同的参数有不同的反应 */
void animalMakeSound(Animal animal) {
  animal.makeSound();
}
main() {
  Animal animal = Animal();
  Dog dog = Dog();
  Cat cat = Cat();

  animalMakeSound(animal); 
  // 动物发出声音
  animalMakeSound(dog);    
  // 狗发出汪汪的声音
  animalMakeSound(cat);    
  // 猫发出喵喵的声音
}

Dart静态成员

通过static关键字实现,看到这里你会发现Dart的语法是js+ts+python+java+c+++kotlin,终极缝合怪

如果Person类按以下方式定义,我们需要实例化后通过实例对象.name去访问name

class Person {
  int age = 1;
  String name = '张三';
}

而添加static关键字后直接通过类名来访问

class Person {
  int age = 1;
  /* 静态成员 */
  static String name = '张三';
  /* 静态方法 只能访问静态成员*/
  static void show() {
    print(Person.name);
  }
  /* 非静态方法 可以访问静态成员和非静态成员*/
  void printInfo() {
    print(this.age);//非静态属性
    print(Person.name);//静态属性
    Person.show();//静态方法
  }
}
main() {
  print(Person.name);
  // 张三
  Person.show();
  // 张三
  Person p = new Person();
  p.printInfo();
  // 1
  // 张三
  // 张三
}

Dart抽象类

通过abstract关键字实现

  • 抽象类不能被实例化,只有继承抽象类的子类才能被实例化
  • 子类继承抽象类必须重写其中的抽象方法
  • 抽象方法不能用abstract关键字声明,没有方法体的方法称为抽象方法
  • 实现抽象接口必须实现抽象接口里的所有属性和方法
abstract class Animal {
  makeSound();
}
class Dog extends Animal {
  @override //重写方法
  void makeSound() {
    print('狗发出汪汪的声音');
  }
}
class Cat extends Animal {
  @override //重写方法
  void makeSound() {
    print('猫发出喵喵的声音');
  }
}
main() {
  Animal dog = new Dog();
  dog.makeSound();
  //狗发出汪汪的声音
  Animal cat = new Cat();
  cat.makeSound();
  //猫发出喵喵的声音
}

都调用了makeSound方法,其实这也是一个多态的例子

Dart接口

接口(Interface)是一种约定,用于定义类应该遵循的行为

Dart没有interface关键字,声明class就是接口,通过implements关键字实现接口

/* 抽象接口 */
abstract class Drawable {
  void draw();
}
class Circle implements Drawable {
  @override
  void draw() {
    print('绘制圆形');
  }
}
class Square implements Drawable {
  @override
  void draw() {
    print('绘制正方形');
  }
}
void drawShape(Drawable shape) {
  shape.draw();
}
main() {
  Circle circle = Circle();
  Square square = Square();
  circle.draw();
  // 绘制圆形
  square.draw();
  // 绘制正方形

/* 多态 */
  drawShape(circle);
  // 绘制圆形
  drawShape(square);
  // 绘制正方形
}

Dart Mixin

Java中继承只能有一个父类,Dart也是一样的,Java可以通过 孙extends父,父extends爷来实现多重继承,Dart通过withwith关键字多重继承来实现代码重用,但它不是继承

userService重用了日志和通知的方法

productService重用了日志和通知的方法

mixin LoggerMixin {
  void log(String message) {
    print('日志记录:$message');
  }
}
mixin MessageMixin {
  void note(String message) {
    print('通知信息:$message');
  }
}
class UserService with LoggerMixin, MessageMixin {
  void createUser(String username) {
    log('创建用户:$username');
    note('通知用户:$username');
  }
}
class ProductService with LoggerMixin, MessageMixin {
  void createProduct(String name) {
    log('创建产品:$name');
    note('通知产品:$name');
  }
}
main() {
  var userService = UserService();
  userService.createUser('张三');

  var productService = ProductService();
  productService.createProduct('手机');
}

Dart泛型

  • 解决方法/类/接口的重用性,使其支持多种类型
  • 避免类型转换和运行时错误
  • 抽象和通用性
  • 减少重复代码和增加性能,泛型是在编译时进行类型擦除的,不会引入运行时的性能开销

泛型方法

T getInfo<T>(T value) {
  return value;
}
main() {
  print(getInfo(21));
  print(getInfo('既可以返回num又可以返回String'));
  /* 指定类型 */
  print(getInfo<List> ([1,2,3,4,5]));
  //[1, 2, 3, 4, 5]
} 

泛型类,该栈可以接受多种类型的元素

class Stack<T> {
  List<T> items = [];
  void push(T item) {
    items.add(item);
  }

  T? pop() {
    //?代表可空类型
    if (items.isNotEmpty) {
      return items.removeLast();
    }
    return null;
  }

  bool get isEmpty => items.isEmpty; //计算属性
}

main() {
  var stack = Stack<int>();
  // 入栈
  stack.push(1);
  stack.push(2);
  stack.push(3);
  //出栈
  while (!stack.isEmpty) {
    print(stack.pop());
  }

  var stringStack = Stack<String>();
  // 入栈
  stringStack.push('Hello');
  stringStack.push('World');
  stringStack.push('Dart');
  //出栈
  while (!stringStack.isEmpty) {
    print(stringStack.pop());
  }
}

泛型接口

Dart compareTo函数

  • 如果指定的数与参数相等返回 0。
  • 如果指定的数小于参数返回 -1。
  • 如果指定的数大于参数返回 1
abstract class Comparable<T> {//支持特定类型
  int compareTo(T other);
}

class Person implements Comparable<Person> {
  String name;
  int age;

  Person(this.name, this.age);

  @override
  int compareTo(Person other) {
    return age.compareTo(other.age);
  }
}

void main() {
  var san = Person('张三', 30);
  var si = Person('李四', 25);

  print(san.compareTo(si));
  // 1
  print(si.compareTo(san));
  // -1
}

is类型判断

is用于判断是不是某种类型

class Person {
  int age = 1;
  String name = '张三';

  void printInfo() {
    print(this.name);
  }
}
main() {
  Person p = new Person();
  if (p is Person) {
    p.name = '王八';
  }
  p.printInfo();
  //王八
  if (p is Object) {//所有类型都继承于object
    p.name = '李四';
  }
  p.printInfo();
  //李四
}

Dart级联操作

class Person {
  int age = 1;
  String name = '张三';
  void printInfo() {
    print(this.age);
  }
}
main() {
  Person p = new Person();
  // p.printInfo();
  // //1
  // p.age = 40;
  // p.printInfo();
  // //40
  /* 以上代码可以简化为 */
  p
    ..age = 1
    ..printInfo()
    ..age = 40
    ..printInfo();
}

Dart异步方法

Future<String> fetchData() async {//async将方法变为异步方法
  // 延时模拟异步操作
  await Future.delayed(Duration(seconds: 2));
  return '数据已获取';
}

void main() async {
  print('开始获取数据');
  String result = await fetchData();//await等待方法执行完成
  print(result);
  print('数据获取完成');
}

空安全和类型断言

void printLength(String? str) {
  //?代表可空类型
  try {
    print(str!.length); //!有值正常执行,无值抛出异常,在这里捕获异常所以打印str为空
  } catch (e) {
    print('str为空');
  }
}
/* 等价于下面的函数 */
void printLength2(String str){
  // ignore: unnecessary_null_comparison
  if(str!=null){
    print(str.length); 
  }else{
    print('str为空');
  }
}
main() {
  printLength(null);
}

性能优化

通过const关键字实现常量构造函数,传入的数据相同构建的对象内存地址也相同,相同实例共享存储空间,不同数据的实例新建空间

成员变量需要定义为final,运行时赋值一次后不能再更改

class Person {
  final String name;
  final int age;

  const Person(this.name, this.age);
}

void main() {
  var person1 = const Person('张三', 25);
  var person2 = const Person('张三', 25);
  var person3 = const Person('李四', 30);

  print(identical(person1, person2)); //查看两个对象是否是同一个实例
  // true
  print(identical(person1, person3)); 
  // false
}

Dart库

安装库

方式一

新建 pubspec.yaml文件(flutter应用存在该文件不需要新建)

添加依赖配置

执行pub get

或者

执行flutter pub get(pubspec.yaml文件存在)

方式二

执行flutter pub add css_colors添加包

执行flutter pub remove css_colors删除包

# 包重命名
import '地址' as 名字;
#只导入部分
import '地址' show 名字;
#隐藏部分
import '地址' hide 名字;