Dart 开发语言概览



本文将从变量和运算符开始到类和库的使用来向你介绍 Dart 编程语言的主要功能,这里假设你已经有使用其它语言进行编程的经验。

This page shows you how to use each major Dart feature, from variables and operators to classes and libraries, with the assumption that you already know how to program in another language. For a briefer, less complete introduction to the language, see the language samples page.

你可以通过查看 Dart 库概览 学习更多关于 Dart 核心库的知识。若还想了解更多有关语言功能的详细内容,请参阅 Dart 编程语言规范

To learn more about Dart’s core libraries, see the library tour. Whenever you want more details about a language feature, consult the Dart language specification.

一个简单的 Dart 程序

A basic Dart program

下面的应用程序代码用到了很多 Dart 的基本功能:

The following code uses many of Dart’s most basic features:

// Define a function.
void printInteger(int aNumber) {
  print('The number is $aNumber.'); // Print to console.

// This is where the app starts executing.
void main() {
  var number = 42; // Declare and initialize a variable.
  printInteger(number); // Call a function.

下面是上述应用程序中使用到的代码片段,这些代码片段适用于所有(或几乎所有)的 Dart 应用:

Here’s what this program uses that applies to all (or almost all) Dart apps:

// This is a comment.

// 注释。

以双斜杠开头的一行语句称为单行注释。Dart 同样支持多行注释和文档注释。查阅注释获取更多相关信息。

A single-line comment. Dart also supports multi-line and document comments. For details, see Comments.


一种特殊的类型,表示一个值永远不会被使用。类似于 main()printInteger() 的函数,以 void 声明的函数返回类型,并不会返回值。

A special type that indicates a value that’s never used. Functions like printInteger() and main() that don’t explicitly return a value have the void return type.


另一种数据类型,表示一个整型数字。 Dart 中一些其他的内置类型包括 StringListbool

Another type, indicating an integer. Some additional built-in types are String, List, and bool.



A number literal. Number literals are a kind of compile-time constant.



A handy way to display output.

'...' (或 "...")


A string literal.

$variableName (或 ${expression})


String interpolation: including a variable or expression’s string equivalent inside of a string literal. For more information, see Strings.


一个特殊且 必须的 顶级函数,Dart 应用程序总是会从该函数开始执行。查阅 main() 函数 获取更多相关信息。

The special, required, top-level function where app execution starts. For more information, see The main() function.


用于定义变量,通过这种方式定义变量不需要指定变量类型。这类变量的类型 (int) 由它的初始值决定 (42)。

A way to declare a variable without specifying its type. The type of this variable (int) is determined by its initial value (42).


Important concepts

当你在学习 Dart 语言时, 应该牢记以下几点:

As you learn about the Dart language, keep these facts and concepts in mind:

  • Everything you can place in a variable is an object, and every object is an instance of a class. Even numbers, functions, and null are objects. With the exception of null (if you enable sound null safety), all objects inherit from the Object class.

    所有变量引用的都是 对象,每个对象都是一个 的实例。数字、函数以及 null 都是对象。除去 null 以外(如果你开启了 空安全), 所有的类都继承于 [Object][] 类。

  • 尽管 Dart 是强类型语言,但是在声明变量时指定类型是可选的,因为 Dart 可以进行类型推断。在上述代码中,变量 number 的类型被推断为 int 类型。

    Although Dart is strongly typed, type annotations are optional because Dart can infer types. In the code above, number is inferred to be of type int.

  • 如果你开启了 空安全,变量在未声明为可空类型时不能为 null。你可以通过在类型后加上问号 (?) 将类型声明为可空。例如,int? 类型的变量可以是整形数字或 null。如果你 明确知道 一个表达式不会为空,但 Dart 不这么认为时,你可以在表达式后添加 ! 来断言表达式不为空(为空时将抛出异常)。例如:int x = nullableButNotNullInt!

    If you enable null safety, variables can’t contain null unless you say they can. You can make a variable nullable by putting a question mark (?) at the end of its type. For example, a variable of type int? might be an integer, or it might be null. If you know that an expression never evaluates to null but Dart disagrees, you can add ! to assert that it isn’t null (and to throw an exception if it is). An example: int x = nullableButNotNullInt!

  • 如果你想要显式地声明允许任意类型,使用 Object?(如果你 开启了空安全)、 Object 或者 特殊类型 dynamic 将检查延迟到运行时进行。

    When you want to explicitly say that any type is allowed, use the type Object? (if you’ve enabled null safety), Object, or — if you must defer type checking until runtime — the special type dynamic.

  • Dart 支持泛型,比如 List<int>(表示一组由 int 对象组成的列表)或 List<Object>(表示一组由任何类型对象组成的列表)。

    Dart supports generic types, like List<int> (a list of integers) or List<Object> (a list of objects of any type).

  • Dart 支持顶级函数(例如 main 方法),同时还支持定义属于类或对象的函数(即 静态实例方法)。你还可以在函数中定义函数(嵌套局部函数)。

    Dart supports top-level functions (such as main()), as well as functions tied to a class or object (static and instance methods, respectively). You can also create functions within functions (nested or local functions).

  • Dart 支持顶级 变量,以及定义属于类或对象的变量(静态和实例变量)。实例变量有时称之为域或属性。

    Similarly, Dart supports top-level variables, as well as variables tied to a class or object (static and instance variables). Instance variables are sometimes known as fields or properties.

  • Dart 没有类似于 Java 那样的 publicprotectedprivate 成员访问限定符。如果一个标识符以下划线 (_) 开头则表示该标识符在库内是私有的。可以查阅 库和可见性 获取更多相关信息。

    Unlike Java, Dart doesn’t have the keywords public, protected, and private. If an identifier starts with an underscore (_), it’s private to its library. For details, see Libraries and visibility.

  • 标识符 可以以字母或者下划线 (_) 开头,其后可跟字符和数字的组合。

    Identifiers can start with a letter or underscore (_), followed by any combination of those characters plus digits.

  • Dart 中 表达式语句 是有区别的,表达式有值而语句没有。比如条件表达式 expression condition ? expr1 : expr2 中含有值 expr1expr2。与 if-else 分支语句相比,if-else 分支语句则没有值。一个语句通常包含一个或多个表达式,但是一个表达式不能只包含一个语句。

    Dart has both expressions (which have runtime values) and statements (which don’t). For example, the conditional expression condition ? expr1 : expr2 has a value of expr1 or expr2. Compare that to an if-else statement, which has no value. A statement often contains one or more expressions, but an expression can’t directly contain a statement.

  • Dart 工具可以显示 警告错误 两种类型的问题。警告表明代码可能有问题但不会阻止其运行。错误分为编译时错误和运行时错误;编译时错误代码无法运行;运行时错误会在代码运行时导致 异常

    Dart tools can report two kinds of problems: warnings and errors. Warnings are just indications that your code might not work, but they don’t prevent your program from executing. Errors can be either compile-time or run-time. A compile-time error prevents the code from executing at all; a run-time error results in an exception being raised while the code executes.



下面的表格中列出了 Dart 语言所使用的关键字。

The following table lists the words that the Dart language treats specially.


Avoid using these words as identifiers. However, if necessary, the keywords marked with superscripts can be identifiers:

  • 带有上标 1 的关键字为 上下文关键字,只有在特定的场景才有意义,它们可以在任何地方作为有效的标识符。

    Words with the superscript 1 are contextual keywords, which have meaning only in specific places. They’re valid identifiers everywhere.

  • 带有上标 2 的关键字为 内置标识符,其作用只是在JavaScript代码转为Dart代码时更简单,这些关键字在大多数时候都可以作为有效的标识符,但是它们不能用作类名或者类型名或者作为导入前缀使用。

    Words with the superscript 2 are built-in identifiers. To simplify the task of porting JavaScript code to Dart, these keywords are valid identifiers in most places, but they can’t be used as class or type names, or as import prefixes.

  • 带有上标 3 的关键字为 Dart 1.0 发布后用于 支持异步 相关内容。不能在由关键字 asyncasync*sync* 标识的方法体中使用 awaityield 作为标识符。

    Words with the superscript 3 are limited reserved words related to asynchrony support. You can’t use await or yield as an identifier in any function body marked with async, async*, or sync*.

其它没有上标的关键字为 保留字,均不能用作标识符。

All other words in the table are reserved words, which can’t be identifiers.




Here’s an example of creating a variable and initializing it:

var name = 'Bob';

变量仅存储对象的引用。这里名为 name 的变量存储了一个 String 类型对象的引用,“Bob” 则是该对象的值。

Variables store references. The variable called name contains a reference to a String object with a value of “Bob”.

name 变量的类型被推断为 String,但是你可以为其指定类型。如果一个对象的引用不局限于单一的类型,可以将其指定为 Object(或 dynamic)类型。

The type of the name variable is inferred to be String, but you can change that type by specifying it. If an object isn’t restricted to a single type, specify the Object type (or dynamic if necessary).

Object name = 'Bob';


Another option is to explicitly declare the type that would be inferred:

String name = 'Bob';


Default value

在 Dart 中,未初始化的变量拥有一个默认的初始化值:null。(如果你未迁移至 空安全,所有变量都为可空类型。)即便数字也是如此,因为在 Dart 中一切皆为对象,数字也不例外。

Uninitialized variables that have a nullable type have an initial value of null. (If you haven’t opted into null safety, then every variable has a nullable type.) Even variables with numeric types are initially null, because numbers—like everything else in Dart—are objects.

int? lineCount;
assert(lineCount == null);

If you enable null safety, then you must initialize the values of non-nullable variables before you use them:

int lineCount = 0;

You don’t have to initialize a local variable where it’s declared, but you do need to assign it a value before it’s used. For example, the following code is valid because Dart can detect that lineCount is non-null by the time it’s passed to print():

int lineCount;

if (weLikeToCount) {
  lineCount = countLines();
} else {
  lineCount = 0;


Late variables

Dart 2.12 added the late modifier, which has two use cases:

  • Declaring a non-nullable variable that’s initialized after its declaration.
  • Lazily initializing a variable.

Often Dart’s control flow analysis can detect when a non-nullable variable is set to a non-null value before it’s used, but sometimes analysis fails. Two common cases are top-level variables and instance variables: Dart often can’t determine whether they’re set, so it doesn’t try.

If you’re sure that a variable is set before it’s used, but Dart disagrees, you can fix the error by marking the variable as late:

late String description;

void main() {
  description = 'Feijoada!';

When you mark a variable as late but initialize it at its declaration, then the initializer runs the first time the variable is used. This lazy initialization is handy in a couple of cases:

  • The variable might not be needed, and initializing it is costly.
  • You’re initializing an instance variable, and its initializer needs access to this.

In the following example, if the temperature variable is never used, then the expensive _readThermometer() function is never called:

// This is the program's only call to _readThermometer().
late String temperature = _readThermometer(); // Lazily initialized.

Final 和 Const

Final and const

如果你不想更改一个变量,可以使用关键字 final 或者 const 修饰变量,这两个关键字可以替代 var 关键字或者加在一个具体的类型前。一个 final 变量只可以被赋值一次;一个 const 变量是一个编译时常量(const 变量同时也是 final 的)。顶层的 final 变量或者类的 final 变量在其第一次使用的时候被初始化。

If you never intend to change a variable, use final or const, either instead of var or in addition to a type. A final variable can be set only once; a const variable is a compile-time constant. (Const variables are implicitly final.) A final top-level or class variable is initialized the first time it’s used.

下面的示例中我们创建并设置两个 final 变量:

Here’s an example of creating and setting a final variable:

final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

你不能修改一个 final 变量的值:

You can’t change the value of a final variable:

name = 'Alice'; // Error: a final variable can only be set once.

使用关键字 const 修饰变量表示该变量为 编译时常量。如果使用 const 修饰类中的变量,则必须加上 static 关键字,即 static const(译者注:顺序不能颠倒)。在声明 const 变量时可以直接为其赋值,也可以使用其它的 const 变量为其赋值:

Use const for variables that you want to be compile-time constants. If the const variable is at the class level, mark it static const. Where you declare the variable, set the value to a compile-time constant such as a number or string literal, a const variable, or the result of an arithmetic operation on constant numbers:

const bar = 1000000; // 直接赋值 [Unit of pressure (dynes/cm2)]
const double atm = 1.01325 * bar; // 利用其它 const 变量赋值 (Standard atmosphere)

const 关键字不仅仅可以用来定义常量,还可以用来创建 常量值,该常量值可以赋予给任何变量。你也可以将构造函数声明为 const 的,这种类型的构造函数创建的对象是不可改变的。

The const keyword isn’t just for declaring constant variables. You can also use it to create constant values, as well as to declare constructors that create constant values. Any variable can have a constant value.

var foo = const [];
final bar = const [];
const baz = []; // 相当于 `const []` (Equivalent to `const []`)

如果使用初始化表达式为常量赋值可以省略掉关键字 const,比如上面的常量 baz 的赋值就省略掉了 const。详情请查阅 不要冗余地使用 const

You can omit const from the initializing expression of a const declaration, like for baz above. For details, see DON’T use const redundantly.

没有使用 final 或 const 修饰的变量的值是可以被更改的,即使这些变量之前引用过 const 的值。

You can change the value of a non-final, non-const variable, even if it used to have a const value:

foo = [1, 2, 3]; // foo 的值之前为 const [] (Was const [])


You can’t change the value of a const variable:

baz = [42]; // 报错:常量不可以被赋值。(Error: Constant variables can't be assigned a value.)

你可以在常量中使用 类型检查和强制类型转换 (isas)、 集合中的 if 以及 展开操作符 (......?):

You can define constants that use type checks and casts (is and as), collection if, and spread operators (... and ...?):

const Object i = 3; // Where i is a const Object with an int value...
const list = [i as int]; // Use a typecast.
const map = {if (i is int) i: 'int'}; // Use is and collection if.
const set = {if (list is List<int>) ...list}; // ...and a spread.

可以查阅 ListsMapsClasses 获取更多关于使用 const 创建常量值的信息。

For more information on using const to create constant values, see Lists, Maps, and Classes.


Built-in types

Dart 语言支持下列内容:

The Dart language has special support for the following:

  • Numbers (int, double)
  • Strings (String)
  • Booleans (bool)
  • Lists (也被称为 arrays)

    Lists (List, also known as arrays)

  • Sets (Set)
  • Maps (Map)
  • Runes (常用于在 Characters API 中进行字符替换)

    Runes (Runes; often replaced by the characters API)

  • Symbols (Symbol)
  • The value null (Null)

使用字面量来创建对象也受到支持。例如 'This is a string' 是一个字符串字面量,true 是一个布尔字面量。

This support includes the ability to create objects using literals. For example, 'this is a string' is a string literal, and true is a boolean literal.

由于 Dart 中每个变量引用都指向一个对象(一个 的实例),通常也可以使用 构造器 来初始化变量。一些内置的类型有它们自己的构造器。例如你可以使用 Map() 来创建一个 map 对象。

Because every variable in Dart refers to an object—an instance of a class—you can usually use constructors to initialize variables. Some of the built-in types have their own constructors. For example, you can use the Map() constructor to create a map.

Some other types also have special roles in the Dart language:

  • Object: The superclass of all Dart classes except Null.
  • Future and Stream: Used in asynchrony support.
  • Iterable: Used in for-in loops and in synchronous generator functions.
  • Never: Indicates that an expression can never successfully finish evaluating. Most often used for functions that always throw an exception.
  • dynamic: Indicates that you want to disable static checking. Usually you should use Object or Object? instead.
  • void: Indicates that a value is never used. Often used as a return type.

The Object, Object?, Null, and Never classes have special roles in the class hierarchy, as described in the top-and-bottom section of Understanding null safety.


Dart 支持两种 Number 类型:

Dart numbers come in two flavors:


整数值;长度不超过 64 位,具体取值范围 依赖于不同的平台。在 DartVM 上其取值位于 -263 至 263 - 1 之间。在 Web 上,整型数值代表着 JavaScript 的数字(64 位无小数浮点型),其允许的取值范围在 -253 至 253 - 1 之间。

Integer values no larger than 64 bits, depending on the platform. On native platforms, values can be from -263 to 263 - 1. On the web, integer values are represented as JavaScript numbers (64-bit floating-point values with no fractional part) and can be from -253 to 253 - 1.


64 位的双精度浮点数字,且符合 IEEE 754 标准。

64-bit (double-precision) floating-point numbers, as specified by the IEEE 754 standard.

intdouble 都是 [num][num] 的子类。 num 中定义了一些基本的运算符比如 +、-、*、/ 等,还定义了 abs()ceil()floor() 等方法(位运算符,比如 >> 定义在 int 中)。如果 num 及其子类不满足你的要求,可以查看 dart:math 库中的 API。

Both int and double are subtypes of num. The num type includes basic operators such as +, -, /, and *, and is also where you’ll find abs(), ceil(), and floor(), among other methods. (Bitwise operators, such as >>, are defined in the int class.) If num and its subtypes don’t have what you’re looking for, the dart:math library might.


Integers are numbers without a decimal point. Here are some examples of defining integer literals:

var x = 1;
var hex = 0xDEADBEEF;
var exponent = 8e5;


If a number includes a decimal, it is a double. Here are some examples of defining double literals:

var y = 1.1;
var exponents = 1.42e5;

You can also declare a variable as a num. If you do this, the variable can have both integer and double values.

num x = 1; // x can have both int and double values
x += 2.5;


Integer literals are automatically converted to doubles when necessary:

double z = 1; // Equivalent to double z = 1.0.


Here’s how you turn a string into a number, or vice versa:

// 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');


The int type specifies the traditional bitwise shift (<<, >>), AND (&), and OR (|) operators. For example:

assert((3 << 1) == 6); // 0011 << 1 == 0110
assert((3 >> 1) == 1); // 0011 >> 1 == 0001
assert((3 | 4) == 7); // 0011 | 0100 == 0111


Literal numbers are compile-time constants. Many arithmetic expressions are also compile-time constants, as long as their operands are compile-time constants that evaluate to numbers.

const msPerSecond = 1000;
const secondsUntilRetry = 5;
const msUntilRetry = secondsUntilRetry * msPerSecond;

更多内容,请查看 Dart 中的数字

For more information, see Numbers in Dart.


Dart 字符串(String 对象)包含了 UTF-16 编码的字符序列。可以使用单引号或者双引号来创建字符串:

A Dart string (String object) holds a sequence of UTF-16 code units. You can use either single or double quotes to create a string:

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.";

// 代码中文解释
var s1 = '使用单引号创建字符串字面量。';
var s2 = "双引号也可以用于创建字符串字面量。";
var s3 = '使用单引号创建字符串时可以使用斜杠来转义那些与单引号冲突的字符串:\'。';
var s4 = "而在双引号中则不需要使用转义与单引号冲突的字符串:'";

在字符串中,请以 ${表达式} 的形式使用表达式,如果表达式是一个标识符,可以省略掉 {}。如果表达式的结果为一个对象,则 Dart 会调用该对象的 toString 方法来获取一个字符串。

You can put the value of an expression inside a string by using ${expression}. If the expression is an identifier, you can skip the {}. To get the string corresponding to an object, Dart calls the object’s toString() method.

var s = 'string interpolation';

assert('Dart has $s, which is very handy.' ==
    'Dart has string interpolation, ' +
        'which is very handy.');
assert('That deserves all caps. ' +
        '${s.toUpperCase()} is very handy!' ==
    'That deserves all caps. ' +
        'STRING INTERPOLATION is very handy!');

// 代码中文解释
var s = '字符串插值';

assert('Dart 有$s,使用起来非常方便。' == 'Dart 有字符串插值,使用起来非常方便。');
assert('使用${s.substring(3,5)}表达式也非常方便' == '使用插值表达式也非常方便。');

你可以使用 + 运算符或并列放置多个字符串来连接字符串:

You can concatenate strings using adjacent string literals or the + operator:

var s1 = 'String '
    " works even over line breaks.";
assert(s1 ==
    'String concatenation works even over '
        'line breaks.');

var s2 = 'The + operator ' + 'works, as well.';
assert(s2 == 'The + operator works, as well.');

// 代码中文解释
var s1 = '可以拼接'
assert(s1 == '可以拼接字符串即便它们不在同一行。');

var s2 = '使用加号 + 运算符' + '也可以达到相同的效果。';
assert(s2 == '使用加号 + 运算符也可以达到相同的效果。');


Another way to create a multi-line string: use a triple quote with either single or double quotation marks:

var s1 = '''

var s2 = """这也是一个多行字符串。""";

在字符串前加上 r 作为前缀创建 “raw” 字符串(即不会被做任何处理(比如转义)的字符串):

You can create a “raw” string by prefixing it with r:

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

// 代码中文解释
var s = r'在 raw 字符串中,转义字符串 \n 会直接输出 “\n” 而不是转义为换行。';

你可以查阅 Runes 与 grapheme clusters 获取更多关于如何在字符串中表示 Unicode 字符的信息。

See Runes and grapheme clusters for details on how to express Unicode characters in a string.


Literal strings are compile-time constants, as long as any interpolated expression is a compile-time constant that evaluates to null or a numeric, string, or boolean value.

// 可以将下面三个常量作为字符串插值拼接到字符串字面量中。(These work in a const string.)
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// 而下面三个常量不能作为字符串插值拼接到字符串字面量。
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';

可以查阅 字符串和正则表达式 获取更多关于如何使用字符串的信息。

For more information on using strings, see Strings and regular expressions.


Dart 使用 bool 关键字表示布尔类型,布尔类型只有两个对象 truefalse,两者都是编译时常量。

To represent boolean values, Dart has a type named bool. Only two objects have type bool: the boolean literals true and false, which are both compile-time constants.

Dart 的类型安全不允许你使用类似 if (nonbooleanValue) 或者 assert (nonbooleanValue) 这样的代码检查布尔值。相反,你应该总是显示地检查布尔值,比如像下面的代码这样:

Dart’s type safety means that you can’t use code like if (nonbooleanValue) or assert (nonbooleanValue). Instead, explicitly check for values, like this:

// 检查是否为空字符串 (Check for an empty string).
var fullName = '';

// 检查是否小于等于零。
var hitPoints = 0;
assert(hitPoints <= 0);

// 检查是否为 null。
var unicorn;
assert(unicorn == null);

// 检查是否为 NaN。
var iMeantToDoThis = 0 / 0;


数组 (Array) 是几乎所有编程语言中最常见的集合类型,在 Dart 中数组由 List 对象表示。通常称之为 List

Perhaps the most common collection in nearly every programming language is the array, or ordered group of objects. In Dart, arrays are List objects, so most people just call them lists.

Dart 中 List 字面量看起来与 JavaScript 中数组字面量一样。下面是一个 Dart List 的示例:

Dart list literals look like JavaScript array literals. Here’s a simple Dart list:

var list = [1, 2, 3];

你可以在 Dart 的集合类型的最后一个项目后添加逗号。这个尾随逗号并不会影响集合,但它能有效避免「复制粘贴」的错误。

You can add a comma after the last item in a Dart collection literal. This trailing comma doesn’t affect the collection, but it can help prevent copy-paste errors.

var list = [

List 的下标索引从 0 开始,第一个元素的下标为 0,最后一个元素的下标为 list.length - 1。你可以像 JavaScript 中的用法那样获取 Dart 中 List 的长度以及元素:

Lists use zero-based indexing, where 0 is the index of the first value and list.length - 1 is the index of the last value. You can get a list’s length and refer to list values just as you would in JavaScript:

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

list[1] = 1;
assert(list[1] == 1);

在 List 字面量前添加 const 关键字会创建一个编译时常量:

To create a list that’s a compile-time constant, add const before the list literal:

var constantList = const [1, 2, 3];
// constantList[1] = 1; // This line will cause an error.

Dart 在 2.3 引入了 扩展操作符...)和 空感知扩展操作符...?),它们提供了一种将多个元素插入集合的简洁方法。

Dart 2.3 introduced the spread operator (...) and the null-aware spread operator (...?), which provide a concise way to insert multiple values into a collection.

例如,你可以使用扩展操作符(...)将一个 List 中的所有元素插入到另一个 List 中:

For example, you can use the spread operator (...) to insert all the values of a list into another list:

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

如果扩展操作符右边可能为 null ,你可以使用 null-aware 扩展操作符(...?)来避免产生异常:

If the expression to the right of the spread operator might be null, you can avoid exceptions by using a null-aware spread operator (...?):

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


For more details and examples of using the spread operator, see the spread operator proposal.

Dart 还同时引入了 集合中的 if集合中的 for 操作,在构建集合时,可以使用条件判断 (if) 和循环 (for)。

Dart also offers collection if and collection for, which you can use to build collections using conditionals (if) and repetition (for).

下面示例是使用 集合中的 if 来创建一个 List 的示例,它可能包含 3 个或 4 个元素:

Here’s an example of using collection if to create a list with three or four items in it:

var nav = [
  if (promoActive) 'Outlet'

下面是使用 集合中的 for 将列表中的元素修改后添加到另一个列表中的示例:

Here’s an example of using collection for to manipulate the items of a list before adding them to another list:

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

你可以查阅 集合中使用控制流建议 获取更多关于在集合中使用 iffor 的细节内容和示例。

For more details and examples of using collection if and for, see the control flow collections proposal.

List 类中有许多用于操作 List 的便捷方法,你可以查阅 泛型集合 获取更多与之相关的信息。

The List type has many handy methods for manipulating lists. For more information about lists, see Generics and Collections.


在 Dart 中,set 是一组特定元素的无序集合。 Dart 支持的集合由集合的字面量和 Set 类提供。

A set in Dart is an unordered collection of unique items. Dart support for sets is provided by set literals and the Set type.

下面是使用 Set 字面量来创建一个 Set 集合的方法:

Here is a simple Dart set, created using a set literal:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

可以使用在 {} 前加上类型参数的方式创建一个空的 Set,或者将 {} 赋值给一个 Set 类型的变量:

To create an empty set, use {} preceded by a type argument, or assign {} to a variable of type Set:

var names = <String>{}; // 类型+{}的形式创建Set。
// Set<String> names = {}; // 声明类型变量的形式创建 Set (This works, too).
// var names = {}; // 这样的形式将创建一个 Map 而不是 Set (Creates a map, not a set.)

使用 add() 方法或 addAll() 方法向已存在的 Set 中添加项目:

Add items to an existing set using the add() or addAll() methods:

var elements = <String>{};

使用 .length 可以获取 Set 中元素的数量:

Use .length to get the number of items in the set:

var elements = <String>{};
assert(elements.length == 5);

可以在 Set 字面量前添加 const 关键字创建一个 Set 编译时常量:

To create a set that’s a compile-time constant, add const before the set literal:

final constantSet = const {
// constantSet.add('helium'); // This line will cause an error.

从 Dart 2.3 开始,Set 可以像 List 一样支持使用扩展操作符(......?)以及 Collection iffor 操作。你可以查阅 List 扩展操作符List 集合操作符 获取更多相关信息。

Sets support spread operators (... and ...?) and collection if and for, just like lists do. For more information, see the list spread operator and list collection operator discussions.

你也可以查阅 泛型 以及 Set 获取更多相关信息。

For more information about sets, see Generics and Sets.


通常来说,Map 是用来关联 keys 和 values 的对象。其中键和值都可以是任何类型的对象。每个 只能出现一次但是 可以重复出现多次。 Dart 中 Map 提供了 Map 字面量以及 Map 类型两种形式的 Map。

In general, a map is an object that associates keys and values. Both keys and values can be any type of object. Each key occurs only once, but you can use the same value multiple times. Dart support for maps is provided by map literals and the Map type.

下面是一对使用 Map 字面量创建 Map 的例子:

Here are a couple of simple Dart maps, created using map literals:

var gifts = {
  // 键:    值
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'

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

你也可以使用 Map 的构造器创建 Map:

You can create the same objects using a Map constructor:

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

var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

向现有的 Map 中添加键值对与 JavaScript 的操作类似:

Add a new key-value pair to an existing map just as you would in JavaScript:

var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds'; // 添加键值对 (Add a key-value pair)

从一个 Map 中获取一个值的操作也与 JavaScript 类似。

Retrieve a value from a map the same way you would in JavaScript:

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

如果检索的 Key 不存在于 Map 中则会返回一个 null:

If you look for a key that isn’t in a map, you get a null in return:

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

使用 .length 可以获取 Map 中键值对的数量:

Use .length to get the number of key-value pairs in the map:

var gifts = {'first': 'partridge'};
gifts['fourth'] = 'calling birds';
assert(gifts.length == 2);

在一个 Map 字面量前添加 const 关键字可以创建一个 Map 编译时常量:

To create a map that’s a compile-time constant, add const before the map literal:

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

// constantMap[2] = 'Helium'; // This line will cause an error.

Map 可以像 List 一样支持使用扩展操作符(......?)以及集合的 if 和 for 操作。你可以查阅 List 扩展操作符List 集合操作符 获取更多相关信息。

Maps support spread operators (... and ...?) and collection if and for, just like lists do. For details and examples, see the spread operator proposal and the control flow collections proposal.

你也可以查阅 泛型 以及 Maps 获取更多相关信息。

For more information about maps, see the generics section and the library tour’s coverage of the Maps API.

Runes 与 grapheme clusters

Runes and grapheme clusters

在 Dart 中,runes 公开了字符串的 Unicode 码位。使用 characters 包 来访问或者操作用户感知的字符,也被称为 Unicode (扩展) grapheme clusters

In Dart, runes expose the Unicode code points of a string. You can use the characters package to view or manipulate user-perceived characters, also known as Unicode (extended) grapheme clusters.

Unicode 编码为每一个字母、数字和符号都定义了一个唯一的数值。因为 Dart 中的字符串是一个 UTF-16 的字符序列,所以如果想要表示 32 位的 Unicode 数值则需要一种特殊的语法。

Unicode defines a unique numeric value for each letter, digit, and symbol used in all of the world’s writing systems. Because a Dart string is a sequence of UTF-16 code units, expressing Unicode code points within a string requires special syntax.

表示 Unicode 字符的常见方式是使用 \uXXXX,其中 XXXX 是一个四位数的 16 进制数字。例如心形字符(♥)的 Unicode 为 \u2665。对于不是四位数的 16 进制数字,需要使用大括号将其括起来。例如大笑的 emoji 表情(😆)的 Unicode 为 \u{1f600}

The usual way to express a Unicode code point is \uXXXX, where XXXX is a 4-digit hexadecimal value. For example, the heart character (♥) is \u2665. To specify more or less than 4 hex digits, place the value in curly brackets. For example, the laughing emoji (😆) is \u{1f606}.

如果你需要读写单个 Unicode 字符,可以使用 characters 包中定义的 characters getter。它将返回 Characters 对象作为一系列 grapheme clusters 的字符串。下面是使用 characters API 的样例:

If you need to read or write individual Unicode characters, use the characters getter defined on String by the characters package. The returned Characters object is the string as a sequence of grapheme clusters. Here’s an example of using the characters API:

import 'package:characters/characters.dart';
var hi = 'Hi 🇩🇰';
print('The end of the string: ${hi.substring(hi.length - 1)}');
print('The last character: ${hi.characters.last}\n');


The output, depending on your environment, looks something like this:

$ dart bin/main.dart
Hi 🇩🇰
The end of the string: ???
The last character: 🇩🇰

有关使用 characters 包操作字符串的详细信息,请参阅用于 characters 包的样例API 参考

For details on using the characters package to manipulate strings, see the example and API reference for the characters package.


Symbol 表示 Dart 中声明的操作符或者标识符。你几乎不会需要 Symbol,但是它们对于那些通过名称引用标识符的 API 很有用,因为代码压缩后,尽管标识符的名称会改变,但是它们的 Symbol 会保持不变。

A Symbol object represents an operator or identifier declared in a Dart program. You might never need to use symbols, but they’re invaluable for APIs that refer to identifiers by name, because minification changes identifier names but not identifier symbols.

可以使用在标识符前加 # 前缀来获取 Symbol:

To get the symbol for an identifier, use a symbol literal, which is just # followed by the identifier:


Symbol 字面量是编译时常量。

Symbol literals are compile-time constants.



Dart 是一种真正面向对象的语言,所以即便函数也是对象并且类型为 Function,这意味着函数可以被赋值给变量或者作为其它函数的参数。你也可以像调用函数一样调用 Dart 类的实例。详情请查阅 可调用的类

Dart is a true object-oriented language, so even functions are objects and have a type, Function. This means that functions can be assigned to variables or passed as arguments to other functions. You can also call an instance of a Dart class as if it were a function. For details, see Callable classes.


Here’s an example of implementing a function:

bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;

虽然高效 Dart 指南建议在公开的 API 上定义返回类型,不过即便不定义,该函数也依然有效:

Although Effective Dart recommends type annotations for public APIs, the function still works if you omit the types:

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;


For functions that contain just one expression, you can use a shorthand syntax:

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

语法 => 表达式{ return 表达式; } 的简写, => 有时也称之为 箭头 函数。

The => expr syntax is a shorthand for { return expr; }. The => notation is sometimes referred to as arrow syntax.



函数可以有两种形式的参数:必要参数可选参数。必要参数定义在参数列表前面,可选参数则定义在必要参数后面。可选参数可以是 命名的位置的

A function can have any number of required positional parameters. These can be followed either by named parameters or by optional positional parameters (but not both).

向函数传入参数或者定义函数参数时,可以使用 [尾随逗号][trailing comma]。

You can use trailing commas when you pass arguments to a function or when you define function parameters.


Named parameters

命名参数默认为可选参数,除非他们被特别标记为 required

Named parameters are optional unless they’re specifically marked as required.

当你调用函数时,可以使用 参数名: 参数值 的形式来指定命名参数。例如:

When calling a function, you can specify named parameters using paramName: value. For example:

enableFlags(bold: true, hidden: false);

When defining a function, use {参数1, 参数2, …} to specify named parameters:

定义函数时,使用 {param1, param2, …} 来指定命名参数:

/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool? bold, bool? hidden}) {...}

虽然命名参数是可选参数的一种类型,但是你仍然可以使用 required 来标识一个命名参数是必须的参数,此时调用者必须为该参数提供一个值。例如:

Although named parameters are a kind of optional parameter, you can annotate them with required to indicate that the parameter is mandatory — that users must provide a value for the parameter. For example:

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

如果调用者想要通过 Scrollbar 的构造函数构造一个 Scrollbar 对象而不提供 child 参数,则会导致编译错误。

If someone tries to create a Scrollbar without specifying the child argument, then the analyzer reports an issue.


Optional positional parameters

使用 [] 将一系列参数包裹起来作为位置参数:

Wrapping a set of function parameters in [] marks them as optional positional parameters:

String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  return result;


Here’s an example of calling this function without the optional parameter:

assert(say('Bob', 'Howdy') == 'Bob says Howdy');


And here’s an example of calling this function with the third parameter:

assert(say('Bob', 'Howdy', 'smoke signal') ==
    'Bob says Howdy with a smoke signal');


Default parameter values

可以用 = 为函数的命名参数和位置参数定义默认值,默认值必须为编译时常量,没有指定默认值的情况下默认值为 null

Your function can use = to define default values for both named and positional parameters. The default values must be compile-time constants. If no default value is provided, the default value is null.


Here’s an example of setting default values for named parameters:

/// 设置 [bold] 和 [hidden] 标识……
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false, bool hidden = false}) {...}

// bold 的值将为 true;而 hidden 将为 false。
enableFlags(bold: true);


The next example shows how to set default values for positional parameters:

String say(String from, String msg,
    [String device = 'carrier pigeon']) {
  var result = '$from says $msg with a $device';
  return result;

assert(say('Bob', 'Howdy') ==
    'Bob says Howdy with a carrier pigeon');

List 或 Map 同样也可以作为默认值。下面的示例定义了一个名为 doStuff() 的函数,并为其名为 listgifts 的参数指定了一个 List 类型的值和 Map 类型的值。

You can also pass lists or maps as default values. The following example defines a function, doStuff(), that specifies a default list for the list parameter and a default map for the gifts parameter.

void doStuff(
    {List<int> list = const [1, 2, 3],
    Map<String, String> gifts = const {
      'first': 'paper',
      'second': 'cotton',
      'third': 'leather'
    }}) {
  print('list:  $list');
  print('gifts: $gifts');

main() 函数

The main() function

每个 Dart 程序都必须有一个 main() 顶级函数作为程序的入口, main() 函数返回值为 void 并且有一个 List<String> 类型的可选参数。

Every app must have a top-level main() function, which serves as the entrypoint to the app. The main() function returns void and has an optional List<String> parameter for arguments.

下面是一个简单 main() 函数:

Here’s a simple main() function:

void main() {
  print('Hello, World!');

下面是使用命令行访问带参数的 main() 函数示例:

Here’s an example of the main() function for a command-line app that takes arguments:

// 使用命令 dart args.dart 1 test 运行该应用
// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {

  assert(arguments.length == 2);
  assert(int.parse(arguments[0]) == 1);
  assert(arguments[1] == 'test');

你可以通过使用 参数库 来定义和解析命令行参数。

You can use the args library to define and parse command-line arguments.


Functions as first-class objects


You can pass a function as a parameter to another function. For example:

void printElement(int element) {

var list = [1, 2, 3];

// 将 printElement 函数作为参数传递。


You can also assign a function to a variable, such as:

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');


This example uses an anonymous function. More about those in the next section.


Anonymous functions

大多数方法都是有名字的,比如 main()printElement()。你可以创建一个没有名字的方法,称之为 匿名函数Lambda 表达式Closure 闭包。你可以将匿名方法赋值给一个变量然后使用它,比如将该变量添加到集合或从中删除。

Most functions are named, such as main() or printElement(). You can also create a nameless function called an anonymous function, or sometimes a lambda or closure. You might assign an anonymous function to a variable so that, for example, you can add or remove it from a collection.


An anonymous function looks similar to a named function— zero or more parameters, separated by commas and optional type annotations, between parentheses.


([[类型] 参数[, …]]) {

The code block that follows contains the function’s body:

下面代码定义了只有一个参数 item 且没有参数类型的匿名方法。 List 中的每个元素都会调用这个函数,打印元素位置和值的字符串:

The following example defines an anonymous function with an untyped parameter, item. The function, invoked for each item in the list, prints a string that includes the value at the specified index.

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

点击 Run 按钮执行代码。

Click Run to execute the code.

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

如果函数体内只有一行返回语句,你可以使用胖箭头缩写法。粘贴下面代码到 DartPad 中并点击运行按钮,验证两个函数是否一致。

If the function contains only a single expression or return statement, you can shorten it using arrow notation. Paste the following line into DartPad and click Run to verify that it is functionally equivalent.

    (item) => print('${list.indexOf(item)}: $item'));


Lexical scope

Dart 是词法有作用域语言,变量的作用域在写代码的时候就确定了,大括号内定义的变量只能在大括号内访问,与 Java 类似。

Dart is a lexically scoped language, which means that the scope of variables is determined statically, simply by the layout of the code. You can “follow the curly braces outwards” to see if a variable is in scope.


Here is an example of nested functions with variables at each scope level:

bool topLevel = true;

void main() {
  var insideMain = true;

  void myFunction() {
    var insideFunction = true;

    void nestedFunction() {
      var insideNestedFunction = true;


注意 nestedFunction() 函数可以访问包括顶层变量在内的所有的变量。

Notice how nestedFunction() can use variables from every level, all the way up to the top level.


Lexical closures

闭包 即一个函数对象,即使函数对象的调用在它原始作用域之外,依然能够访问在它词法作用域内的变量。

A closure is a function object that has access to variables in its lexical scope, even when the function is used outside of its original scope.

函数可以封闭定义到它作用域内的变量。接下来的示例中,函数 makeAdder() 捕获了变量 addBy。无论函数在什么时候返回,它都可以使用捕获的 addBy 变量。

Functions can close over variables defined in surrounding scopes. In the following example, makeAdder() captures the variable addBy. Wherever the returned function goes, it remembers addBy.

/// 返回一个将 [addBy] 添加到该函数参数的函数。
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(int addBy) {
  return (int i) => addBy + i;

void main() {
  // 生成加 2 的函数。
  var add2 = makeAdder(2);

  // 生成加 4 的函数。
  var add4 = makeAdder(4);

  assert(add2(3) == 5);
  assert(add4(3) == 7);


Testing functions for equality


Here’s an example of testing top-level functions, static methods, and instance methods for equality:

void foo() {} // 定义顶层函数 (A top-level function)

class A {
  static void bar() {} // 定义静态方法
  void baz() {} // 定义实例方法

void main() {
  Function x;

  // 比较顶层函数是否相等。
  x = foo;
  assert(foo == x);

  // 比较静态方法是否相等。
  x = A.bar;
  assert(A.bar == x);

  // 比较实例方法是否相等。
  var v = A(); // A 的实例 #1
  var w = A(); // A 的实例 #2
  var y = w;
  x = w.baz;

  // 这两个闭包引用了相同的实例对象,因此它们相等。
  assert(y.baz == x);

  // 这两个闭包引用了不同的实例对象,因此它们不相等。
  assert(v.baz != w.baz);


Return values

所有的函数都有返回值。没有显示返回语句的函数最后一行默认为执行 return null;

All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.

foo() {}

assert(foo() == null);



Dart 支持下表的操作符。你可以将这些运算符实现为 一个类的成员

Dart supports the operators shown in the following table. You can implement many of these operators as class members.






unary postfix

表达式++ 表达式-- () [] . ?.

expr++ expr-- () [] . ?.


unary prefix

-表达式 !表达式 ~表达式 ++表达式 --表达式

-expr !expr ~expr ++expr --expr await expr



* / % ~/



+ -



<< >> >>>


bitwise AND



bitwise XOR



bitwise OR



relational and type test

>= > <= < as is is!



== !=


logical AND



logical OR



if null




表达式 1 ? 表达式 2 : 表达式 3

expr1 ? expr2 : expr3



..    ?..

.. ?..



= *= /= += -= &= ^= 等等……

= *= /= += -= &= ^= etc.


When you use operators, you create expressions. Here are some examples of operator expressions:

a + b
a = b
a == b
c ? a : b
a is T

运算符表 中,运算符的优先级按先后排列,即第一行优先级最高,最后一行优先级最低,而同一行中,最左边的优先级最高,最右边的优先级最低。例如:% 运算符优先级高于 == ,而 == 高于 &&。根据优先级规则,那么意味着以下两行代码执行的效果相同:

In the operator table, each operator has higher precedence than the operators in the rows that follow it. For example, the multiplicative operator % has higher precedence than (and thus executes before) the equality operator ==, which has higher precedence than the logical AND operator &&. That precedence means that the following two lines of code execute the same way:

// 括号提高了可读性。
// Parentheses improve readability.
if ((n % i == 0) && (d % i == 0)) ...

// 难以理解,但是与上面的代码效果一样。
if (n % i == 0 && d % i == 0) ...


Arithmetic operators

Dart 支持常用的算术运算符:

Dart supports the usual arithmetic operators, as shown in the following table.

运算符 描述
-表达式 一元负, 也可以作为反转(反转表达式的符号)
~/ 除并取整
% 取模



assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // 结果是一个浮点数
assert(5 ~/ 2 == 2); // 结果是一个整数
assert(5 % 2 == 1); // 取余

assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');

Dart 还支持自增自减操作。

Dart also supports both prefix and postfix increment and decrement operators.



var = var + 1 (表达式的值为 var + 1)


var++ var = var + 1 (表达式的值为 var)
--var var = var – 1 (表达式的值为 var – 1)
var-- var = var – 1 (表达式的值为 var)



var a, b;

a = 0;
b = ++a; // 在 b 赋值前将 a 增加 1。
assert(a == b); // 1 == 1

a = 0;
b = a++; // 在 b 赋值后将 a 增加 1。
assert(a != b); // 1 != 0

a = 0;
b = --a; // 在 b 赋值前将 a 减少 1。
assert(a == b); // -1 == -1

a = 0;
b = a--; // 在 b 赋值后将 a 减少 1。
assert(a != b); // -1 != 0


Equality and relational operators


The following table lists the meanings of equality and relational operators.





!= 不等
> 大于
< 小于
>= 大于等于
<= 小于等于

要判断两个对象 x 和 y 是否表示相同的事物使用 == 即可。(在极少数情况下,可能需要使用 identical() 函数来确定两个对象是否完全相同。)。下面是 == 运算符的一些规则:

To test whether two objects x and y represent the same thing, use the == operator. (In the rare case where you need to know whether two objects are the exact same object, use the identical() function instead.) Here’s how the == operator works:

  1. 假设有变量 xy,且 x 和 y 至少有一个为 null,则当且仅当 x 和 y 均为 null 时 x == y 才会返回 true,否则只有一个为 null 则返回 false。

    If x or y is null, return true if both are null, and false if only one is null.

  2. x.==(y) 将会返回值,这里不管有没有 y,即 y 是可选的。也就是说 == 其实是 x 中的一个方法,并且可以被重写。详情请查阅重写运算符

    Return the result of the method invocation x.==(y). (That’s right, operators such as == are methods that are invoked on their first operand. For details, see Operators.)


Here’s an example of using each of the equality and relational operators:

assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);


Type test operators

asisis! 运算符是在运行时判断对象类型的运算符。

The as, is, and is! operators are handy for checking types at runtime.

Operator Meaning

类型转换(也用作指定 类前缀))

Typecast (also used to specify library prefixes)


如果对象是指定类型则返回 true

True if the object has the specified type


如果对象是指定类型则返回 false

True if the object doesn’t have the specified type

当且仅当 obj 实现了 T 的接口,obj is T 才是 true。例如 obj is Object 总为 true,因为所有类都是 Object 的子类。

The result of obj is T is true if obj implements the interface specified by T. For example, obj is Object? is always true.

仅当你确定这个对象是该类型的时候,你才可以使用 as 操作符可以把对象转换为特定的类型。例如:

Use the as operator to cast an object to a particular type if and only if you are sure that the object is of that type. Example:

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

如果你不确定这个对象类型是不是 T,请在转型前使用 is T 检查类型。

If you aren’t sure that the object is of type T, then use is T to check the type before using the object.

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

你可以使用 as 运算符进行缩写:

You can make the code shorter using the as operator:

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


Assignment operators

可以使用 = 来赋值,同时也可以使用 ??= 来为值为 null 的变量赋值。

As you’ve already seen, you can assign values using the = operator. To assign only if the assigned-to variable is null, use the ??= operator.

// Assign value to a
a = value;
// Assign value to b if b is null; otherwise, b stays the same
b ??= value;

+= 这样的赋值运算符将算数运算符和赋值运算符组合在了一起。

Compound assignment operators such as += combine an operation with an assignment.

= –= /= %= >>= ^=
+= *= ~/= <<= &= |=


Here’s how compound assignment operators work:

场景 复合运算 等效表达式
假设有运算符 op a op= b a = a op b
示例: a += b a = a + b


The following example uses assignment and compound assignment operators:

var a = 2; // 使用 = 赋值 (Assign using =)
a *= 3; // 赋值并做乘法运算 Assign and multiply: a = a * 3
assert(a == 6);


Logical operators


You can invert or combine boolean expressions using the logical operators.

运算符 描述
!表达式 对表达式结果取反(即将 true 变为 false,false 变为 true)
|| 逻辑或
&& 逻辑与


Here’s an example of using the logical operators:

if (!done && (col == 0 || col == 3)) {
  // ...Do something...


Bitwise and shift operators

在 Dart 中,二进制位运算符可以操作二进制的某一位,但仅适用于整数。

You can manipulate the individual bits of numbers in Dart. Usually, you’d use these bitwise and shift operators with integers.

运算符 描述
& 按位与
| 按位或
^ 按位异或
~表达式 按位取反(即将 “0” 变为 “1”,“1” 变为 “0”)
<< 位左移
>> 位右移


Here’s an example of using bitwise and shift operators:

final value = 0x22;
final bitmask = 0x0f;

assert((value & bitmask) == 0x02); // 按位与 (AND)
assert((value & ~bitmask) == 0x20); // 取反后按位与 (AND NOT)
assert((value | bitmask) == 0x2f); // 按位或 (OR)
assert((value ^ bitmask) == 0x2d); // 按位异或 (XOR)
assert((value << 4) == 0x220); // 位左移 (Shift left)
assert((value >> 4) == 0x02); // 位右移 (Shift right)


Conditional expressions

Dart 有两个特殊的运算符可以用来替代 if-else 语句:

Dart has two operators that let you concisely evaluate expressions that might otherwise require if-else statements:

condition ? expr1 : expr2
If condition is true, evaluates expr1 (and returns its value); otherwise, evaluates and returns the value of expr2.

条件 ? 表达式 1 : 表达式 2 :如果条件为 true,执行表达式 1并返回执行结果,否则执行表达式 2 并返回执行结果。

expr1 ?? expr2
If expr1 is non-null, returns its value; otherwise, evaluates and returns the value of expr2.

表达式 1 ?? 表达式 2:如果表达式 1 为非 null 则返回其值,否则执行表达式 2 并返回其值。

根据布尔表达式确定赋值时,请考虑使用 ?:

When you need to assign a value based on a boolean expression, consider using ? and :.

var visibility = isPublic ? 'public' : 'private';

如果赋值是根据判定是否为 null 则考虑使用 ??

If the boolean expression tests for null, consider using ??.

String playerName(String? name) => name ?? 'Guest';


The previous example could have been written at least two other ways, but not as succinctly:

// Slightly longer version uses ?: operator.
String playerName(String? name) => name != null ? name : 'Guest';

// Very long version uses if-else statement.
String playerName(String? name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';


Cascade notation

级联运算符 (.., ?..) 可以让你在同一个对象上连续调用多个对象的变量或方法。

Cascades (.., ?..) allow you to make a sequence of operations on the same object. In addition to function calls, you can also access fields on that same object. This often saves you the step of creating a temporary variable and allows you to write more fluid code.


Consider the following code:

var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;

The constructor, Paint(), returns a Paint object. The code that follows the cascade notation operates on this object, ignoring any values that might be returned.

The previous example is equivalent to this code:

var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;

If the object that the cascade operates on can be null, then use a null-shorting cascade (?..) for the first operation. Starting with ?.. guarantees that none of the cascade operations are attempted on that null object.

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


The previous code is equivalent to the following:

var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.onClick.listen((e) => window.alert('Confirmed!'));


You can also nest cascades. For example:

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = 'jenny@example.com'
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')


Be careful to construct your cascade on a function that returns an actual object. For example, the following code fails:

var sb = StringBuffer();
  ..write('bar'); // 出错:void 对象中没有方法 write (Error: method 'write' isn't defined for 'void').

上述代码中的 sb.write() 方法返回的是 void,返回值为 void 的方法则不能使用级联运算符。

The sb.write() call returns void, and you can’t construct a cascade on void.


Other operators


You’ve seen most of the remaining operators in other examples:

运算符 名字 描述
() 使用方法 代表调用一个方法
[] 访问 List 访问 List 中特定位置的元素
. 访问成员 成员访问符
?. 条件访问成员 与上述成员访问符类似,但是左边的操作对象不能为 null,例如 foo?.bar,如果 foo 为 null 则返回 null ,否则返回 bar

更多关于 ., ?... 运算符介绍,请参考.

For more information about the ., ?., and .. operators, see Classes.


Control flow statements

你可以使用下面的语句来控制 Dart 代码的执行流程:

You can control the flow of your Dart code using any of the following:

  • ifelse

    if and else

  • for 循环

    for loops

  • whiledo-while 循环

    while and do-while loops

  • breakcontinue

    break and continue

  • switchcase

    switch and case

  • assert

使用 try-catchthrow 也能影响控制流,详情参考异常部分。

You can also affect the control flow using try-catch and throw, as explained in Exceptions.

If 和 Else

If and else

Dart 支持 if - else 语句,其中 else 是可选的,比如下面的例子。你也可以参考条件表达式

Dart supports if statements with optional else statements, as the next sample shows. Also see conditional expressions.

if (isRaining()) {
} else if (isSnowing()) {
} else {

不同于 JavaScript,Dart 的 if 语句中的条件必须是布尔值而不能为其它类型。详情请查阅布尔值

Unlike JavaScript, conditions must use boolean values, nothing else. See Booleans for more information.

For 循环

For loops

你可以使用标准的 for 循环进行迭代。例如:

You can iterate with the standard for loop. For example:

var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {

在 Dart 语言中,for 循环中的闭包会自动捕获循环的 索引值 以避免 JavaScript 中一些常见的陷阱。假设有如下代码:

Closures inside of Dart’s for loops capture the value of the index, avoiding a common pitfall found in JavaScript. For example, consider:

var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
callbacks.forEach((c) => c());

上述代码执行后会输出 01,但是如果在 JavaScript 中执行同样的代码则会输出两个 2

The output is 0 and then 1, as expected. In contrast, the example would print 2 and then 2 in JavaScript.

如果要遍历的对象是一个可迭代对象(例如 List 或 Set),并且你不需要知道当前的遍历索引,则可以使用 for-in 方法进行 遍历

If the object that you are iterating over is an Iterable (such as List or Set) and if you don’t need to know the current iteration counter, you can use the for-in form of iteration:

for (var candidate in candidates) {

可迭代对象同时可以使用 forEach() 方法作为另一种选择:

Iterable classes also have a forEach() method as another option:

var collection = [1, 2, 3];
collection.forEach(print); // 1 2 3

While 和 Do-While

While and do-while

while 循环会在执行循环体前先判断条件:

A while loop evaluates the condition before the loop:

while (!isDone()) {

do-while 循环则会 先执行一遍循环体 再判断条件:

A do-while loop evaluates the condition after the loop:

do {
} while (!atEndOfPage());

Break 和 Continue

Break and continue

使用 break 可以中断循环:

Use break to stop looping:

while (true) {
  if (shutDownRequested()) break;

使用 continue 可以跳过本次循环直接进入下一次循环:

Use continue to skip to the next loop iteration:

for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {

如果你正在使用诸如 List 或 Set 之类的 Iterable 对象,你可以用以下方式重写上述例子:

You might write that example differently if you’re using an Iterable such as a list or set:

    .where((c) => c.yearsExperience >= 5)
    .forEach((c) => c.interview());

Switch 和 Case

Switch and case

Switch 语句在 Dart 中使用 == 来比较整数、字符串或编译时常量,比较的两个对象必须是同一个类型且不能是子类并且没有重写 == 操作符。 枚举类型非常适合在 Switch 语句中使用。

Switch statements in Dart compare integer, string, or compile-time constants using ==. The compared objects must all be instances of the same class (and not of any of its subtypes), and the class must not override ==. Enumerated types work well in switch statements.

每一个非空的 case 子句都必须有一个 break 语句,也可以通过 continuethrow 或者 return 来结束非空 case 语句。

Each non-empty case clause ends with a break statement, as a rule. Other valid ways to end a non-empty case clause are a continue, throw, or return statement.

不匹配任何 case 语句的情况下,会执行 default 子句中的代码:

Use a default clause to execute code when no case clause matches:

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
  case 'PENDING':
  case 'APPROVED':
  case 'DENIED':
  case 'OPEN':

下面的例子忽略了 case 子句的 break 语句,因此会产生错误:

The following example omits the break statement in a case clause, thus generating an error:

var command = 'OPEN';
switch (command) {
  case 'OPEN':
    // 错误: 没有 break

  case 'CLOSED':

但是,Dart 支持空的 case 语句,允许其以 fall-through 的形式执行。

However, Dart does support empty case clauses, allowing a form of fall-through:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // case 语句为空时的 fall-through 形式。
  case 'NOW_CLOSED':
    // case 条件值为 CLOSED 和 NOW_CLOSED 时均会执行该语句。

在非空 case 语句中想要实现 fall-through 的形式,可以使用 continue 语句配合 label 的方式实现:

If you really want fall-through, you can use a continue statement and a label:

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    continue nowClosed;
  // 继续执行标签为 nowClosed 的 case 子句。

  case 'NOW_CLOSED':
    // case 条件值为 CLOSED 和 NOW_CLOSED 时均会执行该语句。

每个 case 子句都可以有局部变量且仅在该 case 语句内可见。

A case clause can have local variables, which are visible only inside the scope of that clause.



在开发过程中,可以在条件表达式为 false 时使用 — assert(条件, 可选信息); — 语句来打断代码的执行。你可以在本文中找到大量使用 assert 的例子。下面是相关示例:

During development, use an assert statement — assert(condition, optionalMessage); — to disrupt normal execution if a boolean condition is false. You can find examples of assert statements throughout this tour. Here are some more:

// 确保变量值不为 null (Make sure the variable has a non-null value)
assert(text != null);

// 确保变量值小于 100。
assert(number < 100);

// 确保这是一个 https 地址。

assert 的第二个参数可以为其添加一个字符串消息。

To attach a message to an assertion, add a string as the second argument to assert (optionally with a trailing comma):

    'URL ($urlString) should start with "https".');

assert 的第一个参数可以是值为布尔值的任何表达式。如果表达式的值为 true,则断言成功,继续执行。如果表达式的值为 false,则断言失败,抛出一个 AssertionError 异常。

The first argument to assert can be any expression that resolves to a boolean value. If the expression’s value is true, the assertion succeeds and execution continues. If it’s false, the assertion fails and an exception (an AssertionError) is thrown.

如何判断 assert 是否生效?assert 是否生效依赖开发工具和使用的框架:

When exactly do assertions work? That depends on the tools and framework you’re using:

  • Flutter 在调试模式时生效。

    Flutter enables assertions in debug mode.

  • 一些开发工具比如 dartdevc 通常情况下是默认生效的。

    Development-only tools such as dartdevc typically enable assertions by default.

  • 其他一些工具,比如 dart 以及 dart2js 通过在运行 Dart 程序时添加命令行参数 --enable-asserts 使 assert 生效。

    Some tools, such as dart and dart2js, support assertions through a command-line flag: --enable-asserts.

在生产环境代码中,断言会被忽略,与此同时传入 assert 的参数不被判断。

In production code, assertions are ignored, and the arguments to assert aren’t evaluated.



Dart 代码可以抛出和捕获异常。异常表示一些未知的错误情况,如果异常没有捕获则会被抛出从而导致抛出异常的代码终止执行。

Your Dart code can throw and catch exceptions. Exceptions are errors indicating that something unexpected happened. If the exception isn’t caught, the isolate that raised the exception is suspended, and typically the isolate and its program are terminated.

与 Java 不同的是,Dart 的所有异常都是非必检异常,方法不必声明会抛出哪些异常,并且你也不必捕获任何异常。

In contrast to Java, all of Dart’s exceptions are unchecked exceptions. Methods don’t declare which exceptions they might throw, and you aren’t required to catch any exceptions.

Dart 提供了 ExceptionError 两种类型的异常以及它们一系列的子类,你也可以定义自己的异常类型。但是在 Dart 中可以将任何非 null 对象作为异常抛出而不局限于 Exception 或 Error 类型。

Dart provides Exception and Error types, as well as numerous predefined subtypes. You can, of course, define your own exceptions. However, Dart programs can throw any non-null object—not just Exception and Error objects—as an exception.



下面是关于抛出或者 引发 异常的示例:

Here’s an example of throwing, or raising, an exception:

throw FormatException('Expected at least 1 section');


You can also throw arbitrary objects:

throw 'Out of llamas!';

因为抛出异常是一个表达式,所以可以在 => 语句中使用,也可以在其他使用表达式的地方抛出异常:

Because throwing an exception is an expression, you can throw exceptions in => statements, as well as anywhere else that allows expressions:

void distanceTo(Point other) => throw UnimplementedError();




Catching, or capturing, an exception stops the exception from propagating (unless you rethrow the exception). Catching an exception gives you a chance to handle it:

try {
} on OutOfLlamasException {

对于可以抛出多种异常类型的代码,也可以指定多个 catch 语句,每个语句分别对应一个异常类型,如果 catch 语句没有指定异常类型则表示可以捕获任意异常类型:

To handle code that can throw more than one type of exception, you can specify multiple catch clauses. The first catch clause that matches the thrown object’s type handles the exception. If the catch clause does not specify a type, that clause can handle any type of thrown object:

try {
} on OutOfLlamasException {
  // 指定异常
} on Exception catch (e) {
  // 其它类型的异常
  print('Unknown exception: $e');
} catch (e) {
  // // 不指定类型,处理其它全部
  print('Something really unknown: $e');

如上述代码所示可以使用 oncatch 来捕获异常,使用 on 来指定异常类型,使用 catch 来捕获异常对象,两者可同时使用。

As the preceding code shows, you can use either on or catch or both. Use on when you need to specify the exception type. Use catch when your exception handler needs the exception object.

你可以为 catch 方法指定两个参数,第一个参数为抛出的异常对象,第二个参数为栈信息 StackTrace 对象:

You can specify one or two parameters to catch(). The first is the exception that was thrown, and the second is the stack trace (a StackTrace object).

try {
  // ···
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');

关键字 rethrow 可以将捕获的异常再次抛出:

To partially handle an exception, while allowing it to propagate, use the rethrow keyword.

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // 运行时错误
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // 允许调用者查看异常。

void main() {
  try {
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');


无论是否抛出异常,finally 语句始终执行,如果没有指定 catch 语句来捕获异常,则异常会在执行完 finally 语句后抛出:

To ensure that some code runs whether or not an exception is thrown, use a finally clause. If no catch clause matches the exception, the exception is propagated after the finally clause runs:

try {
} finally {
  // 总是清理,即便抛出了异常。

finally 语句会在任何匹配的 catch 语句后执行:

The finally clause runs after any matching catch clauses:

try {
} catch (e) {
  print('Error: $e'); // 先处理异常。
} finally {
  cleanLlamaStalls(); // 然后清理。

你可以阅读 Dart 核心库概览的 异常 章节获取更多相关信息。

Learn more by reading the Exceptions section of the library tour.


Dart 是支持基于 mixin 继承机制的面向对象语言,所有对象都是一个类的实例,而除了 Null 以外的所有的类都继承自 Object 类。 基于 mixin 的继承 意味着尽管每个类(top class Object? 除外)都只有一个超类,一个类的代码可以在其它多个类继承中重复使用。 扩展方法 是一种在不更改类或创建子类的情况下向类添加功能的方式。

Dart is an object-oriented language with classes and mixin-based inheritance. Every object is an instance of a class, and all classes except Null descend from Object. Mixin-based inheritance means that although every class (except for the top class,Object?) has exactly one superclass, a class body can be reused in multiple class hierarchies. Extension methods are a way to add functionality to a class without changing the class or creating a subclass.


Using class members

对象的 成员 由函数和数据(即 方法实例变量)组成。方法的 调用 要通过对象来完成,这种方式可以访问对象的函数和数据。

Objects have members consisting of functions and data (methods and instance variables, respectively). When you call a method, you invoke it on an object: the method has access to that object’s functions and data.


Use a dot (.) to refer to an instance variable or method:

var p = Point(2, 2);

// 获取 y 值
assert(p.y == 2);

// 调用变量 p 的 distanceTo() 方法。
double distance = p.distanceTo(Point(4, 4));

使用 ?. 代替 . 可以避免因为左边表达式为 null 而导致的问题:

Use ?. instead of . to avoid an exception when the leftmost operand is null:

// If p is non-null, set a variable equal to its y value.
var a = p?.y;


Using constructors

可以使用 构造函数 来创建一个对象。构造函数的命名方式可以为 类名 类名 . 标识符 的形式。例如下述代码分别使用 Point()Point.fromJson() 两种构造器创建了 Point 对象:

You can create an object using a constructor. Constructor names can be either ClassName or ClassName.identifier. For example, the following code creates Point objects using the Point() and Point.fromJson() constructors:

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});

以下代码具有相同的效果,但是构造函数名前面的的 new 关键字是可选的:

The following code has the same effect, but uses the optional new keyword before the constructor name:

var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});

一些类提供了常量构造函数。使用常量构造函数,在构造函数名之前加 const 关键字,来创建编译时常量时:

Some classes provide constant constructors. To create a compile-time constant using a constant constructor, put the const keyword before the constructor name:

var p = const ImmutablePoint(2, 2);


Constructing two identical compile-time constants results in a single, canonical instance:

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // 它们是同一个实例 (They are the same instance!)

常量上下文 场景中,你可以省略掉构造函数或字面量前的 const 关键字。例如下面的例子中我们创建了一个常量 Map:

Within a constant context, you can omit the const before a constructor or literal. For example, look at this code, which creates a const map:

// Lots of const keywords here.
// 这里有很多 const 关键字
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],

根据上下文,你可以只保留第一个 const 关键字,其余的全部省略:

You can omit all but the first use of the const keyword:

// Only one const, which establishes the constant context.
// 只需要一个 const 关键字,其它的则会隐式地根据上下文进行关联。
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],

但是如果无法根据上下文判断是否可以省略 const,则不能省略掉 const 关键字,否则将会创建一个 非常量对象 例如:

If a constant constructor is outside of a constant context and is invoked without const, it creates a non-constant object:

var a = const ImmutablePoint(1, 1); // 创建一个常量 (Creates a constant)
var b = ImmutablePoint(1, 1); // 不会创建一个常量 (Does NOT create a constant)

assert(!identical(a, b)); // 这两变量并不相同 (NOT the same instance!)


Getting an object’s type

可以使用 Object 对象的 runtimeType 属性在运行时获取一个对象的类型,该对象类型是 Type 的实例。

To get an object’s type at runtime, you can use the Object property runtimeType, which returns a Type object.

print('The type of a is ${a.runtimeType}');

到目前为止,我们已经解了如何 使用 类。本节的其余部分将向你介绍如何 实现 一个类。

Up to here, you’ve seen how to use classes. The rest of this section shows how to implement classes.


Instance variables


Here’s how you declare instance variables:

class Point {
  double? x; // Declare instance variable x, initially null.
  double? y; // Declare y, initially null.
  double z = 0; // Declare z, initially 0.

所有未初始化的实例变量其值均为 null

All uninitialized instance variables have the value null.

所有实例变量均会隐式地声明一个 Getter 方法。非终值的实例变量和 late final 声明但未声明初始化的实例变量还会隐式地声明一个 Setter 方法。你可以查阅 Getter 和 Setter 获取更多相关信息。

All instance variables generate an implicit getter method. Non-final instance variables and late final instance variables without initializers also generate an implicit setter method. For details, see Getters and setters.

class Point {
  double? x; // Declare instance variable x, initially null.
  double? y; // Declare y, initially null.

void main() {
  var point = Point();
  point.x = 4; // 使用 x 的 Setter 方法。
  assert(point.x == 4); // 使用 x 的 Getter 方法。
  assert(point.y == null); // 默认值为 null。

Instance variables can be final, in which case they must be set exactly once. Initialize final, non-late instance variables at declaration, using a constructor parameter, or using a constructor’s initializer list:

class ProfileMark {
  final String name;
  final DateTime start = DateTime.now();

  ProfileMark.unnamed() : name = '';

If you want to assign the value of a final instance variable after the constructor body starts, you can use late final, but be careful.



声明一个与类名一样的函数即可声明一个构造函数(对于命名式构造函数 还可以添加额外的标识符)。大部分的构造函数形式是生成式构造函数,其用于创建一个类的实例:

Declare a constructor by creating a function with the same name as its class (plus, optionally, an additional identifier as described in Named constructors). The most common form of constructor, the generative constructor, creates a new instance of a class:

class Point {
  double x = 0;
  double y = 0;

  Point(double x, double y) {
    // 还会有更好的方式来实现此逻辑,敬请期待。
    this.x = x;
    this.y = y;

使用 this 关键字引用当前实例。

The this keyword refers to the current instance.

对于大多数编程语言来说在构造函数中为实例变量赋值的过程都是类似的,而 Dart 则提供了一种特殊的语法糖来简化该步骤:

The pattern of assigning a constructor argument to an instance variable is so common, Dart has syntactic sugar to make it easy:

class Point {
  double x = 0;
  double y = 0;

  // 在构造函数体执行前用于设置 x 和 y 的语法糖。
  Point(this.x, this.y);


Default constructors

如果你没有声明构造函数,那么 Dart 会自动生成一个无参数的构造函数并且该构造函数会调用其父类的无参数构造方法。

If you don’t declare a constructor, a default constructor is provided for you. The default constructor has no arguments and invokes the no-argument constructor in the superclass.


Constructors aren’t inherited


Subclasses don’t inherit constructors from their superclass. A subclass that declares no constructors has only the default (no argument, no name) constructor.


Named constructors


Use a named constructor to implement multiple constructors for a class or to provide extra clarity:

const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  double x = 0;
  double y = 0;

  Point(this.x, this.y);

  // 命名式构造函数
      : x = xOrigin,
        y = yOrigin;


Remember that constructors are not inherited, which means that a superclass’s named constructor is not inherited by a subclass. If you want a subclass to be created with a named constructor defined in the superclass, you must implement that constructor in the subclass.


Invoking a non-default superclass constructor

默认情况下,子类的构造函数会调用父类的匿名无参数构造方法,并且该调用会在子类构造函数的函数体代码执行前,如果子类构造函数还有一个 初始化列表,那么该初始化列表会在调用父类的该构造函数之前被执行,总的来说,这三者的调用顺序如下:

By default, a constructor in a subclass calls the superclass’s unnamed, no-argument constructor. The superclass’s constructor is called at the beginning of the constructor body. If an initializer list is also being used, it executes before the superclass is called. In summary, the order of execution is as follows:

  1. 初始化列表

    initializer list

  2. 父类的无参数构造函数

    superclass’s no-arg constructor

  3. 当前类的构造函数

    main class’s no-arg constructor


If the superclass doesn’t have an unnamed, no-argument constructor, then you must manually call one of the constructors in the superclass. Specify the superclass constructor after a colon (:), just before the constructor body (if any).

下面的示例中,Employee 类的构造函数调用了父类 Person 的命名构造函数。点击运行按钮执行示例代码。

In the following example, the constructor for the Employee class calls the named constructor for its superclass, Person. Click Run to execute the code.

class Person {
  String? firstName;

  Person.fromJson(Map data) {
    print('in Person');

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');

void main() {
  var employee = Employee.fromJson({});
  // Prints:
  // in Person
  // in Employee
  // Instance of 'Employee'


Because the arguments to the superclass constructor are evaluated before invoking the constructor, an argument can be an expression such as a function call:

class Employee extends Person {
  Employee() : super.fromJson(fetchDefaultData());
  // ···


Initializer list


Besides invoking a superclass constructor, you can also initialize instance variables before the constructor body runs. Separate initializers with commas.

// Initializer list sets instance variables before
// the constructor body runs.
// 使用初始化列表在构造函数体执行前设置实例变量。
Point.fromJson(Map<String, double> json)
    : x = json['x']!,
      y = json['y']! {
  print('In Point.fromJson(): ($x, $y)');

在开发模式下,你可以在初始化列表中使用 assert 来验证输入数据:

During development, you can validate inputs by using assert in the initializer list.

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');

使用初始化列表设置 final 字段非常方便,下面的示例中就使用初始化列表来设置了三个 final 变量的值。点击运行按钮执行示例代码。

Initializer lists are handy when setting up final fields. The following example initializes three final fields in an initializer list. Click Run to execute the code.

import 'dart:math';

class Point {
  final double x;
  final double y;
  final double distanceFromOrigin;

  Point(double x, double y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);

void main() {
  var p = Point(2, 3);


Redirecting constructors


Sometimes a constructor’s only purpose is to redirect to another constructor in the same class. A redirecting constructor’s body is empty, with the constructor call appearing after a colon (:).

class Point {
  double x, y;

  // 该类的主构造函数。
  Point(this.x, this.y);

  // 委托实现给主构造函数。
  Point.alongXAxis(double x) : this(x, 0);


Constant constructors

如果类生成的对象都是不变的,可以在生成这些对象时就将其变为编译时常量。你可以在类的构造函数前加上 const 关键字并确保所有实例变量均为 final 来实现该功能。

If your class produces objects that never change, you can make these objects compile-time constants. To do this, define a const constructor and make sure that all instance variables are final.

class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);


Constant constructors don’t always create constants. For details, see the section on using constructors.


Factory constructors

使用 factory 关键字标识类的构造函数将会令该构造函数变为工厂构造函数,这将意味着使用该构造函数构造类的实例时并非总是会返回新的实例对象。例如,工厂构造函数可能会从缓存中返回一个实例,或者返回一个子类型的实例。

Use the factory keyword when implementing a constructor that doesn’t always create a new instance of its class. For example, a factory constructor might return an instance from a cache, or it might return an instance of a subtype. Another use case for factory constructors is initializing a final variable using logic that can’t be handled in the initializer list.

在如下的示例中, Logger 的工厂构造函数从缓存中返回对象,和 Logger.fromJson 工厂构造函数从 JSON 对象中初始化一个最终变量。

In the following example, the Logger factory constructor returns objects from a cache, and the Logger.fromJson factory constructor initializes a final variable from a JSON object.

class Logger {
  final String name;
  bool mute = false;

  // _cache 变量是库私有的,因为在其名字前面有下划线。
  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());


  void log(String msg) {
    if (!mute) print(msg);


Invoke a factory constructor just like you would any other constructor:

var logger = Logger('UI');
logger.log('Button clicked');

var logMap = {'name': 'UI'};
var loggerJson = Logger.fromJson(logMap);




Methods are functions that provide behavior for an object.


Instance methods

对象的实例方法可以访问实例变量和 this。下面的 distanceTo() 方法就是一个实例方法的例子:

Instance methods on objects can access instance variables and this. The distanceTo() method in the following sample is an example of an instance method:

import 'dart:math';

class Point {
  double x = 0;
  double y = 0;

  Point(this.x, this.y);

  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);



运算符是有着特殊名称的实例方法。 Dart 允许您使用以下名称定义运算符:

Operators are instance methods with special names. Dart allows you to define operators with the following names:

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>  

为了表示重写操作符,我们使用 operator 标识来进行标记。下面是重写 +- 操作符的例子:

An operator declaration is identified using the built-in identifier operator. The following example defines vector addition (+) and subtraction (-):

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);

  // Operator == and hashCode not shown.
  // ···

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

Getters and setters

Getter 和 Setter 是一对用来读写对象属性的特殊方法,上面说过实例对象的每一个属性都有一个隐式的 Getter 方法,如果为非 final 属性的话还会有一个 Setter 方法,你可以使用 getset 关键字为额外的属性添加 Getter 和 Setter 方法:

Getters and setters are special methods that provide read and write access to an object’s properties. Recall that each instance variable has an implicit getter, plus a setter if appropriate. You can create additional properties by implementing getters and setters, using the get and set keywords:

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

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

  // 定义两个计算产生的属性:right 和 bottom。
  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);

使用 Getter 和 Setter 的好处是,你可以先使用你的实例变量,过一段时间过再将它们包裹成方法且不需要改动任何代码,即先定义后更改且不影响原有逻辑。

With getters and setters, you can start with instance variables, later wrapping them with methods, all without changing client code.


Abstract methods

实例方法、Getter 方法以及 Setter 方法都可以是抽象的,定义一个接口方法而不去做具体的实现让实现它的类去实现该方法,抽象方法只能存在于 抽象类中。

Instance, getter, and setter methods can be abstract, defining an interface but leaving its implementation up to other classes. Abstract methods can only exist in abstract classes.


To make a method abstract, use a semicolon (;) instead of a method body:

abstract class Doer {
  // 定义实例变量和方法等等……

  void doSomething(); // 定义一个抽象方法。

class EffectiveDoer extends Doer {
  void doSomething() {
    // 提供一个实现,所以在这里该方法不再是抽象的……


Abstract classes

使用关键字 abstract 标识类可以让该类成为 抽象类,抽象类将无法被实例化。抽象类常用于声明接口方法、有时也会有具体的方法实现。如果想让抽象类同时可被实例化,可以为其定义 工厂构造函数

Use the abstract modifier to define an abstract class—a class that can’t be instantiated. Abstract classes are useful for defining interfaces, often with some implementation. If you want your abstract class to appear to be instantiable, define a factory constructor.

抽象类常常会包含 抽象方法。下面是一个声明具有抽象方法的抽象类示例:

Abstract classes often have abstract methods. Here’s an example of declaring an abstract class that has an abstract method:

// This class is declared abstract and thus
// can't be instantiated.
// 该类被声明为抽象的,因此它不能被实例化。
abstract class AbstractContainer {
  // 定义构造函数、字段、方法等……

  void updateChildren(); // 抽象方法。


Implicit interfaces

每一个类都隐式地定义了一个接口并实现了该接口,这个接口包含所有这个类的实例成员以及这个类所实现的其它接口。如果想要创建一个 A 类支持调用 B 类的 API 且不想继承 B 类,则可以实现 B 类的接口。

Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. If you want to create a class A that supports class B’s API without inheriting B’s implementation, class A should implement the B interface.

一个类可以通过关键字 implements 来实现一个或多个接口并实现每个接口定义的 API:

A class implements one or more interfaces by declaring them in an implements clause and then providing the APIs required by the interfaces. For example:

// A person. The implicit interface contains greet().
// Person 类的隐式接口中包含 greet() 方法。
class Person {
  // In the interface, but visible only in this library.
  final String _name;

  // 构造函数不在接口中。

  // greet() 方法在接口中。
  String greet(String who) => '你好,$who。我是$_name。';

// Person 接口的一个实现。
class Impostor implements Person {
  String get _name => '';

  String greet(String who) => '你好$who。你知道我是谁吗?';

String greetBob(Person person) => person.greet('小芳');

void main() {


Here’s an example of specifying that a class implements multiple interfaces:

class Point implements Comparable, Location {...}


Extending a class

使用 extends 关键字来创建一个子类,并可使用 super 关键字引用一个父类:

Use extends to create a subclass, and super to refer to the superclass:

class Television {
  void turnOn() {
  // ···

class SmartTelevision extends Television {
  void turnOn() {
  // ···


Overriding members

子类可以重写父类的实例方法(包括 操作符)、 Getter 以及 Setter 方法。你可以使用 @override 注解来表示你重写了一个成员:

Subclasses can override instance methods (including operators), getters, and setters. You can use the @override annotation to indicate that you are intentionally overriding a member:

class SmartTelevision extends Television {
  void turnOn() {...}
  // ···

你可以使用 covariant 关键字 来缩小代码中那些符合 类型安全 的方法参数或实例变量的类型。

To narrow the type of a method parameter or instance variable in code that is type safe, you can use the covariant keyword.

noSuchMethod 方法


如果调用了对象上不存在的方法或实例变量将会触发 noSuchMethod 方法,你可以重写 noSuchMethod 方法来追踪和记录这一行为:

To detect or react whenever code attempts to use a non-existent method or instance variable, you can override noSuchMethod():

class A {
  // 除非你重写 noSuchMethod,否则调用一个不存在的成员会导致 NoSuchMethodError。
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: '


You can’t invoke an unimplemented method unless one of the following is true:

  • 接收方是静态的 dynamic 类型。

    The receiver has the static type dynamic.

  • 接收方具有静态类型,定义了未实现的方法(抽象亦可),并且接收方的动态类型实现了 noSuchMethod 方法且具体的实现与 Object 中的不同。

    The receiver has a static type that defines the unimplemented method (abstract is OK), and the dynamic type of the receiver has an implemention of noSuchMethod() that’s different from the one in class Object.

你可以查阅 noSuchMethod 转发规范 获取更多相关信息。

For more information, see the informal noSuchMethod forwarding specification.


Extension methods

扩展方法是向现有库添加功能的一种方式。你可能已经在不知道它是扩展方法的情况下使用了它。例如,当您在 IDE 中使用代码完成功能时,它建议将扩展方法与常规方法一起使用。

Extension methods are a way to add functionality to existing libraries. You might use extension methods without even knowing it. For example, when you use code completion in an IDE, it suggests extension methods alongside regular methods.

这里是一个在 String 中使用扩展方法的样例,我们取名为 parseInt(),它在 string_apis.dart 中定义:

Here’s an example of using an extension method on String named parseInt() that’s defined in string_apis.dart:

import 'string_apis.dart';
print('42'.padLeft(5)); // Use a String method.
print('42'.parseInt()); // Use an extension method.

有关使用以及实现扩展方法的详细信息,请参阅 扩展方法页面

For details of using and implementing extension methods, see the extension methods page.


Enumerated types

枚举类型是一种特殊的类型,也称为 enumerationsenums,用于定义一些固定数量的常量值。

Enumerated types, often called enumerations or enums, are a special kind of class used to represent a fixed number of constant values.

Using enums


使用关键字 enum 来定义枚举类型:

Declare an enumerated type using the enum keyword:

enum Color { red, green, blue }

你可以在声明枚举类型时使用 尾随逗号

You can use trailing commas when declaring an enumerated type.

每一个枚举值都有一个名为 index 成员变量的 Getter 方法,该方法将会返回以 0 为基准索引的位置值。例如,第一个枚举值的索引是 0 ,第二个枚举值的索引是 1。以此类推。

Each value in an enum has an index getter, which returns the zero-based position of the value in the enum declaration. For example, the first value has index 0, and the second value has index 1.

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

想要获得全部的枚举值,使用枚举类的 values 方法获取包含它们的列表:

To get a list of all of the values in the enum, use the enum’s values constant.

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);

你可以在 Switch 语句中使用枚举,但是需要注意的是必须处理枚举值的每一种情况,即每一个枚举值都必须成为一个 case 子句,不然会出现警告:

You can use enums in switch statements, and you’ll get a warning if you don’t handle all of the enum’s values:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
  case Color.green:
  default: // 没有该语句会出现警告。
    print(aColor); // 'Color.blue'


Enumerated types have the following limits:

  • 枚举不能成为子类,也不可以 mix in,你也不可以实现一个枚举。

    You can’t subclass, mix in, or implement an enum.

  • 不能显式地实例化一个枚举类。

    You can’t explicitly instantiate an enum.

你可以查阅 Dart 编程语言规范 获取更多相关信息。

For more information, see the Dart language specification.

使用 Mixin 为类添加功能

Adding features to a class: mixins

Mixin 是一种在多重继承中复用某个类中代码的方法模式。

Mixins are a way of reusing a class’s code in multiple class hierarchies.

使用 with 关键字并在其后跟上 Mixin 类的名字来使用 Mixin 模式:

To use a mixin, use the with keyword followed by one or more mixin names. The following example shows two classes that use mixins:

class Musician extends Performer with Musical {
  // ···

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;

想要实现一个 Mixin,请创建一个继承自 Object 且未声明构造函数的类。除非你想让该类与普通的类一样可以被正常地使用,否则请使用关键字 mixin 替代 class。例如:

To implement a mixin, create a class that extends Object and declares no constructors. Unless you want your mixin to be usable as a regular class, use the mixin keyword instead of class. For example:

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');

可以使用关键字 on 来指定哪些类可以使用该 Mixin 类,比如有 Mixin 类 A,但是 A 只能被 B 类使用,则可以这样定义 A:

Sometimes you might want to restrict the types that can use a mixin. For example, the mixin might depend on being able to invoke a method that the mixin doesn’t define. As the following example shows, you can restrict a mixin’s use by using the on keyword to specify the required superclass:

class Musician {
  // ...
mixin MusicalPerformer on Musician {
  // ...
class SingerDancer extends Musician with MusicalPerformer {
  // ...

In the preceding code, only classes that extend or implement the Musician class can use the mixin MusicalPerformer. Because SingerDancer extends Musician, SingerDancer can mix in MusicalPerformer.


Class variables and methods

使用关键字 static 可以声明类变量或类方法。

Use the static keyword to implement class-wide variables and methods.


Static variables


Static variables (class variables) are useful for class-wide state and constants:

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

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


Static variables aren’t initialized until they’re used.


Static methods

静态方法(即类方法)不能对实例进行操作,因此不能使用 this。但是他们可以访问静态变量。如下面的例子所示,你可以在一个类上直接调用静态方法:

Static methods (class methods) don’t operate on an instance, and thus don’t have access to this. They do, however, have access to static variables. As the following example shows, you invoke static methods directly on a class:

import 'dart:math';

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

  static double 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);


You can use static methods as compile-time constants. For example, you can pass a static method as a parameter to a constant constructor.



如果你查看数组的 API 文档,你会发现数组 List 的实际类型为 List<E>。 <…> 符号表示数组是一个 泛型(或 参数化类型通常 使用一个字母来代表类型参数,比如 E、T、S、K 和 V 等等。

If you look at the API documentation for the basic array type, List, you’ll see that the type is actually List<E>. The <…> notation marks List as a generic (or parameterized) type—a type that has formal type parameters. By convention, most type variables have single-letter names, such as E, T, S, K, and V.


Why use generics?


Generics are often required for type safety, but they have more benefits than just allowing your code to run:

  • 适当地指定泛型可以更好地帮助代码生成。

    Properly specifying generic types results in better generated code.

  • 使用泛型可以减少代码重复。

    You can use generics to reduce code duplication.

比如你想声明一个只能包含 String 类型的数组,你可以将该数组声明为 List<String>(读作“字符串类型的 list”),这样的话就可以很容易避免因为在该数组放入非 String 类变量而导致的诸多问题,同时编译器以及其他阅读代码的人都可以很容易地发现并定位问题:

If you intend for a list to contain only strings, you can declare it as List<String> (read that as “list of string”). That way you, your fellow programmers, and your tools can detect that assigning a non-string to the list is probably a mistake. Here’s an example:

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


Another reason for using generics is to reduce code duplication. Generics let you share a single interface and implementation between many types, while still taking advantage of static analysis. For example, say you create an interface for caching an object:

abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);

不久后你可能又会想专门为 String 类对象做一个缓存,于是又有了专门为 String 做缓存的类:

You discover that you want a string-specific version of this interface, so you create another interface:

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);


Later, you decide you want a number-specific version of this interface… You get the idea.


Generic types can save you the trouble of creating all these interfaces. Instead, you can create a single interface that takes a type parameter:

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);

在上述代码中,T 是一个替代类型。其相当于类型占位符,在开发者调用该接口的时候会指定具体类型。

In this code, T is the stand-in type. It’s a placeholder that you can think of as a type that a developer will define later.


Using collection literals

List、Set 以及 Map 字面量也可以是参数化的。定义参数化的 List 只需在中括号前添加 <type>;定义参数化的 Map 只需要在大括号前添加 <keyType, valueType>

List, set, and map literals can be parameterized. Parameterized literals are just like the literals you’ve already seen, except that you add <type> (for lists and sets) or <keyType, valueType> (for maps) before the opening bracket. Here is an example of using typed literals:

var names = <String>['小芸', '小芳', '小民'];
var uniqueNames = <String>{'小芸', '小芳', '小民'};
var pages = <String, String>{
  'index.html': '主页',
  'robots.txt': '网页机器人提示',
  'humans.txt': '我们是人类,不是机器'


Using parameterized types with constructors


To specify one or more types when using a constructor, put the types in angle brackets (<...>) just after the class name. For example:

var nameSet = Set<String>.from(names);

下面代码创建了一个键为 Int 类型,值为 View 类型的 Map 对象:

The following code creates a map that has integer keys and values of type View:

var views = Map<int, View>();


Generic collections and the types they contain

Dart的泛型类型是 固化的,这意味着即便在运行时也会保持类型信息:

Dart generic types are reified, which means that they carry their type information around at runtime. For example, you can test the type of a collection:

var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true


Restricting the parameterized type

有时使用泛型的时候可能会想限制泛型的类型范围,这时候可以使用 extends 关键字:

When implementing a generic type, you might want to limit the types of its parameters. You can do this using extends.

class Foo<T extends SomeBaseClass> {
  // 具体实现……
  String toString() => "'Foo<$T>' 的实例";

class Extender extends SomeBaseClass {...}

这时候就可以使用 SomeBaseClass 或者它的子类来作为泛型参数:

It’s OK to use SomeBaseClass or any of its subclasses as generic argument:

var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();

这时候也可以指定无参数的泛型,这时无参数泛型的类型则为 Foo<SomeBaseClass>

It’s also OK to specify no generic argument:

var foo = Foo();
print(foo); // 'Foo<SomeBaseClass>' 的实例 (Instance of 'Foo<SomeBaseClass>')

将非 SomeBaseClass 的类型作为泛型参数则会导致编译错误:

Specifying any non-SomeBaseClass type results in an error:

var foo = Foo<Object>();


Using generic methods

起初 Dart 只支持在类的声明时指定泛型,现在同样也可以在方法上使用泛型,称之为 泛型方法

Initially, Dart’s generic support was limited to classes. A newer syntax, called generic methods, allows type arguments on methods and functions:

T first<T>(List<T> ts) {
  // 处理一些初始化工作或错误检测……
  T tmp = ts[0];
  // 处理一些额外的检查……
  return tmp;

方法 first<T> 的泛型 T 可以在如下地方使用:

Here the generic type parameter on first (<T>) allows you to use the type argument T in several places:

  • 函数的返回值类型 (T)。

    In the function’s return type (T).

  • 参数的类型 (List<T>)。

    In the type of an argument (List<T>).

  • 局部变量的类型 (T tmp)。

    In the type of a local variable (T tmp).

你可以查阅 使用泛型函数 获取更多关于泛型的信息。

For more information about generics, see Using Generic Methods.


Libraries and visibility

importlibrary 关键字可以帮助你创建一个模块化和可共享的代码库。代码库不仅只是提供 API 而且还起到了封装的作用:以下划线(_)开头的成员仅在代码库中可见。 每个 Dart 程序都是一个库,即便没有使用关键字 library 指定。

The import and library directives can help you create a modular and shareable code base. Libraries not only provide APIs, but are a unit of privacy: identifiers that start with an underscore (_) are visible only inside the library. Every Dart app is a library, even if it doesn’t use a library directive.

Dart 的库可以使用 包工具 来发布和部署。

Libraries can be distributed using packages.


Using libraries

使用 import 来指定命名空间以便其它库可以访问。

Use import to specify how a namespace from one library is used in the scope of another library.

比如你可以导入代码库 dart:html 来使用 Dart Web 中相关 API:

For example, Dart web apps generally use the dart:html library, which they can import like this:

import 'dart:html';

import 的唯一参数是用于指定代码库的 URI,对于 Dart 内置的库,使用 dart:xxxxxx 的形式。而对于其它的库,你可以使用一个文件系统路径或者以 package:xxxxxx 的形式。 package:xxxxxx 指定的库通过包管理器(比如 pub 工具)来提供:

The only required argument to import is a URI specifying the library. For built-in libraries, the URI has the special dart: scheme. For other libraries, you can use a file system path or the package: scheme. The package: scheme specifies libraries provided by a package manager such as the pub tool. For example:

import 'package:test/test.dart';


Specifying a library prefix

如果你导入的两个代码库有冲突的标识符,你可以为其中一个指定前缀。比如如果 library1 和 library2 都有 Element 类,那么可以这么处理:

If you import two libraries that have conflicting identifiers, then you can specify a prefix for one or both libraries. For example, if library1 and library2 both have an Element class, then you might have code like this:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// 使用 lib1 的 Element 类。
Element element1 = Element();

// 使用 lib2 的 Element 类。
lib2.Element element2 = lib2.Element();


Importing only part of a library


If you want to use only part of a library, you can selectively import the library. For example:

// 只导入 lib1 中的 foo。(Import only foo).
import 'package:lib1/lib1.dart' show foo;

// 导入 lib2 中除了 foo 外的所有。
import 'package:lib2/lib2.dart' hide foo;


Lazily loading a library

延迟加载(也常称为 懒加载)允许应用在需要时再去加载代码库,下面是可能使用到延迟加载的场景:

Deferred loading (also called lazy loading) allows a web app to load a library on demand, if and when the library is needed. Here are some cases when you might use deferred loading:

  • 为了减少应用的初始化时间。

    To reduce a web app’s initial startup time.

  • 处理 A/B 测试,比如测试各种算法的不同实现。

    To perform A/B testing—trying out alternative implementations of an algorithm, for example.

  • 加载很少会使用到的功能,比如可选的屏幕和对话框。

    To load rarely used functionality, such as optional screens and dialogs.

使用 deferred as 关键字来标识需要延时加载的代码库:

To lazily load a library, you must first import it using deferred as.

import 'package:greetings/hello.dart' deferred as hello;

当实际需要使用到库中 API 时先调用 loadLibrary 函数加载库:

When you need the library, invoke loadLibrary() using the library’s identifier.

Future<void> greet() async {
  await hello.loadLibrary();

在前面的代码,使用 await 关键字暂停代码执行直到库加载完成。更多关于 asyncawait 的信息请参考异步支持

In the preceding code, the await keyword pauses execution until the library is loaded. For more information about async and await, see asynchrony support.

loadLibrary 函数可以调用多次也没关系,代码库只会被加载一次。

You can invoke loadLibrary() multiple times on a library without problems. The library is loaded only once.


Keep in mind the following when you use deferred loading:

  • 延迟加载的代码库中的常量需要在代码库被加载的时候才会导入,未加载时是不会导入的。

    A deferred library’s constants aren’t constants in the importing file. Remember, these constants don’t exist until the deferred library is loaded.

  • 导入文件的时候无法使用延迟加载库中的类型。如果你需要使用类型,则考虑把接口类型转移到另一个库中然后让两个库都分别导入这个接口库。

    You can’t use types from a deferred library in the importing file. Instead, consider moving interface types to a library imported by both the deferred library and the importing file.

  • Dart会隐式地将 loadLibrary() 导入到使用了 deferred as 命名空间 的类中。 loadLibrary() 函数返回的是一个 Future

    Dart implicitly inserts loadLibrary() into the namespace that you define using deferred as namespace. The loadLibrary() function returns a Future.


Implementing libraries

查阅 创建依赖库包 可以获取有关如何实现库包的建议,包括:

See Create Library Packages for advice on how to implement a library package, including:

  • 如何组织库的源文件。

    How to organize library source code.

  • 如何使用 export 命令。

    How to use the export directive.

  • 何时使用 part 命令。

    When to use the part directive.

  • 何时使用 library 命令。

    When to use the library directive.

  • 如何使用倒入和导出命令实现多平台的库支持。

    How to use conditional imports and exports to implement a library that supports multiple platforms.


Asynchrony support

Dart 代码库中有大量返回 FutureStream 对象的函数,这些函数都是 异步 的,它们会在耗时操作(比如I/O)执行完毕前直接返回而不会等待耗时操作执行完毕。

Dart libraries are full of functions that return Future or Stream objects. These functions are asynchronous: they return after setting up a possibly time-consuming operation (such as I/O), without waiting for that operation to complete.

asyncawait 关键字用于实现异步编程,并且让你的代码看起来就像是同步的一样。

The async and await keywords support asynchronous programming, letting you write asynchronous code that looks similar to synchronous code.

处理 Future

Handling Futures

可以通过下面两种方式,获得 Future 执行完成的结果:

When you need the result of a completed Future, you have two options:

  • 使用 asyncawait

    Use async and await.

  • 使用 Future API,具体描述参考 库概览

    Use the Future API, as described in the library tour.

使用 asyncawait 的代码是异步的,但是看起来有点像同步代码。例如,下面的代码使用 await 等待异步函数的执行结果。

Code that uses async and await is asynchronous, but it looks a lot like synchronous code. For example, here’s some code that uses await to wait for the result of an asynchronous function:

await lookUpVersion();

必须在带有 async 关键字的 异步函数 中使用 await

To use await, code must be in an async function—a function marked as async:

Future<void> checkVersion() async {
  var version = await lookUpVersion();
  // 使用 version 继续处理逻辑

使用 trycatch 以及 finally 来处理使用 await 导致的异常:

Use try, catch, and finally to handle errors and cleanup in code that uses await:

try {
  version = await lookUpVersion();
} catch (e) {
  // 无法找到版本时做出的反应

你可以在异步函数中多次使用 await 关键字。例如,下面代码中等待了三次函数结果:

You can use await multiple times in an async function. For example, the following code waits three times for the results of functions:

var entrypoint = await findEntryPoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);

await 表达式的返回值通常是一个 Future 对象;如果不是的话也会自动将其包裹在一个 Future 对象里。Future 对象代表一个“承诺”,await 表达式会阻塞直到需要的对象返回。

In await expression, the value of expression is usually a Future; if it isn’t, then the value is automatically wrapped in a Future. This Future object indicates a promise to return an object. The value of await expression is that returned object. The await expression makes execution pause until that object is available.

如果在使用 await 时导致编译错误,请确保 await 在一个异步函数中使用。例如,如果想在 main() 函数中使用 await,那么 main() 函数就必须使用 async 关键字标识。

If you get a compile-time error when using await, make sure await is in an async function. For example, to use await in your app’s main() function, the body of main() must be marked as async:

Future<void> main() async {
  print('在 Main 函数中执行:版本是 ${await lookUpVersion()}');


Declaring async functions

异步函数 是函数体由 async 关键字标记的函数。

An async function is a function whose body is marked with the async modifier.

将关键字 async 添加到函数并让其返回一个 Future 对象。假设有如下返回 String 对象的方法:

Adding the async keyword to a function makes it return a Future. For example, consider this synchronous function, which returns a String:

String lookUpVersion() => '1.0.0';

将其改为异步函数,返回值是 Future:

If you change it to be an async function—for example, because a future implementation will be time consuming—the returned value is a Future:

Future<String> lookUpVersion() async => '1.0.0';

注意,函数体不需要使用 Future API。如有必要,Dart 会创建 Future 对象。

Note that the function’s body doesn’t need to use the Future API. Dart creates the Future object if necessary.

如果函数没有返回有效值,需要设置其返回类型为 Future<void>

If your function doesn’t return a useful value, make its return type Future<void>.

关于 Future、asyncawait 的使用介绍,可以参见这个 codelab: asynchronous programming codelab

For an interactive introduction to using futures, async, and await, see the asynchronous programming codelab.

处理 Stream

Handling Streams

如果想从 Stream 中获取值,可以有两种选择:

When you need to get values from a Stream, you have two options:

  • 使用 async 关键字和一个 异步循环(使用 await for 关键字标识)。

    Use async and an asynchronous for loop (await for).

  • 使用 Stream API。详情参考 库概览

    Use the Stream API, as described in the library tour.

使用 await for 定义异步循环看起来是这样的:

An asynchronous for loop has the following form:

await for (varOrType identifier in expression) {
  // 每当 Stream 发出一个值时会执行

表达式 的类型必须是 Stream。执行流程如下:

The value of expression must have type Stream. Execution proceeds as follows:

  1. 等待直到 Stream 返回一个数据。

    Wait until the stream emits a value.

  2. 使用 1 中 Stream 返回的数据执行循环体。

    Execute the body of the for loop, with the variable set to that emitted value.

  3. 重复 1、2 过程直到 Stream 数据返回完毕。

    Repeat 1 and 2 until the stream is closed.

使用 breakreturn 语句可以停止接收 Stream 数据,这样就跳出了循环并取消注册监听 Stream。

To stop listening to the stream, you can use a break or return statement, which breaks out of the for loop and unsubscribes from the stream.

**如果在实现异步 for 循环时遇到编译时错误,请检查确保 await for 处于异步函数中。 ** 例如,要在应用程序的 main() 函数中使用异步 for 循环,main() 函数体必须标记为 async

If you get a compile-time error when implementing an asynchronous for loop, make sure the await for is in an async function. For example, to use an asynchronous for loop in your app’s main() function, the body of main() must be marked as async:

Future<void> main() async {
  // ...
  await for (var request in requestServer) {
  // ...

你可以查阅库概览中有关 dart:async 的部分获取更多有关异步编程的信息。

For more information about asynchronous programming, in general, see the dart:async section of the library tour.



当你需要延迟地生成一连串的值时,可以考虑使用 生成器函数。Dart 内置支持两种形式的生成器方法:

When you need to lazily produce a sequence of values, consider using a generator function. Dart has built-in support for two kinds of generator functions:

  • 同步 生成器:返回一个 Iterable 对象。

    Synchronous generator: Returns an Iterable object.

  • 异步 生成器:返回一个 Stream 对象。

    Asynchronous generator: Returns a Stream object.

通过在函数上加 sync* 关键字并将返回值类型设置为 Iterable 来实现一个 同步 生成器函数,在函数中使用 yield 语句来传递值:

To implement a synchronous generator function, mark the function body as sync*, and use yield statements to deliver values:

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;

实现 异步 生成器函数与同步类似,只不过关键字为 async* 并且返回值为 Stream:

To implement an asynchronous generator function, mark the function body as async*, and use yield statements to deliver values:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;

如果生成器是递归调用的,可是使用 yield* 语句提升执行性能:

If your generator is recursive, you can improve its performance by using yield*:

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


Callable classes

通过实现类的 call() 方法,允许使用类似函数调用的方式来使用该类的实例。

To allow an instance of your Dart class to be called like a function, implement the call() method.

在下面的示例中,WannabeFunction 类定义了一个 call() 函数,函数接受三个字符串参数,函数体将三个字符串拼接,字符串间用空格分割,并在结尾附加了一个感叹号。单击运行按钮执行代码。

In the following example, the WannabeFunction class defines a call() function that takes three strings and concatenates them, separating each with a space, and appending an exclamation. Click Run to execute the code.

class WannabeFunction {
  String call(String a, String b, String c) => '$a $b $c!';

var wf = WannabeFunction();
var out = wf('你好', ',使用 Dart 的', '朋友');

void main() => print(out);



大多数计算机中,甚至在移动平台上,都在使用多核 CPU。为了有效利用多核性能,开发者一般使用共享内存的方式让线程并发地运行。然而,多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。

Most computers, even on mobile platforms, have multi-core CPUs. To take advantage of all those cores, developers traditionally use shared-memory threads running concurrently. However, shared-state concurrency is error prone and can lead to complicated code.

为了解决多线程带来的并发问题,Dart 使用 isolate 替代线程,所有的 Dart 代码均运行在一个 isolate 中。每一个 isolate 有它自己的堆内存以确保其状态不被其它 isolate 访问。

Instead of threads, all Dart code runs inside of isolates. Each isolate has its own memory heap, ensuring that no isolate’s state is accessible from any other isolate.


For more information, see the following:


A type alias — often called a typedef because it’s declared with the keyword typedef — is a concise way to refer to a type. Here’s an example of declaring and using a type alias named IntList:

typedef IntList = List<int>;
IntList il = [1, 2, 3];

A type alias can have type parameters:

typedef ListMapper<X> = Map<X, List<X>>;
Map<String, List<String>> m1 = {}; // Verbose.
ListMapper<String> m2 = {}; // Same thing but shorter and clearer.

We recommend using inline function types instead of typedefs for functions, in most situations. However, function typedefs can still be useful:

typedef Compare<T> = int Function(T a, T b);

int sort(int a, int b) => a - b;

void main() {
  assert(sort is Compare<int>); // True!



使用元数据可以为代码增加一些额外的信息。元数据注解以 @ 开头,其后紧跟一个编译时常量(比如 deprecated)或者调用一个常量构造函数。

Use metadata to give additional information about your code. A metadata annotation begins with the character @, followed by either a reference to a compile-time constant (such as deprecated) or a call to a constant constructor.

Dart 中有两个注解是所有代码都可以使用的:@deprecated@override。你可以查阅 扩展一个类 获取有关 @override 的使用示例。下面是使用 @deprecated 的示例:

Two annotations are available to all Dart code: @deprecated and @override. For examples of using @override, see Extending a class. Here’s an example of using the @deprecated annotation:

class Television {
  /// _弃用: 使用 [turnOn] 替代_
  void activate() {

  /// 打开 TV 的电源。
  void turnOn() {...}

可以自定义元数据注解。下面的示例定义了一个带有两个参数的 @todo 注解:

You can define your own metadata annotations. Here’s an example of defining a @todo annotation that takes two arguments:

library todo;

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);

使用 @Todo 注解的示例:

And here’s an example of using that @todo annotation:

import 'todo.dart';

@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');

元数据可以在 library、class、typedef、type parameter、 constructor、factory、function、field、parameter 或者 variable 声明之前使用,也可以在 import 或 export 之前使用。可使用反射在运行时获取元数据信息。

Metadata can appear before a library, class, typedef, type parameter, constructor, factory, function, field, parameter, or variable declaration and before an import or export directive. You can retrieve metadata at runtime using reflection.



Dart 支持单行注释、多行注释和文档注释。

Dart supports single-line comments, multi-line comments, and documentation comments.


Single-line comments

单行注释以 // 开始。所有在 // 和该行结尾之间的内容均被编译器忽略。

A single-line comment begins with //. Everything between // and the end of line is ignored by the Dart compiler.

void main() {
  // TODO: refactor into an AbstractLlamaGreetingFactory?
  print('Welcome to my Llama farm!');


Multi-line comments

多行注释以 /* 开始,以 */ 结尾。所有在 /**/ 之间的内容均被编译器忽略(不会忽略文档注释),多行注释可以嵌套。

A multi-line comment begins with /* and ends with */. Everything between /* and */ is ignored by the Dart compiler (unless the comment is a documentation comment; see the next section). Multi-line comments can nest.

void main() {
   * This is a lot of work. Consider raising chickens.

  Llama larry = Llama();


Documentation comments

文档注释可以是多行注释,也可以是单行注释,文档注释以 /// 或者 /** 开始。在连续行上使用 /// 与多行文档注释具有相同的效果。

Documentation comments are multi-line or single-line comments that begin with /// or /**. Using /// on consecutive lines has the same effect as a multi-line doc comment.


Inside a documentation comment, the analyzer ignores all text unless it is enclosed in brackets. Using brackets, you can refer to classes, methods, fields, top-level variables, functions, and parameters. The names in brackets are resolved in the lexical scope of the documented program element.


Here is an example of documentation comments with references to other classes and arguments:

/// A domesticated South American camelid (Lama glama).
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
/// Just like any other animal, llamas need to eat,
/// so don't forget to [feed] them some [Food].
class Llama {
  String? name;

  /// Feeds your llama [food].
  /// The typical llama eats one bale of hay per week.
  void feed(Food food) {
    // ...

  /// Exercises your llama with an [activity] for
  /// [timeLimit] minutes.
  void exercise(Activity activity, int timeLimit) {
    // ...

在生成的文档中,[feed] 会成为一个链接,指向 feed 方法的文档, [Food] 会成为一个链接,指向 Food 类的 API 文档。

In the class’s generated documentation, [feed] becomes a link to the docs for the feed method, and [Food] becomes a link to the docs for the Food class.

解析 Dart 代码并生成 HTML 文档,可以使用 Dart 的 文档生成工具。关于生成文档的示例,请参考 Dart API documentation 查看关于文档结构的建议,请参考文档: Guidelines for Dart Doc Comments.

To parse Dart code and generate HTML documentation, you can use Dart’s documentation generation tool. For an example of generated documentation, see the Dart API documentation. For advice on how to structure your comments, see Guidelines for Dart Doc Comments.



本页概述了 Dart 语言中常用的功能。还有更多特性有待实现,但我们希望它们不会破坏现有代码。有关更多信息,请参考 Dart 语言规范高效 Dart 语言指南

This page summarized the commonly used features in the Dart language. More features are being implemented, but we expect that they won’t break existing code. For more information, see the Dart language specification and Effective Dart.

要了解更多关于 Dart 核心库的内容,请参考 Dart 核心库概览

To learn more about Dart’s core libraries, see A Tour of the Dart Libraries.