Dart :-

Dart 是使用 Flutter 框架开发跨平台移动应用的主要编程语言。

Dart 语言 :-

设置

在 Linux 上设置 :-

  1. 使用 apt-get 安装

sudo apt-get update
sudo apt-get install apt-transport-https
wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/dart.gpg
echo 'deb [signed-by=/usr/share/keyrings/dart.gpg arch=amd64] https://storage.googleapis.com/download.dartlang.org/linux/debian stable main' | sudo tee /etc/apt/sources.list.d/dart_stable.list
  1. 然后安装 Dart SDK

sudo apt-get update
sudo apt-get install dart
  1. 修改 PATH 以访问所有 Dart 二进制文件
echo 'export PATH="$PATH:/usr/lib/dart/bin"' >> ~/.profile

在终端运行 Dart 代码 :-

dart filename.dart

功能

Dart 是一种严格类型的编程语言。它支持 AOT (Ahead of time)JIT (Just In Time) 编译。它是一种编译型编程语言,也可以将代码转译成 JavaScript

JIT 编译在开发期间使用,使用一个特别快的编译器。然后,当应用准备好发布时,它会被 AOT 编译。

Dart 可以提供两全其美:极快的开发周期,以及快速的执行和启动时间。

  • dart:core 库包含每个 Dart 程序所需的内置类型、集合和其他核心功能。
  • dart:core 库会自动导入到每个 Dart 程序中。
  • DartJavaScript 一样,是单线程编程语言

代码示例

void main() {
  var firstName = 'Ryzen'; // String type inference
  String lastName = 'FW'; // String type defined
  int number = 100; // integer type
  double cost = 11.40; // fractional value
  dynamic isOkay = true; // dynamic type can holds any type

  print(firstName + ' ' + lastName);
  print(number);
  print(cost);
  print(isOkay);
}
  • main() 是入口点。
  • dart 支持类型推断和类型定义。
  • void 表示该函数不返回任何值,并且是可选的。

导入包并从用户那里获取输入

import 'dart:io';

void main() {
  stdout.writeln('What is your name: ?');
  String name = stdin.readLineSync();
  print('My name is: $name');
}

Dart 语言通用特性

  • 可以放入变量中的一切都是对象,而每个对象都是类的实例。即使是数字、函数和 null 也是对象。
  • 所有对象都继承自 Object 类。
  • Dart 是一种强类型语言。不能将整数值赋给字符串类型等。
  • Dart 支持泛型类型,例如 List<int>(整数列表)或 List<dynamic>(任何类型对象的列表)。
  • 与 Java 不同,Dart 没有 publicprotectedprivate 关键字
  • ( ; ) 分号是语句结尾的强制要求

强类型语言: 变量的类型在编译时就知道。例如:C++JavaSwift

动态类型语言: 变量的类型在运行时知道。例如:PythonRubyJavaScript

数据类型

简单或原始数据类型

  • 整数
  • 双精度
  • 字符串
  • 布尔值
  • dynamic

main() {
  int amount1 = 100;
  var amount2 = 200;

  print('Amount1: $amount1 | Amount2: $amount2 \n');

  double dAmount1 = 100.11;
  var dAmount2    = 200.22;

  print('dAmount1: $dAmount1 | dAmount2: $dAmount2 \n');

  String name1 = 'Ryzen';
  var name2    = 'FW';

  print('My name is: $name1 $name2 \n');

  bool isItTrue1 = true;
  var isItTrue2  = false;
  
  print('isItTrue1: $isItTrue1 | isItTrue2: $isItTrue2 \n');

  dynamic weakVariable = 100;
  print('WeakVariable 1: $weakVariable \n');

  weakVariable = 'Dart Programming';
  print('WeakVariable 2: $weakVariable');
}

intdouble 都是 num 的子类型。num 类型包含基本运算符,如 +, -, /, 和 * 等。

如果我们声明一个变量为 String,我们只能在其中放入 String 值。

我们不能将一种类型赋给另一种静态类型。
  • 我们可以在声明某物时定义类型,或者让编译器决定类型。

  • 这里的 names 是通过类型推断的 String 类型。

  • 而 ages 是我们定义的 int 类型。

void main() {
  var name = 'Ryzen'; // type inference made this as String
  print(name);

  name = 100; // Will show error here
  print(name);

  int age = 30;
  print(age);
}
我们可以在动态类型的变量中放入任何数据

void main() {
  var weakType; // dynamic

  weakType = 'Ryzen';
  print(weakType);

  weakType = 100;
  print(weakType);
}

技巧

  • 最好避免使用 vardynamic 类型,以利用类型安全语言的优势。这将减少错误。

函数中的类型

这里我们首先定义了一个 square 函数,它接受一个整数并返回乘法后的整数值。

doubleSquare 函数接受 double 值并返回 double 类型的值。

dynamicSquare 函数是动态的,在这种情况下最好,因为我们可以传递 intdouble 值。它会根据计算返回结果。因此,如果结果是整数,则返回 int;如果结果是 double,则返回 double

void main() {
  print(square(5));
  print(doubleSquare(5.5));

  print(dynamicSquare(5));
  print(dynamicSquare(5.5));
}

int square(int n) {
  return n * n;
}

double doubleSquare(double d) {
  return d * d;
}

dynamic dynamicSquare(dynamic val) {
  return val * val;
}

字符串

Dart 字符串是 UTF-16 码单元的序列。

var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";

可以通过在字符串前加上 r 来创建“原始”字符串

var s = r'In a raw string, not even \n gets special treatment.';

字符串插值

var age = 35;
var str = 'My age is: $age';
print(str);

多行字符串

var s1 = '''
You can create
multi-line strings like this one.
''';

var s2 = """This is also a
multi-line string.""";

类型转换

这是将字符串转换为数字,或反之亦然的方法

// String -> int
var one = int.parse('1');
assert(one == 1);

// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);

// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');

// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');

常量和 Final

常量是编译时常量值,而 final 变量只能设置一次。

常量示例

  const aConstNum = 0; // int constant
  const aConstBool = true; // bool constant 
  const aConstString = 'a constant string'; // string constant

  print(aConstNum);
  print(aConstBool);
  print(aConstString);

  print(aConstNum.runtimeType);
  print(aConstBool.runtimeType);
  print(aConstString.runtimeType);

Final 示例

final amount = 5;
print(amount);

我们也可以使用 const 关键字来定义常量值

void main() {
  var list = const [1, 2, 3];
  list.add(4); // it will produce error as list points constant values
  print(list);
}

注释

// In-line comment
/*
Block comment
*/

/// Documentation

null 对象

如果一个变量被声明但未赋值,它将包含 null 对象。

void main() {
  int num;
  print(num); // output: null
}

运算符

与 JavaScript 语言相同。所有标准运算符都可以在这里使用。

void main() {
  int num = 10 + 22;
  num = num - 2;

  print(num);

  num = num % 5;
  print(num);

  // relational ==, !=, >=, <=
  if (num == 0) {
    print('Zero');
  }

  num = 100;
  num *= 2;
  print(num);

  // unary operator
  ++num;
  num++;
  num += 1;
  num -= 1;
  print(num);

  // logical && and logical ||
  if (num > 200 && num < 203) {
    print('200 to 202');
  }

  // != Not Equal
  if (num != 100) {
    print('num is not equal to 100');
  }
}

Null 感知运算符

(?.), (??), (??=)

它类似于 Swift 编程语言的 optional (?:) 运算符。这意味着,如果对象为 null,则不执行任何操作。

class Num {
  int num = 10;
}

void main () {
  var n = Num();
  int number;
  
  // we can check null by this
  if (n != null ){
    number = n.num;
  }
  print(number);
}

或者,我们可以使用 Null Aware (?.) 运算符来跳过 if..else 条件。

所以在这种情况下,如果 n 对象为 null,它不会崩溃。

void main () {
  var n = Num();
  int number;
  
  number = n?.num; // null aware
  print(number);
}

代码将崩溃

void main () {
  var n;
  int number;
  
  number = n.num; // no null checking
  print(number);
}

如果为 null (?.) 的安全代码

void main () {
  var n;
  int number;
  
  number = n?.num; // null checking
  print(number); // output: null
}

Null 感知变体二 (??)

如果对象为 null,并且我们想要一个 默认值,我们可以遵循 ?? 运算符。

class Num {
  int num = 10
}

void main () {
  var n = Num();
  int number;
  
  number = n?.num ?? 18; // default value
  print(number);
}

Null 感知变体三 (??=)

如果相应对象为 null,则将其值赋给该对象。

void main() {
  int number;
  number ??= 100;
  print(number);
}

三元运算符

JavaScript 语言 ?: 相同

int x = 100;
var result = x % 2 == 0 ? 'Even' :'Odd';
print(result);

类型测试

as 用于类型转换 is 如果对象具有指定类型则为 True !is 如果对象具有指定类型则为 False

if (emp is Person) {
  // Type check
  emp.firstName = 'Bob';
}

(emp as Person).firstName = 'Bob';

级联表示法

(..) 级联 (..) 允许您对同一对象执行一系列操作。

querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

第一个方法调用 querySelector() 返回一个 selector 对象。级联表示法后面的代码作用于此选择器对象,忽略后续可能返回的任何值。

前面的示例等同于

条件语句

if..else if..else

JavaScript 语言相同。

int number = 100;

if (number % 2 == 0) {
  print('Even');
}
else if (number % 3 == 0) {
  print('Odd');
}
else {
  print('Confused');
}

Switch 语句

JavaScript 语言相同。

int number = 1;

switch(number) {
  case 0:
    print('Even');
    break;
  case 1:
    print('Odd');
    break;
  default:
    print('Confused');
}

循环

JavaScript 语言相同

  1. 标准 for 循环

for (var i = 0; i < 10; ++i) {
  print(i);
}
  1. for-in 循环

var numbers = [1, 2, 3];

for (var n in numbers) {
  print(n);
}
  1. forEach 循环

在这里,我们在 forEach 方法内提供了一个函数。因此 forEach 是一个高阶函数。此外,在此第一个示例中,我们在 forEach 中使用了匿名函数。

var numbers = [1, 2, 3];
  
numbers.forEach((num) => print(num));

我们可以用另一种方式重写此 forEach

void main() {
  var numbers = [1, 2, 3];

  numbers.forEach(printNum);
}

void printNum(num) {
  print(num);
}
  1. While 循环

  int num = 5;

  while (num > 0){ 
    print(num);
    num -= 1;
  }
  1. do-while 循环

  int num = 5;

  do {
    print(num);
    num -= 1;
  } while(num > 0);
  1. Break 和 Continue

void main() {
  for (var i = 0; i < 10; ++i) {
    if (i > 5) break;
    print(i);
  }

  for (var i = 0; i < 10; ++i) {
    if (i % 2 == 0) continue;
    print("Odd: $i");
  }
}

集合

列表

void main() {
  List names = ['Jack', 'Jill'];
  print(names.length);
  for (var n in names) {
    print(n);
  }

  List <int> ages = [18, 20, 33];
  for (var a in ages) {
    print(a);
  }
}

要创建编译时常量列表,请在列表字面量前添加 const

var constantList = const [1, 2, 3];
// constantList[1] = 1; // Uncommenting this causes an error.

展开运算符

Dart 2.3 引入了展开运算符 (…)。例如,您可以使用展开运算符 (…) 将列表的所有元素插入另一个列表

var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);

Null 感知展开运算符

Dart 2.3 引入了 null 感知展开运算符 (…?)。如果展开运算符右侧的表达式可能为 null,您可以使用 null 感知展开运算符 (…?) 来避免异常。

var list;
var list2 = [0, ...?list];
assert(list2.length == 1);

Collection if 和 collection for

Dart 2.3 还引入了 collection if 和 collection for,您可以使用它们通过条件 (if) 和重复 (for) 来构建集合。

这是使用 collection if 创建包含三到四个项目的列表的示例

var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];

这是使用 collection for 在将列表项添加到另一个列表之前对其进行操作的示例

var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');

Set

Dart 中的 set 是无序的唯一项集合。Dart 对 set 的支持由 set 字面量和 Set 类型提供。

// Here is a simple Dart set, created using a set literal:  
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

要创建空 set,请使用 {} 前面加上类型参数,或将 {} 赋给类型为 Set 的变量

var names = <String>{};
// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.
  • 使用 add() 或 addAll() 方法将项添加到现有 set
  • 使用 .length 获取 set 中的项数

var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);

截至 Dart 2.3,set 支持展开运算符 (… 和 …?) 以及 collection if 和 for,就像列表一样。

地图

// Dart infers that gifts has the type Map<String, String> and nobleGases has the type Map<int, String>.
var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

// You can create the same objects using a Map constructor:

var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

如果您查找 map 中不存在的键,则返回 null

var gifts = {'first': 'partridge'};
assert(gifts['fifth'] == null);

要创建编译时常量 map,请在 map 字面量前添加 const

final constantMap = const {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

// constantMap[2] = 'Helium'; // Uncommenting this causes an error.

功能

  • 每个 function 都是 Function 类的对象
  • 每个 function 如果返回某些内容,则应具有 return type。否则它将返回 void

一些例子

void main() {
  showOutput(square(2));
  showOutput(square(2.5));
}

void showOutput(var msg) {
  print(msg);
}

dynamic square(var num) {
  return num * num;
}

箭头函数

Fat Arrow Expression => 或箭头函数

对于函数中的单个表达式,我们可以使用称为Fat Arrow => 的简写语法。它会隐式返回 => 后面的值。它在某种程度上类似于 JavaScript Arrow Function

我们可以通过此方式重定义上面的 square 函数

dynamic square(var num) => num * num;

匿名函数

  • 一个无名函数称为匿名函数,有时也称为 lambda 或闭包。

下面的示例定义了一个带有未类型化参数 item 的匿名函数。该函数为列表中的每个 item 调用,会打印一个包含指定索引处值的字符串。

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

参数

位置参数和命名参数

位置参数像其他语言一样从左开始工作。

void main() {
  print(sum(2, 2));
}

dynamic sum(var num1, var num2) => num1 + num2;

对于命名参数,我们必须在函数签名内的命名参数外部使用 {}

void main() {
  print(sumName(num1: 2, num2: 2));
}

dynamic sumName({var num1, var num2}) => num1 + num2;

我们也可以混合位置参数和命名参数。

默认情况下,命名参数是可选的。因此,我们可以使用 null 感知运算符来检查此可选参数。

void main() {
  print(sum(2, num2: 2));
  print(sum(2));
}

dynamic sum(var num1, {var num2}) => num1 + ( num2 ?? 0 );

您可以在任何 Dart 代码(不仅仅是 Flutter)中用 @required 注释命名参数,以指示它是必需参数。

const Scrollbar({Key key, @required Widget child})

Required 定义在 meta 包中。直接导入 package:meta/meta.dart,或导入另一个导出 meta 的包,例如 Flutter 的 package:flutter/material.dart。

位置可选参数

我们必须在位置可选参数周围使用方括号。就是这样。

因此,我们使用上面的示例在下面重新定义

void main() {
  print(sum(2, 2));
  print(sum(2));
}

dynamic sum(var num1, [var num2]) => num1 + ( num2 ?? 0 );
默认参数值

要为参数提供默认值,它必须被声明为 位置可选命名可选,并在 = 符号后提供默认值。

void main() {
  print(isAdult(1));
  print(isAdult());
}

bool isAdult([int age = 18]) => age >= 18;

生成器

为了惰性地生成一系列值,我们可以使用生成器函数。

Dart 支持两种生成器函数

  1. 同步生成器 – 返回一个可迭代对象
  2. 异步生成器 – 返回一个流对象
  • 可迭代对象是按顺序访问值的集合。
  • 流对象表示异步数据事件。
同步生成器
  • 将函数体标记为 sync* 并使用 yield 来传递值

import 'dart:io';

Iterable<int> countStream(int max) sync * {
  for (int i = 0; i < max; ++i) {
    yield i;
    sleep(Duration(seconds: 1));
  }
}

void main() {
  print('start');
  countStream(5).forEach((data){
    print(data);
  });
  print('end');
}
异步生成器
  • 将函数体标记为 async* 并使用 yield 来传递值

import 'dart:io';

Stream<int> countStream(int max) async * {
  for (int i = 0; i < max; ++i) {
    yield i;
    sleep(Duration(seconds: 1));
  }
}

void main() {
  print('start');
  countStream(5).listen((data){
    print(data);
  },
  onDone: (){
    print("Done");
  });
  print('end');
}

当我们需要使用函数调用在 yield 中生成值时,我们必须使用 yield*

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

  • 来自 dart:core 库的 Object 类是 dart 编程中所有对象类型的基类。
  • 当名称冲突时,请使用 this
  • 默认构造函数 的名称与类名相同
  • 命名构造函数 用于为类实现多个构造函数或提供额外的清晰度

一个带有构造函数、属性和方法的空白类。

class Person {
  String name;
  int age;

  // default constructor
  Person(String name, [int age = 18]) {
    this.name = name;
    this.age = age;
  }

  // named constructor
  Person.guest() {
    name = 'Guest';
    age = 18;
  }

  void showOutput() {
    print('Name: ${this.name}');
    print('Age: ${this.age}');
  }
}

void main() {
  var person1 = Person('Jack');
  Person person2 = Person('Jill', 15);

  person1.showOutput();
  person2.showOutput();

  var person3 = Person.guest();
  person3.showOutput();
}

输出

Name: Jack
Age: 18
Name: Jill
Age: 15

使用语法糖,我们可以像这样写一个更短的默认构造函数。

Dart 自动将同名参数赋给同名属性。

如果需要,我们可以在默认构造函数中包含自动赋值以及其他功能,例如 Vehicle 示例。

class Person {
  String name;
  int age;

  Person(this.name, [this.age = 18]);
}

class Vehicle {
  String model;
  int year;

  Vehicle(this.model, this.year) {
    print(this.model);
    print(this.year);
  }
}

Final 和 Const

finalconst 在定义任何变量之前使用时,不能重新赋值。

  • 但是,如果在类中声明了没有值的 final 变量,则必须在 constructor() 方法中对其进行赋值。

final String person1 = 'Jack';
const String person2 = 'Jill'; // compile time constant

print(person1);
print(person2);

// can not reassign
// person1 = 'aa';
// person2 = 'bb';
  • 实例变量可以是 final
  • 实例变量可以是 static const

class X {
  final name; // type will be defined by inferred value
  static const int age = 10;
  
  X(this.name);
}

main() {
  var x = X('Jack');
  print(x.name);
  print(X.age); // use Classname.StaticVariable
}

类继承

使用 extends 创建子类,并使用 super 引用父类

class Vehicle {
  String model;
  int year;

  Vehicle(this.model, this.year) {
    print(this.model);
    print(this.year);
  }

  void showOutput(){
    print(model);
    print(year);
  }
}

class Car extends Vehicle {
  double price;

  Car(String model, int year, this.price) : super(model, year);

  void showOutput() {
    super.showOutput();
    print(this.price);
  }
}

void main() {
  var car1 = Car('Accord', 2014, 150000);
  car1.showOutput();
}

方法重写

@override 注释将实例成员标记为覆盖同名的超类成员。但它是可选的。

  • @override 注释的目的是为了捕获超类重命名成员,而以前重写了该成员的独立子类可能会默默地继续使用超类实现。
  • 当您无法控制超类 method 的实现时,请使用 @override

class X {
  String name;
  
  X(this.name);
  
  void showOutput() {
    print(this.name);
  }
  
  dynamic square(dynamic val) {
    return val * val;
  }
}

class Y extends X {
  Y(String name) : super(name);
  
  @override
  void showOutput() {
    print(this.name);
    print('Hello');
  }
  
  // not using @override at this time
  dynamic square(dynamic val) {
    return val * val + 2;
  }
}

void main() {
  var obj = Y('Jack');
  obj.showOutput();
  print(obj.square(2));
}

Getters 和 Setters

  • Getters 和 setters 是特殊方法,它们提供对对象属性的读写访问。
  • 每个实例变量都有一个隐式 getter,以及一个setter(如果合适)。
  • 您可以通过实现 getters 和 setters 来创建额外的属性,使用 getset 关键字

class Rectangle {
 num left, top, width, height;

 Rectangle(this.left, this.top, this.width, this.height);

 // Define two calculated properties: right and bottom.
 num get right => left + width;
 set right(num value) => left = value - width;
 num get bottom => top + height;
 set bottom(num value) => top = value - height;
}

void main() {
 var rect = Rectangle(3, 4, 20, 15);
 assert(rect.left == 3);
 rect.right = 12;
 assert(rect.left == -8);
}

抽象方法

实例、getter 和 setter 方法可以是抽象的,定义接口但将其实现留给其他类。抽象方法只能存在于抽象类中。

abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}

抽象类

使用 abstract 修饰符定义抽象类——一个不能实例化的类。抽象类对于定义接口很有用

// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods...

  void updateChildren(); // Abstract method.
}
多态

// Abstract Class and Abstract Method

abstract class Human {
  String _type;
  
  void showType();
}

class Man extends Human {
  Man(String type){
    _type = type;
  }
  void showType() {
    print(_type);
  }
}

class Woman extends Human {
  Woman(String type){
    _type = type;
  }

  void showType() {
    print(_type);
  }
}

void main() {
  // polymorphism
  Human human;
  Man person1 = Man('man');
  Woman person2 = Woman('woman');

  human = person1;
  human.showType();

  human = person2;
  human.showType();
}

Mixins

Dart 语言中,只有单继承。要共享类之间的功能,我们可以使用 mixins

Mixins 是一种在多个类层次结构中重用类代码的方式。

要使用 mixin,请使用 with 关键字,后跟一个或多个 mixin 名称。

  • 为类添加功能:mixins

// Mixins

class Robot {
  void perform(){
    print("Performing");
  }
}

mixin Walk {
  void perform(){
    print("Walking");
  }
}

mixin Run {
  void perform(){
    print("Running");
  }
}

class IRobo extends Robot with Walk, Run {
  void showActivity(){
    perform();
  }
}

void main() {
  IRobo robo = IRobo();
  robo.showActivity();
}

Mixins 的另一个例子

mixin CanFly {
  void fly(String name) {
    print('$name flying');
  }
}

mixin CanDrive {
  void drive(String name) {
    print('$name driving');
  }
}

class Car with CanDrive {

}

class Helicopter with CanFly, CanDrive {
  void perform(String name) {
    fly(name);
    drive(name);
  }
}

void main() {
  Car car = Car();
  car.drive('car');

  Helicopter helicopter = Helicopter();
  helicopter.perform('helicopter');
}

类变量

  • 使用 static 关键字实现类范围的变量和方法。
  • 静态变量在被使用之前不会被初始化

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

静态方法

  • 静态方法(类方法)不操作实例,因此没有对 this 的访问权限

import 'dart:math';

class Point {
  num x, y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}

Enum

Enum 类型定义了一组命名常量。每个 enum 值都有一个 index getter,它返回 enum 声明中该值的零基位置。例如,第一个值索引为 0,第二个值索引为 1。

enum Color {
  red,
  green,
  blue,
}

void main() {
  var color = Color.red;

  if (color == Color.red) {
    print('Red');
  }
}

词法作用域

Dart 是一种词法作用域语言,这意味着变量的作用域是静态确定的,仅通过代码的布局来确定。您可以“跟着花括号向外”查看变量是否在作用域内。

这是一个嵌套函数及其每个作用域级别变量的示例

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

请注意,nestedFunction() 如何使用来自每个级别(一直到顶层)的变量

词法闭包

  • 闭包是一个函数对象,即使在函数在其原始作用域之外使用时,它也可以访问其词法作用域中的变量。
  • 函数可以捕获在周围作用域中定义的变量。

// Function type, dynamic also works
Function makeAdder(int initial) {
  int total = initial;

  // nested function
  int addToTotal(int addBy) {
    total += addBy;
    return total;
  }

  return addToTotal;
}

void main() {
  var adder = makeAdder(2);
  var val = adder(5);
  print(val);

  val = adder(3);
  print(val);
}

输出

7
10

Assert

assert 通常用于检查条件,如果条件为 false,则终止程序执行。

dart 中,assert 仅在开发模式下工作,不在生产代码中。Flutter 只在 debug 模式下启用 assert

// Make sure the variable has a non-null value.
assert(text != null);

// Make sure the value is less than 100.
assert(number < 100);

// Make sure this is an https URL.
assert(urlString.startsWith('https'));

要在 Dart 中启用 assert,您需要运行以下命令

dart --enable-asserts FILENAME.dart 

通常在 Flutter 中,assert 在调试模式下会自动启用。

异常处理

  • throw 错误,请使用 throwrethrow 关键字
  • catch 错误,请遵循示例
  • finally 块是可选的,并且总是运行

int mustGreaterThanZero(int val) {
  if (val <= 0) {
    throw Exception('Value must be greater than zero');
  }
  return val;
}

void letVerifyTheValue(var val) {
  var valueVerification;

  try {
    valueVerification = mustGreaterThanZero(val);
  }
  catch(e) {
    print(e);
  }
  finally {
    if (valueVerification == null) {
      print('Value is not accepted');
    }
    else {
      print('Value verified: $valueVerification');
    }
  }
}

void main() {
  letVerifyTheValue(10);
  // letVerifyTheValue(0);
}

输出

Value verified: 10
Exception: Value must be greater than zero
Value is not accepted
  • 要捕获特定异常类型,请遵循此操作

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

Generics

泛型通常对于类型安全是必需的,但它们的好处不仅仅是让您的代码能够运行

  • 正确指定泛型类型可以生成更好的代码
  • 您可以使用泛型来减少代码重复。

如果您打算让列表只包含字符串,可以将其声明为 List(读作“字符串列表”)。这样,您、您的同事程序员以及您的工具就可以检测到将非字符串赋给列表很可能是一个错误。下面是一个示例

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error

异步编程

Dart 中的异步编程以 Future 和 Stream 类为特征。

futures

  • Dart 代码是单线程的
  • Future<T> 对象表示异步操作的结果,该操作产生类型为 T 的结果。如果结果是不可用值,则 Future 的类型为 Future<void>
  • Future 异步地表示单个值,无论是数据还是错误。

两种处理 Future 的方法

  1. 使用 Future API
  2. 使用 asyncawait 操作

then 中传递 callbackFuture 示例

使用 catchError 来捕获任何错误

Future delayedPrint(int seconds, String msg) {
  final duration = Duration(seconds: seconds);
  return Future.delayed(duration).then((value) => msg);
}

main() {
  print('Life');
  delayedPrint(2, "Is").then((status) {
    print(status);
  }).catchError((err) => print(err));
  print('Good');
}

输出

Life
Good
Is

异步操作与同步行为

Future delayedPrint(int seconds, String msg) {
  final duration = Duration(seconds: seconds);
  return Future.delayed(duration).then((value) => msg);
}

main() async {
  print('Life');
  await delayedPrint(2, "Is").then((status){
    print(status);
  });
  print('Good');
}

输出

Life
Is
Good

我们还可以使用 try..catch 块来捕获异步操作的错误。

另一个例子

task3() 依赖于 task2()

void task1() {
  print('Task 1 Done.');
}

Future <String> task2() async {
  Duration duration = Duration(seconds: 2);
  
  String result;

  await Future.delayed(duration, () {
    print('Task 2 Done.');
    result = ' Task 2 Data';
  });

  return result;
}

void task3(String result) {
  print('Task 3 Done. $result');
}

void main() async {
  task1();
  String result = await task2();
  task3(result);
}

Streams

Stream 是一系列异步事件。与 future 不同,流会在有事件准备好时通知。

  • Stream 类似于 Future
  • Stream 随时间传递零个或多个值或错误。
  • 要创建 Stream,请使用 StreamController
  • 默认情况下,Stream 设置为单次订阅。因此,两个 listen 不会工作。
  • 对于多个监听器,请使用 .asBroadcastStream() 方法

让我们创建一个 RandomNumberStream

import 'dart:async';
import 'dart:math' as Math;

class RandomNumber {
  final StreamController _controller = StreamController<int>();
  int _count = Math.Random().nextInt(100);
  int times = 0;

  RandomNumber() {
    Timer.periodic(Duration(seconds: 1), (timer){
      _controller.sink.add(_count);
      _count = Math.Random().nextInt(100);
      times += 1;

      if (times > 5) {
        timer.cancel();
        _controller.sink.close();
      }
    });
  }

  Stream<int> get stream => _controller.stream;
}

监听流的变化

void main() {
  final randomNumStream = RandomNumber().stream;

  final subscription = randomNumStream.listen(
    (data){
      print('Data: $data');
    },
    onError: (err) {
      print('Error: $err');
    },
    cancelOnError: false,
    onDone: (){
      print('Done');
    }
  );
}

用于广播

void main() {
  final broadCastRandomNumStream = RandomNumber().stream.asBroadcastStream();

  final sub1 = broadCastRandomNumStream.listen(printData);
  final sub2 = broadCastRandomNumStream.listen(printData);
}

void printData(data){
  print('Data: $data');
}
另一个 Stream 示例

在这种情况下,我们使用生成器函数和 yield 关键字将值放入流中

import 'dart:io';

Stream<int> countStream(int max) async * {
  for (int i = 0; i < max; ++i) {
    yield i;
    sleep(Duration(seconds: 1));
  }
}

void main() {
  print('start');
  countStream(5).listen((data){
    print(data);
  },
  onDone: (){
    print("Done");
  });
  print('end');
}

输出

start
end
0
1
2
3
4
Done

正则表达式

Dart 的正则表达式与 JavaScript 的正则表达式具有相同的语法和语义。

void main() {
  RegExp exp = RegExp(r"(\w+)");
  String str = "Focus What Matters!";
  Iterable<RegExpMatch> matches = exp.allMatches(str);
  matches.forEach((match) {
    print(str.substring(match.start, match.end));
  });
}

输出

Focus
What
Matters

文件 IO

读取文件

// File Reading

import 'dart:io';

void main() async {
  File file = File('data.txt');
  var contents = await file.readAsString();
  print(contents);
}

写入新文件

import 'dart:io';

void main() async {
  File file = File('hello.txt');
  var contents = await file.writeAsString('Life is Good!\n\nI love programming');
}

GitHub

查看 Github