目錄

本文會透過範例向你展示 Dart 語言用法的簡單的介紹。

想要深度學習 Dart 語言,請存取左側欄 語言 的具體內容。

想要了解 Dart 的核心庫,存取 Library tour。你也可以檢視 Dart 語言的速查表 CodeLab 來獲得一些快速上手的指導。

Hello World

每個應用都有一個最上層的 main() 函式來作為執行入口。沒有指定返回型別的方法的返回型別會推導為 void。你可以使用最上層函式 print() 來將一段文字輸出顯示到控制檯:

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

你可以閱讀 the main() function 瞭解更多 CLI 的可選引數。

變數

雖然 Dart 是 程式碼型別安全 的語言,你仍然可以用 var 來定義變數,而不用顯式指定它們的型別。由於其支援型別推斷,因此大多數變數的型別會由它們的初始化內容決定:

var name = 'Voyager I';
var year = 1977;
var antennaDiameter = 3.7;
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
var image = {
  'tags': ['saturn'],
  'url': '//path/to/saturn.jpg'
};

你可以 閱讀更多 Dart 中關於變數的內容,包括變數的預設值,finalconst 關鍵字以及靜態型別等。

流程控制陳述式

Dart 支援常用的流程控制陳述式:

if (year >= 2001) {
  print('21st century');
} else if (year >= 1901) {
  print('20th century');
}

for (final object in flybyObjects) {
  print(object);
}

for (int month = 1; month <= 12; month++) {
  print(month);
}

while (year < 2016) {
  year += 1;
}

你可以閱讀更多 Dart 中關於控制流程陳述式的內容,包括 breakcontinue 關鍵字、 switch 陳述式和 case 子句 以及 assert 陳述式。

函式

我們建議 為每個函式的引數以及返回值都指定型別:

int fibonacci(int n) {
  if (n == 0 || n == 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

var result = fibonacci(20);

=> (胖箭頭) 簡寫語法用於僅包含一條陳述式的函式。該語法在將匿名函式作為引數傳遞時非常有用:

flybyObjects.where((name) => name.contains('turn')).forEach(print);

上面的範例除了向你展示了匿名函式(上例中傳入 where() 函式的引數即是一個匿名函式)外,還向你展示了將函式作為引數使用的方式:上面範例將最上層函式 print() 作為引數傳給了 forEach() 函式。

你可以 閱讀更多 Dart 中有關函式的內容,包括可選引數、預設引數值以及詞法作用域。

註釋

Dart 通常使用雙斜槓 // 作為註釋的開始。

// This is a normal, one-line comment.

/// This is a documentation comment, used to document libraries,
/// classes, and their members. Tools like IDEs and dartdoc treat
/// doc comments specially.

/* Comments like these are also supported. */

你可以 閱讀更多 Dart 中有關注釋的內容,包括文件工具的工作原理。

匯入 (Import)

使用 import 關鍵字來存取在其它庫中定義的 API。

// Importing core libraries
import 'dart:math';

// Importing libraries from external packages
import 'package:test/test.dart';

// Importing files
import 'path/to/my_other_file.dart';

你可以 閱讀更多 Dart 中有關庫和可見性的內容,包括庫字首、showhide 關鍵字以及透過 deferred 關鍵字實現的延遲載入。

類別(Class)

下面的範例中向你展示了一個包含三個屬性、兩個建構函式以及一個方法的類別。其中一個屬性不能直接賦值,因此它被定義為一個 getter 方法(而不是變數)。該方法使用字串插值來列印字串文字內變數的字串。

class Spacecraft {
  String name;
  DateTime? launchDate;

  // Read-only non-final property
  int? get launchYear => launchDate?.year;

  // Constructor, with syntactic sugar for assignment to members.
  Spacecraft(this.name, this.launchDate) {
    // Initialization code goes here.
  }

  // Named constructor that forwards to the default one.
  Spacecraft.unlaunched(String name) : this(name, null);

  // Method.
  void describe() {
    print('Spacecraft: $name');
    // Type promotion doesn't work on getters.
    var launchDate = this.launchDate;
    if (launchDate != null) {
      int years = DateTime.now().difference(launchDate).inDays ~/ 365;
      print('Launched: $launchYear ($years years ago)');
    } else {
      print('Unlaunched');
    }
  }
}

你可以 閱讀更多 關於字串的內容,包括字串插值、表示式以及 toString() 方法。

你可以像下面這樣使用 Spacecraft 類:

var voyager = Spacecraft('Voyager I', DateTime(1977, 9, 5));
voyager.describe();

var voyager3 = Spacecraft.unlaunched('Voyager III');
voyager3.describe();

你可以 閱讀更多 Dart 中有關類別的內容,包括初始化列表、可選的 newconst 關鍵字、重新導向建構函式、由 factory 關鍵字定義的工廠建構函式以及 Getter 和 Setter 方法等等。

列舉型別 (Enum)

列舉型別的取值範圍是一組預定義的值或例項。

下面這個簡單的列舉範例定義了一組行星類別:

enum PlanetType { terrestrial, gas, ice }

下面是一個增強型列舉的範例,定義了一組行星類別的常量例項,即太陽系的行星:

/// Enum that enumerates the different planets in our solar system
/// and some of their properties.
enum Planet {
  mercury(planetType: PlanetType.terrestrial, moons: 0, hasRings: false),
  venus(planetType: PlanetType.terrestrial, moons: 0, hasRings: false),
  // ···
  uranus(planetType: PlanetType.ice, moons: 27, hasRings: true),
  neptune(planetType: PlanetType.ice, moons: 14, hasRings: true);

  /// A constant generating constructor
  const Planet(
      {required this.planetType, required this.moons, required this.hasRings});

  /// All instance variables are final
  final PlanetType planetType;
  final int moons;
  final bool hasRings;

  /// Enhanced enums support getters and other methods
  bool get isGiant =>
      planetType == PlanetType.gas || planetType == PlanetType.ice;
}

你可以這樣使用 Planet 列舉:

final yourPlanet = Planet.earth;

if (!yourPlanet.isGiant) {
  print('Your planet is not a "giant planet".');
}

你可以 閱讀更多 Dart 中有關列舉的內容,包括增強型列舉的限制條件、列舉預設包含的屬性、如何獲取列舉值的名稱以及在 switch 陳述式中使用列舉等等。

類別繼承

Dart 支援單繼承。

class Orbiter extends Spacecraft {
  double altitude;

  Orbiter(super.name, DateTime super.launchDate, this.altitude);
}

你可以 閱讀更多 Dart 中有關類繼承的內容,比如可選的 @override 註解等等。

Mixins

Mixin 是一種在多個類層次結構中重用程式碼的方法。下面的是宣告一個 Mixin 的做法:

mixin Piloted {
  int astronauts = 1;

  void describeCrew() {
    print('Number of astronauts: $astronauts');
  }
}

現在你只需使用 Mixin 的方式繼承這個類就可將該類中的功能新增給其它類別。

class PilotedCraft extends Spacecraft with Piloted {
  // ···
}

自此,PilotedCraft 類中就包含了 astronauts 欄位以及 describeCrew() 方法。

你可以 閱讀更多 關於 Mixin 的內容。

介面和抽象類別

所有的類都隱含定義成了一個介面。因此,任意類別都可以作為介面被實現。

class MockSpaceship implements Spacecraft {
  // ···
}

你可以閱讀更多關於 隱含介面 或者 interface 關鍵詞 的內容。

你可以建立一個被任意具體類擴充(或實現)的抽象類別。抽象類別可以包含抽象方法(不含方法體的方法)。

abstract class Describable {
  void describe();

  void describeWithEmphasis() {
    print('=========');
    describe();
    print('=========');
  }
}

任意一個擴充了 Describable 的類別都擁有 describeWithEmphasis() 方法,這個方法又會去呼叫實現類別中實現的 describe() 方法。

你可以 閱讀更多 關於抽象類別和抽象方法的內容。

非同步

使用 asyncawait 關鍵字可以讓你避免回呼(Callback)地獄 (Callback Hell) 並使你的程式碼更具可讀性。

const oneSecond = Duration(seconds: 1);
// ···
Future<void> printWithDelay(String message) async {
  await Future.delayed(oneSecond);
  print(message);
}

上面的方法相當於:

Future<void> printWithDelay(String message) {
  return Future.delayed(oneSecond).then((_) {
    print(message);
  });
}

如下一個範例所示,asyncawait 關鍵字有助於使非同步程式碼變得易於閱讀。

Future<void> createDescriptions(Iterable<String> objects) async {
  for (final object in objects) {
    try {
      var file = File('$object.txt');
      if (await file.exists()) {
        var modified = await file.lastModified();
        print(
            'File for $object already exists. It was modified on $modified.');
        continue;
      }
      await file.create();
      await file.writeAsString('Start describing $object in this file.');
    } on IOException catch (e) {
      print('Cannot create description for $object: $e');
    }
  }
}

你也可以使用 async* 關鍵字,其可以為你提供一個可讀性更好的方式去產生 Stream。

Stream<String> report(Spacecraft craft, Iterable<String> objects) async* {
  for (final object in objects) {
    await Future.delayed(oneSecond);
    yield '${craft.name} flies by $object';
  }
}

你可以 閱讀更多 關於非同步支援的內容,包括非同步函式、FutureStream 以及非同步迴圈(await for)。

例外

使用 throw 關鍵字丟擲一個例外:

if (astronauts == 0) {
  throw StateError('No astronauts.');
}

使用 try 陳述式配合 oncatch(兩者也可同時使用)關鍵字來捕獲一個例外:

Future<void> describeFlybyObjects(List<String> flybyObjects) async {
  try {
    for (final object in flybyObjects) {
      var description = await File('$object.txt').readAsString();
      print(description);
    }
  } on IOException catch (e) {
    print('Could not describe object: $e');
  } finally {
    flybyObjects.clear();
  }
}

注意上述程式碼是非同步的;同步程式碼以及非同步函式中得程式碼都可以使用 try 捕獲例外。

你可以 閱讀更多 關於例外的內容,包括堆疊追蹤、rethrow 關鍵字以及 Error 和 Exception 之間的區別。

Important concepts

As you continue to 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.

  • Although Dart is strongly typed, type annotations are optional because Dart can infer types. In var number = 101, number is inferred to be of type int.

  • 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!

  • 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 supports generic types, like List<int> (a list of integers) or List<Object> (a list of objects of any type).

  • 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).

  • 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.

  • 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 imports.

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

  • 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 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 API 文件,裡面也常常會有範例程式碼。