Dart 笔记
Hello World
Section titled “Hello World”void main() {  print('Hello, World!');}如果需要读取命令行参数
void main(List<String> arguments) {  print('Hello, World!');}在dart中Object是除了null所有类型的父类
自动推断类型
var name = 'Dart' // namne的类型被推断成String明确类型
String name = 'Dart'也可以声明类型为父类
Object name = 'Dart'对于局部变量建议使用var
在 Dart 中,类型转换是一种将一个数据类型转换为另一个数据类型的过程。Dart 提供了多种方式来进行类型转换,以下是几种常见的方法:
- 
使用
as操作符进行显式类型转换:dynamic someValue = 'Hello Dart';String strValue = someValue as String;print(strValue); // 输出: Hello Dart - 
类型检查和转换: 使用
is关键字来检查一个对象是否是某个类型,然后使用as关键字进行转换:dynamic someValue = 123;if (someValue is int) {int intValue = someValue as int;print(intValue); // 输出: 123} - 
使用
toString方法将其他类型转换为字符串:int intValue = 42;String strValue = intValue.toString();print(strValue); // 输出: 42 - 
使用
parse方法将字符串转换为数字:String strValue = '123';int intValue = int.parse(strValue);print(intValue); // 输出: 123String doubleValueStr = '123.45';double doubleValue = double.parse(doubleValueStr);print(doubleValue); // 输出: 123.45 - 
使用
tryParse方法进行安全转换: 如果转换失败,不会抛出异常,而是返回 null:String invalidStr = 'abc';int? intValue = int.tryParse(invalidStr);print(intValue); // 输出: null - 
列表类型转换: 可以将一个动态类型的列表转换为特定类型的列表:
List<dynamic> dynamicList = [1, 2, 3];List<int> intList = dynamicList.cast<int>();print(intList); // 输出: [1, 2, 3] - 
自定义对象类型转换: 如果你有一个自定义类,你可以定义一个方法来进行类型转换:
class Person {String name;int age;Person(this.name, this.age);// 定义一个方法来转换成字符串@overrideString toString() {return 'Name: $name, Age: $age';}}void main() {Person person = Person('Alice', 30);print(person); // 输出: Name: Alice, Age: 30} 
字符串格式化
Section titled “字符串格式化”在Dart中,字符串格式化有几种常见的方法,以下是每种方法的示例:
1. 使用字符串插值 (String Interpolation)
Section titled “1. 使用字符串插值 (String Interpolation)”这是Dart中最常用和最简洁的字符串格式化方法。
void main() {  String name = "Alice";  int age = 30;  print("My name is $name and I am $age years old.");
  // 使用表达式  print("Next year, I will be ${age + 1} years old.");}2. 使用 String 类的 replaceAll 方法
Section titled “2. 使用 String 类的 replaceAll 方法”可以使用 replaceAll 方法进行简单的字符串替换。
void main() {  String template = "My name is {name} and I am {age} years old.";  String name = "Alice";  int age = 30;  String formattedString = template.replaceAll("{name}", name).replaceAll("{age}", age.toString());  print(formattedString);}3. 使用 StringBuffer
Section titled “3. 使用 StringBuffer”StringBuffer 提供了高效的字符串拼接方法,适用于需要大量拼接的情况。
void main() {  String name = "Alice";  int age = 30;  StringBuffer sb = StringBuffer();  sb.write("My name is ");  sb.write(name);  sb.write(" and I am ");  sb.write(age);  sb.write(" years old.");  String formattedString = sb.toString();  print(formattedString);}4. 使用 dart:convert 中的 jsonEncode 进行简单的模板替换
Section titled “4. 使用 dart:convert 中的 jsonEncode 进行简单的模板替换”这种方法适用于复杂的模板替换场景,可以将变量替换逻辑和模板分开。
import 'dart:convert';
void main() {  String template = 'My name is {name} and I am {age} years old.';  Map<String, dynamic> values = {    'name': 'Alice',    'age': 30,  };  String formattedString = template.replaceAllMapped(RegExp(r'{(\w+)}'), (match) {    String key = match.group(1);    return values[key].toString();  });  print(formattedString);}5. 使用 intl 包中的 NumberFormat 和 DateFormat
Section titled “5. 使用 intl 包中的 NumberFormat 和 DateFormat”intl 包提供了强大的国际化和格式化功能,适用于处理日期和数字格式。
import 'package:intl/intl.dart';
void main() {  double amount = 1234.56;  DateTime now = DateTime.now();
  // 格式化数字  String formattedAmount = NumberFormat.currency(symbol: '\$').format(amount);  print("Amount: $formattedAmount");
  // 格式化日期  String formattedDate = DateFormat('yyyy-MM-dd').format(now);  print("Date: $formattedDate");}dart的类型默认是非null的, 如果要使变量能接收null, 需要使用可null类型, 在类型声明后面加上?
String? name在使用变量前需要先初始化变量, 对于可null类型, 默认有默认值null初始化, 对于非null类型, dart不会设置默认值, 强制需要初始化, 不能访问可null类型除了hashCode和toString之外的属性和方法。
不强制要求在声明局部的位置进行初始化, 但是要在其使用前分配一个值。
int lineCount;
if (weLikeToCount) {  lineCount = countLines();} else {  lineCount = 0;}
print(lineCount);在dart中使用late声明惰性变量, 惰性变量可以延迟初始化
late String description;
void main() {  description = 'Feijoada!';  print(description);}如果在读取读去变量时还没有初始化, 程序将发生运行时错误
惰性初始化多用于
- 变量可能不需要, 并且初始化变量的成本很高
 - 正在初始化实例变量, 其初始化设定项需要访问this
 
late String temperature = readThermometer();如果未使用temperature, readThermometer将不会运行
final和const
Section titled “final和const”如果不修改声明的变量, 使用final或const代替var, const变量是编译时常量(const 是隐式的 final)
实力变量可以是final, 但不能是const
任何变量都可以具有常量的值
var foo = const [];final bar = const [];const baz = []; // Equivalent to `const []`可以修改非final和const的值, 即使之前是const值
foo = [1, 2, 3];可以使用类型检查(is 和 as), 集合(if), 拓展运算(... 和 ...?)
const Object i = 3; // 常量对象, 值是3const list = [i as int]; // 将i转换为int类型转换const map = {if (i is int) i: 'int'}; // 如果i是可以转换成int类型, 则map中包含该字段const set = {if (list is List<int>) ...list};final无法修改对象, 但是对象的属性是可以修改的, const对象及其字段都无法修改
| 描述 | 操作符 | 关联 | 
|---|---|---|
| 一元后缀 | expr++ expr— () [] ?[] . ?. ! | 无 | 
| 一元前缀 | -expr !expr ~expr ++expr —expr await expr | 无 | 
| 乘法级 | * / % ~/ | 左 | 
| 加法级 | + - | 左 | 
| 移位 | << >> >>> | 左 | 
| 位与 | & | 左 | 
| 位异或 | ^ | 左 | 
| 位或 | | | 左 | 
| 关系判断 | >= > <= as is !is | 无 | 
| 相等 | == != | 无 | 
| 逻辑与 | && | 左 | 
| 逻辑或 | || | 左 | 
| 空判断 | ?? | 左 | 
| 三元 | expr1 ? expr2 : expr3 | 右 | 
| 级联 | .. ?.. | 左 | 
| 赋值 | = *= /= += 等 | 右 | 
dart有两种除法
/和~/,/计算结果是浮点数,~/计算结果是整数
is和!is用于判断值和类型的关系
var i = 1(i is int)  // true(i !is int) // false(obj is Object?) // 不论obj是什么类型都是true??=赋值
var a = 1a ??= 2   // 当a为null时才发生赋值操作级联操作
class User {   String name;   int age;   User(this.name, this.age);   @override  String toString() {    return 'name: $name, age: $age';  }}
void main(List<String> args) {  var user = User("name", 2)  ..name = "new"  ..age = 22;  print(user); // name: new, age: 22}// 单行注释
/* 多行注释 */
 /** 文档注释  */
/// 文档注释, 连续使用和 /** */作用一样元数据提供有关代码的信息, 元数据以@开头, 后跟编译时常量的引用或常量构造函数的调用
所有dart代码都有的四个注释@Deprecated, @deprecated, @override, 和 @pragma
@Deprecated和@deprecated都表示将要废弃, @Deprecated可以自定义提示信息, @deprecated不能自定义提示信息
在库中以_开始的标识符仅在库内可见
导入库
import 'dart:html'; // dart表示内置库import 'package:test/test.dart'; // pakage 表示游包管理器提供的库如果两个库的标识符冲突, 可以使用指定库的前缀
import 'package:lib1/lib1.dart';import 'package:lib2/lib2.dart' as lib2;
Element element1 = Element();
lib2.Element element2 = lib2.Element();// 只导入fooimport 'package:lib1/lib1.dart' show foo;
// 导入除了doo的所有标识符import 'package:lib2/lib2.dart' hide foo;延迟加载库(仅适用于Web)
import 'package:greetings/hello.dart' deferred as hello;
Future<void> greet() async {  await hello.loadLibrary();  hello.printGreeting();}library指令, 指定库级的文档注释或元数据
@TestOn('browser')library;dart包含两种数字类型int和double
int是不大于64位的有符号整数类型, 在javascript中用64位浮点数的的整数部分表示(相当于53位的有符号整数) double 是64位双精度浮点数
int和double是num的子类
dart字符串是utf-16编码的, 可以使用单引号和双引号创建字符串
var s1 = "s1 string";var s2 = "s2 string";
var s3 = "${s1} --- s3"; // 模板字符串, 相当于 var s3 = "s1 string --- s3"
var s4 = """多行字符串""";
var s5 = '''这也是多行字符串''';
var s6 = r'原始字符串 \n \r';布尔类型只有两种值, true和false, 并且都是编译时常量 dart是强类型语言, 在if中不会隐式转换为bool
对应Unicode码位, 如果要读取或写入单个Unicode字符, 使用characters包
import 'package:characters/characters.dart';
void main() {  var hi = 'Hi \u2665';  print(hi);  print('字符串截取: ${hi.substring(hi.length - 1)}');  print('字符: ${hi.characters.last}');}获取符号
var s = #bar;记录(Records)
Section titled “记录(Records)”记录是只读的, 记录可以有名称, 名称不同或类型不同, 记录的类型不同
({int a, int b}) recordAB = (a: 1, b: 2);({int x, int y}) recordXY = (x: 3, y: 4); // 由于记录名称不一样, recordAB和recordXY是不同类型
(int a, int b) recordAB1 = (1, 2);(int x, int y) recordXY2 = (3, 4); // recordAB1和recordXY1是同一类型
// 命名字段可以通过字段名称读取, 未命名字段可以通过在未命名字段中的顺序读取字段(从1开始)var a = recordAB1.a;var a1 = recordAB1.$1;var list = [1,2,3]; // List<int>var setType = { 1,2,3 }; // Set<int>var emptySet = <int>{}; // 创建空Set
var gifts = {  // 键:    值  'first': 'partridge',  'second': 'turtledoves',  'fifth': 'golden rings'};// 键和值可以是任意的类型var gifts = Map<String, String>(); // 创建一个空的Mapgifts['first'] = 'partridge'; // 给Map添加值展开运算符
var list = [1, 2, 3];var list2 = [0, ...list]; // list2 = [0, 1, 2, 3];
List<int>? list = null;var list2 = [0, ...?list]; // list2 = [0];控制流在集合中的使用
// 如果 promoActive == true, nav包含 Outletvar nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];
// 如果 login == ‘Manager’, nav包含 Inventoryvar nav = ['Home', 'Furniture', 'Plants', if (login case 'Manager') 'Inventory'];
// 循环var listOfInts = [1, 2, 3];// listOfStrings = [ '#0', '#1', '#2', '#3'];var listOfStrings = ['#0', for (var i in listOfInts) '#$i'];泛型可以生成更好的代码, 减少重复代码 默认约束
// 默认泛型都继承 Object?class Foo<T extends Object> {}typedef IntList = List<int>;typedef Compare<T> = int Function(T a, T b);模式可以用来匹配值或解构值
switch (number) {  case 1:    print('one');}// 解构一个双元素的列表, 第一个值是'a'或'b'switch (list) {  case ['a' || 'b', var c]:    print(c);}交换两个变量
var (a, b) = ('left', 'right');(b, a) = (a, b);守卫
switch (obj) {  case 1:    print('one');
  // last >= obj >= first  case >= first && <= last:    print('in range');  // 如果obj是两个值的记录  case (var a, var b):    print('a = $a, b = $b');
  default:}共享变量并使用守卫
switch (shape) {  case Square(size: var s) || Circle(size: var s) when s > 0:    print('Non-empty symmetric shape');}函数是对象且具有Function类型。
// 实现一个函数int add(int i, int j) {  return i + j;}
// 函数的类型可以省略add(i, j) {  return i + j;}
// 箭头函数int add(int i, int j) => i+ j;命名参数
// bold, hidden默认是nullvoid enableFlags({bool? bold, bool? hidden}) {  print("$bold, $hidden");}
// 如果将bold, hidden设置为非空, 则需要添加required或设置默认值void enableFlags({required bool bold, bool hidden = true}) {  print("$bold, $hidden");}可选位置参数
// device是可选的String say(String from, String msg, [String? device]) {  var result = '$from says $msg';  if (device != null) {    result = '$result with a $device';  }  return result;}函数相等
void foo() {} // 顶层函数
class A {  static void bar() {} // A的静态方法  void baz() {} // 示例方法}
void main() {  Function x;
  x = foo;  assert(foo == x);
  x = A.bar;  assert(A.bar == x);
  var v = A(); // Instance #1 of A  var w = A(); // Instance #2 of A  var y = w;  x = w.baz;
  assert(y.baz == x);
  assert(v.baz != w.baz);}dart的所有函数都有一个返回值, 如果未指定返回值则默认返回null
dart有两种生成器, 同步生成器Iterable和异步生成器Stream
Iterable<int> naturalsTo(int n) sync* {  int k = 0;  while (k < n) yield k++;}Stream<int> asynchronousNaturalsTo(int n) async* {  int k = 0;  while (k < n) yield k++;}外部函数
external是函数体和声明分开的函数。在函数声明前加external, 通常用于函数实现在另外的dart库, 或者函数的实现来自另外的语言, 引入外部的函数或值。外部函数可以是顶级函数, 实例方法, getter,setter或非重定向构造函数, 实例变量使用external相当于外部的getter和setter(变量不是final)。
external void someFunc(int i);// for 循环var message = [];for (var i = 0; i < 5; i++) {  message.add('!');}
// for in 循环Iterablefor (final m in message) {  print(m);}
// while 循环 先判断 !isDone() 是不是为truewhile (!isDone()) {  doSomething();}
// do-while 循环 先执行doSomething(), 后判断 !isDone() 是不是为truedo {  doSomething();} while (!isDone());if (isRaining()) {  you.bringRainCoat();} else if (isSnowing()) {  you.wearJacket();} else {  car.putTopDown();}// if-case pair是两个元素的列表时执行if (pair case [int x, int y]) return Point(x, y);// 如case中没有代码, 则将继续向下匹配casevar command = 'OPEN';switch (command) {  case 'CLOSED':    executeClosed();  case 'PENDING':    executePending();  case 'APPROVED':    executeApproved();  case 'DENIED':    executeDenied();  case 'OPEN':    executeOpen();  default:    executeUnknown();}// switch表达式token = switch (charCode) {  slash || star || plus || minus => operator(charCode),  comma || semicolon => punctuation(charCode),  >= digit0 && <= digit9 => number(),  _ => throw FormatException('Invalid')};在dart中如果抛出的异常没有被捕获则将导致isolate挂起, 通常会导致isolate及其程序被终止。
dart提供了Exception和Error类型, 但是dart中可以使用thow抛出任意的非null对象, 不要求必须是Exception和Error类型的对象作为异常, 通常还是使用Exception和Error类型的异常。
通过thow抛出的异常可以通过catch捕获到。
// 不需要异常的信息try {  throw Exception("Exception");} on Exception {  print("e");}
// 捕获异常信息try {  throw Exception("Exception");} on Exception catch (e) {  print(e);}
// 捕获异常信息和堆栈跟踪对象try {  throw Exception("Exception");} on Exception catch (e, s) {  print(e);  print(s);}
// rethrow将再次抛出异常try {  throw Exception("Exception");} on Exception catch (e, s) {  print(e);  print(s);  rethrow;}
// finally 不论是否捕获异常都会执行try {  throw Exception("Exception");} on Exception catch (e, s) {  print(e);  print(s);  rethrow;} finally {  print("finally");}每个实例都是类的对象, 除了Null之外的所有类都源于Object, 除了顶层类Object?都只有一个超类。拓展方法可以在不更改类和创建子类的情况下向类添加功能。
常量构造函数
class ImmutablePoint {  static const ImmutablePoint origin = ImmutablePoint(0, 0);  final double x, y;
  // 常量构造函数  const ImmutablePoint(this.x, this.y);}
// a和b是相等的var a = const ImmutablePoint(1, 1);var b = const ImmutablePoint(1, 1);实例变量
class Point {  double? x; // 声明一个变量, 默认是null  double? y;  double z = 0; // 声明变量z, 默认值是0}所有实例变量都会有隐式getter, 非final变量和没有初始化的late final 还会生成隐式的setter方法
声明非late的实例变量会在实例创建时, 构造函数及其初始化列表执行之前设置值, 因此非late实例变量的初始化表达式不能访问
this
隐式接口
// 一个类会创建一个同名的隐式接口, 该接口包含了该类及其实现接口的任意实例成员(实例变量和实例函数)class Person {  final String _name;  Person(this._name);  String greet(String who) => 'Hello, $who. I am $_name.';}
class Impostor implements Person {  String get _name => '';
  String greet(String who) => 'Hi $who. Do you know who I am?';}
String greetBob(Person person) => person.greet('Bob');
void main() {  print(greetBob(Person('Kathy')));  print(greetBob(Impostor()));}类变量
// 类变量要使用时才会初始化class Person {  static String s = (() { print("init"); return "ok"; })();}
void main() {  print("start");  print(Person.s);}class Point {  final double x;  final double y;
  // 初始化形参, 可以初始化不为null的变量和final实例变量  Point(this.x, this.y);}默认构造函数
如果没有声明默认构造函数, 将生成一个无参构造函数, 并会调用父类的无参构造函数
命名构造函数
class Point {  final double x;  final double y;
  Point(this.x, this.y);
  // 名名构造函数  Point.origin()      : x = xOrigin,        y = yOrigin;}超类参数
class Vector2d {  final double x;  final double y;
  Vector2d(this.x, this.y);}
class Vector3d extends Vector2d {  final double z;
  // 和不使用super相同  // Vector3d(final double x, final double y, this.z) : super(x, y);  Vector3d(super.x, super.y, this.z);}从定向构造函数
class Point {  double x, y;
  Point(this.x, this.y);
  Point.alongXAxis(double x) : this(x, 0);}工厂构造函数
class Logger {  final String name;  bool mute = false;
  static final Map<String, Logger> _cache = <String, Logger>{};
  factory Logger(String name) {    return _cache.putIfAbsent(name, () => Logger._internal(name));  }
  factory Logger.fromJson(Map<String, Object> json) {    return Logger(json['name'].toString());  }
  Logger._internal(this.name);
  void log(String msg) {    if (!mute) print(msg);  }}
// 调用var logger = Logger('UI');logger.log('Button clicked');
var logMap = {'name': 'UI'};var loggerJson = Logger.fromJson(logMap);运算符重载
支持的运算符
| < | + | | | >>> | 
|---|---|---|---|
| > | / | ^ | [] | 
| <== | ~/ | & | []= | 
| >= | * | << | ~ | 
| - | % | >> | == | 
class Vector {  final int x, y;
  Vector(this.x, this.y);
  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
  @override  bool operator ==(Object other) =>      other is Vector && x == other.x && y == other.y;
  @override  int get hashCode => Object.hash(x, y);}
void main() {  final v = Vector(2, 3);  final w = Vector(2, 2);
  assert(v + w == Vector(4, 5));  assert(v - w == Vector(0, 1));}显式getter和setter
class Rectangle {  double left, top, width, height;
  Rectangle(this.left, this.top, this.width, this.height);
  double get right => left + width;  set right(double value) => left = value - width;  double get bottom => top + height;  set bottom(double value) => top = value - height;}
void main() {  var rect = Rectangle(3, 4, 20, 15);  assert(rect.left == 3);  rect.right = 12;  assert(rect.left == -8);}继承
class Television {  void turnOn() {  }}
class SmartTelevision extends Television {  void turnOn() {    super.turnOn();  }}noSuchMethod函数
class A {  // 重写 noSuchMethod  @override  void noSuchMethod(Invocation invocation) {    print('You tried to use a non-existent member: '        '${invocation.memberName}');  }}
void main() {  // 要调用noSuchMethod方法, a必须声明为dynamic, 并且noSuchMethod方法被重写  dynamic a = A();  a.aaa();}mixin
mixin不能有extends, 并且不能声明构造函数
mixin Musical {  bool canPlayPiano = false;  bool canCompose = false;  bool canConduct = false;
  void entertainMe() {    if (canPlayPiano) {      print('Playing piano');    } else if (canConduct) {      print('Waving hands');    } else {      print('Humming to self');    }  }}
// with使用mixinclass Musician with Musical {}
void main() {  var m = Musician();  m.entertainMe();}通过on限制使用mixin的超类
class Musician {}mixin MusicalPerformer on Musician {}class SingerDancer extends Musician with MusicalPerformer {}mixin class
// mixin class同时有class和mixin的限制// mixin不能有extends或with// class不能有onmixin class Musical {  bool canPlayPiano = false;  bool canCompose = false;  bool canConduct = false;
  void entertainMe() {    if (canPlayPiano) {      print('Playing piano');    } else if (canConduct) {      print('Waving hands');    } else {      print('Humming to self');    }  }}abstract mixin class
abstract mixin class Musician {  void playInstrument(String instrumentName);
  void playPiano() {    playInstrument('Piano');  }  void playFlute() {    playInstrument('Flute');  }}
class Virtuoso with Musician { // Musician 作为mixin  void playInstrument(String instrumentName) {    print('Plays the $instrumentName beautifully');  }}
class Novice extends Musician { // Musician 作为class  void playInstrument(String instrumentName) {    print('Plays the $instrumentName poorly');  }}枚举 枚举都会自动拓展Enum类, 他们都是密封的, 不能显示实例化
简单枚举
enum Color { red, green, blue }增强枚举
enum Vehicle implements Comparable<Vehicle> {  car(tires: 4, passengers: 5, carbonPerKilometer: 400),  bus(tires: 6, passengers: 50, carbonPerKilometer: 800),  bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0);
  const Vehicle({    required this.tires,    required this.passengers,    required this.carbonPerKilometer,  });
  final int tires;  final int passengers;  final int carbonPerKilometer;
  int get carbonFootprint => (carbonPerKilometer / passengers).round();
  bool get isTwoWheeled => this == Vehicle.bicycle;
  @override  int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint;}
void main() {  // 使用枚举  final favoriteColor = Color.blue;  if (favoriteColor == Color.blue) {    print('Your favorite color is blue!');  }  // 每个枚举值都有一个index字段表示枚举的索引, 从0开始增加  // Vehicle.values 可以获取所有的枚举变量}拓展方法
extension NumberParsing on String {  int parseInt() {    return int.parse(this);  }}
void main() {  print('42'.parseInt());
  dynamic d = '2';  print(d.parseInt()); // 由于拓展方法是静态解析的所以不能用在dynamic上面}未命名的拓展
extension on String {  bool get isBlank => trim().isEmpty;}可调用对象
class WannabeFunction {  String call(String a, String b, String c) => '$a $b $c!';}
var wf = WannabeFunction();var out = wf('Hi', 'there,', 'gang');
void main() {  print(out);}类修饰符用于控制class或mixin的使用方式
- abstract
 - base
 - final
 - interface
 - sealed
 - mixin
 
使用base可以用在mixin前面
如果允许从任何库或子类型不受限制的使用, 使用不带修饰符的class或mixin
abstract
Section titled “abstract”定义不完整的类, 抽象类不能构造, 抽象类通常有抽象方法
abstract class Vehicle {  // 抽象方法  void moveForward(int meters);}强制继承类或mixin的实现, 基类不能在自己库之外实现
base class Vehicle {  void moveForward(int meters) {  }}import 'a.dart';
Vehicle myVehicle = Vehicle();
base class Car extends Vehicle {  int passengers = 4;}
// 报错base class MockVehicle implements Vehicle {  @override  void moveForward() {  }}interface
Section titled “interface”// 可以使用implements, 不能使用extendsinterface class Vehicle {  void moveForward(int meters) {  }}abstract interface
Section titled “abstract interface”不但无法extends, 还可以包含抽象方法
// Vehicle子类必须是base, final, sealedfinal class Vehicle {  void moveForward(int meters) {  }}sealed
Section titled “sealed”sealed类是隐式抽象的, 但是sealed的子类不是隐式抽象的
dart的运行时是基于事件循环的
Futures
Section titled “Futures”Future表示异步操作的结果, 该操作最终会带有一个值或一个错误
Future<void> checkVersion() async {  var version = await lookUpVersion();}Streams
Section titled “Streams”Stream<int> stream = Stream.periodic(const Duration(seconds: 1), (i) => i * i);