不要 使用 .length 来判断一个集合是否为空。

Iterable 合约并不要求集合知道其长度,也没要求在遍历的时候其长度不能改变。通过调用 .length 来判断集合是否包含内容是非常低效的。

相反,Dart 提供了更加高效率和易用的 getter 函数:.isEmpty.isNotEmpty。使用这些函数并不需要对结果再次取非。

art 有三种核心集合类型。List、Map 和 Set,这些类和大多数类一样,都有未命名的构造函数,但由于这些集合使用频率很高,Dart 有更好的内置语法来创建它们:

var points = <Point>[];
var addresses = <String, Address>{};
var counts = <int>{};

var points = List<Point>();
var addresses = Map<String, Address>();
var counts = Set<int>();

推荐 使用插值的形式来组合字符串和值。

'Hello, $name! You are ${year - birth} years old.';
'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';

不要 使用 List.from() 除非想修改结果的类型。

给定一个可迭代的对象,有两种常见方式来生成一个包含相同元素的 list:

var copy1 = iterable.toList();
var copy2 = List.from(iterable);

明显的区别是前一个更短。更重要的区别在于第一个保留了原始对象的类型参数:

** 要** 使用 whereType() 按类型过滤集合。

var objects = [1, "a", 2, "b", 3];
var ints = objects.whereType<int>();

如果你正在调用 map() ,给它一个显式的类型参数,这样它就能产生一个所需类型的可迭代对象。类型推断通常根据传递给 map() 的函数选择出正确的类型,但有的时候需要明确指明。

var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map<double>((n) => 1 / n);

不要 显示的为参数初始化 null 值。

int _nextId;

class LazyId {
  int _id;

  int get id {
    if (_nextId == null) _nextId = 0;
    if (_id == null) _id = _nextId++;

    return _id;
  }
}

避免 保存可计算的结果。

class Circle {
  double radius;

  Circle(this.radius);

  double get area => pi * radius * radius;
  double get circumference => pi * 2.0 * radius;
}

** 推荐** 使用 final 关键字来创建只读属性。

PREFER using a final field to make a read-only property.

如果你有一个变量,对于外部代买来说只能读取不能修改,最简单的做法就是使用 final 关键字来标记这个变量。

class Box {
  final contents = [];
}

考虑 对简单成员使用 =>

除了使用 => 可以用作函数表达式以外, Dart 还允许使用它来定义成员。这种风格非常适合,仅进行计算并返回结果的简单成员。

double get area => (right - left) * (bottom - top);

bool isReady(double time) =>
    minTime == null || minTime <= time;

String capitalize(String name) =>
    '${name[0].toUpperCase()}${name.substring(1)}';

不要 使用 this. ,在重定向命名函数和避免冲突的情况下除外。

只有当局部变量和成员变量名字一样的时候,你才需要使用 this. 来访问成员变量。只有两种情况需要使用 this.

其中一种情况是要访问的局部变量和成员变量命名一样的时候:

另一种使用 this. 的情况是在重定向到一个命名函数的时候:

不要 在初始化形式中做类型注释。

如果构造函数参数使用 this. 的方式来初始化字段,这时参数的类型被认为和字段类型相同。

class Point {
  double x, y;
  Point(this.x, this.y);
}

尽可能的使用初始化形式。

class Point {
  double x, y;
  Point(this.x, this.y);
}

; 来替代空的构造函数体 {}

在 Dart 中,没有具体函数体的构造函数可以使用分号结尾。(事实上,这是不可变构造函数的要求。)

class Point {
  double x, y;
  Point(this.x, this.y);
}

不要 冗余地使用 const

在表达式一定是常量的上下文中,const 关键字是隐式的,不需要写,也不应该。这里包括:

  • 一个字面量常量集合。
  • 调用一个常量构造函数。
  • 元数据注解。
  • 一个常量声明的初始化方法。
  • switch case 表达式—— case: 中间的部分,不是 case 执行体。

** 推荐** 使用 async/await 而不是直接使用底层的特性。

显式的异步代码是非常难以阅读和调试的,即使使用很好的抽象(比如 future)也是如此。这就是为何 Dart 提供了 async/await。这样可以显著的提高代码的可读性并且让你可以在异步代码中使用语言提供的所有流程控制语句。

Future<int> countActivePlayers(String teamName) async {
  try {
    var team = await downloadTeam(teamName);
    if (team == null) return 0;

    var players = await team.roster;
    return players.where((player) => player.isActive).length;
  } catch (e) {
    log.error(e);
    return 0;
  }
}

下面这些情况 async 是有用的:

  • 你使用了 await。 (这是一个很明显的例子。)
  • 你在异步的抛出一个异常。 async 然后 throwreturn new Future.error(...) 要简短很多。
  • 你在返回一个值,但是你希望他显式的使用 Future。asyncFuture.value(...) 要简短很多。