cv.cm

コーディング · プログラミング言語

Dart入門:初心者向けチュートリアル

約11分で読めます

結論

DartはGoogleが開発した、静的型付けのオブジェクト指向言語で、アプリフレームワークFlutterの言語として知られています。健全なnull安全を備え、ネイティブコードやJavaScript、WebAssemblyにコンパイルでき、開発中は高速なホットリロードが使えます。このページでは、環境構築、型、null安全、制御構文、関数、コレクション、クラス、非同期処理、エラー処理を順に解説します。

Dartとは何か、何に使われるのか

Dartは、Googleが開発したクライアント向けに最適化されたプログラミング言語です。型推論を備えた静的型付けで、健全なnull安全を持ち、ネイティブの機械語(モバイル、デスクトップ、サーバー向け)にも、JavaScriptやWebAssembly(Web向け)にもコンパイルできます。開発中は仮想マシン上でホットリロードが働くため、実行中のアプリに変更が数秒で反映されます。

多くの人がDartを学ぶ目的は、Flutterでアプリを作ることです。FlutterはGoogleのUIツールキットで、1つのコードベースからAndroid、iOS、Web、Windows、macOS、Linux向けに開発できます。Dartはコマンドラインツールやバックエンドサービスにも使われます。

DartのインストールとHello, worldの実行

dart.devからDart SDKをインストールします(Dartが同梱されたFlutterをインストールしても構いません)。Dartのプログラムは、トップレベルの main 関数から始まります。

// hello.dart
void main() {
  print('Hello, world!');
}
dart run hello.dart
# Hello, world!
dart compile exe hello.dart   # builds a native executable
dart create my_app            # creates a new console project

このページのすべての例は、dartpad.devにあるブラウザ上のエディタ「DartPad」でも試せます。

変数と基本の型

型をコンパイラに推論させるには var、一度だけ代入する変数には final、コンパイル時定数には const を使います。型を明示して書くこともできます。

void main() {
  var name = 'Dart';        // inferred as String
  int year = 2011;          // explicit type
  final now = DateTime.now(); // set once, at run time
  const pi = 3.14159;       // compile-time constant
  double ratio = 2;         // int literal becomes 2.0
  bool ok = year > 2000;
  print('$name since $year, pi=${pi.toStringAsFixed(2)}, ok=$ok, ratio=$ratio');
  // Dart since 2011, pi=3.14, ok=true, ratio=2.0
  print(now.year > 2000);   // true
}
型例補足
int42整数。ネイティブ環境では64ビット
double3.1464ビット浮動小数点数
num7 または 7.5int と double のスーパータイプ
booltrue条件式に使えるのは true と false だけ
String'hi'UTF-16。$x や ${expr} による文字列補間が使える
dynamic何でも入る静的チェックを無効にする。ほとんどのコードでは避ける

Webでは int の値がJavaScriptの数値として表現されるため、非常に大きな整数では精度が失われることがあります。

健全なnull安全

Dart 3以降、すべてのDartコードで健全なnull安全が使われます。String 型の変数は決してnullにならず、nullを許可するには String? と書きます。コンパイラがすべての使用箇所を検査するので、nullエラーはプログラムを実行する前に検出されます。

int lengthOf(String? s) {
  // return s.length;         // compile error: s might be null
  if (s == null) return 0;
  return s.length;            // promoted to String after the check
}
void main() {
  String? nickname;
  print(nickname?.length);    // null (null-aware access)
  print(nickname ?? 'guest'); // guest (default if null)
  nickname ??= 'dee';         // assign only if null
  print(lengthOf(nickname));  // 3
  late String config;         // initialized later, checked at run time
  config = 'loaded';
  print(config);              // loaded
}

制御構文

Dartには、おなじみの if、for、while、do-while があり、あらゆるイテラブルを走査する for-in ループも使えます。Dart 3以降は、switch がパターンマッチに対応し、式としても使えます。

void main() {
  var scores = [72, 95, 48];
  for (var s in scores) {
    if (s >= 90) {
      print('$s excellent');
    } else if (s >= 50) {
      print('$s pass');
    } else {
      print('$s fail');
    }
  }
  for (var i = 0; i < 3; i++) {
    print('i = $i');
  }
  var day = 6;
  var kind = switch (day) {
    6 || 7 => 'weekend',
    >= 1 && <= 5 => 'weekday',
    _ => 'invalid',
  };
  print(kind); // weekend
}

関数

関数はファーストクラスのオブジェクトです。引数は位置引数、[] で囲むオプションの位置引数、{} で囲む名前付き引数にできます。名前付き引数は、required を付けない限り省略可能です。アロー構文 => は、1つの式を返す関数の省略記法です。

int add(int a, int b) => a + b;
String greet(String name, {String greeting = 'Hello', required bool loud}) {
  var text = '$greeting, $name';
  return loud ? text.toUpperCase() : text;
}
String label(String text, [int? width]) =>
    width == null ? text : text.padRight(width, '.');
void main() {
  print(add(2, 3));                              // 5
  print(greet('Ana', loud: false));              // Hello, Ana
  print(greet('Ana', greeting: 'Hi', loud: true)); // HI, ANA
  print(label('id', 5));                         // id...
  var twice = (int x) => x * 2;                  // anonymous function
  print([1, 2, 3].map(twice).toList());          // [2, 4, 6]
}

コレクション:List、Set、Map、レコード

基本のコレクションは、List(順序あり)、Set(重複なし)、Map(キーと値のペア)です。コレクションリテラルでは、if、for、スプレッド演算子 ... が使えます。Dart 3で追加されたレコードは、クラスを宣言せずに複数の値をまとめられます。

void main() {
  var nums = [3, 1, 2];               // List<int>
  nums.add(4);
  nums.sort();
  print(nums);                        // [1, 2, 3, 4]
  var tags = {'dart', 'flutter', 'dart'};
  print(tags.length);                 // 2
  var ages = {'Ann': 31, 'Bo': 25};   // Map<String, int>
  ages['Cy'] = 40;
  print(ages['Bo']);                  // 25
  var showAll = false;
  var menu = ['Home', if (showAll) 'Admin', for (var n in nums) 'Item $n'];
  print(menu);  // [Home, Item 1, Item 2, Item 3, Item 4]
  (String, int) pair = ('Ann', 31);   // a record
  var (who, age) = pair;              // destructuring
  print('$who is $age');              // Ann is 31
}

クラスとasync/await

Dartはオブジェクト指向で、すべての値がオブジェクトです。クラスでは、初期化フォーマルパラメータ(this.x)付きのコンストラクタ、名前付きコンストラクタ、ゲッター、ミックスイン、インターフェースが使えます。非同期処理には Future と Stream を async・await とともに使い、ファイルやネットワークの待ち時間でもFlutterアプリの応答性を保てます。

class Point {
  final double x, y;
  const Point(this.x, this.y);
  Point.origin() : x = 0, y = 0;     // named constructor
  double get sum => x + y;           // getter
  @override
  String toString() => 'Point($x, $y)';
}
Future<String> fetchUser() async {
  await Future.delayed(Duration(milliseconds: 100));
  return 'ana';
}
Future<void> main() async {
  print(Point(1, 2).sum);            // 3.0
  print(Point.origin());             // Point(0.0, 0.0)
  var user = await fetchUser();
  print('user: $user');              // user: ana
}

エラー処理

例外は throw で投げ、try、on、catch、finally で処理します。特定の型だけを捕捉するには on SomeType を使います。Dartでは、Exception(プログラムが対処できる想定内の問題)と、Error(アサーションの失敗や範囲エラーなどのバグ)を区別しており、Error は通常、捕捉すべきではありません。

int parseAge(String s) {
  var n = int.tryParse(s);            // null instead of throwing
  if (n == null || n < 0) {
    throw FormatException('Not a valid age: $s');
  }
  return n;
}
void main() {
  for (var input in ['42', 'abc']) {
    try {
      print(parseAge(input));
    } on FormatException catch (e) {
      print('Error: ${e.message}');
    } finally {
      print('checked $input');
    }
  }
}
// 42 / checked 42 / Error: Not a valid age: abc / checked abc

小さな完成プログラム:ショッピングカートの合計

このプログラムは、クラスで商品を表現し、カートをマップに保持してレシートを表示します。クラス、マップ、文字列の書式設定、コレクションのメソッドを組み合わせています。

class Product {
  final String name;
  final double price;
  const Product(this.name, this.price);
}
void main() {
  const apple = Product('Apple', 0.5);
  const bread = Product('Bread', 2.25);
  final cart = <Product, int>{apple: 6, bread: 2};
  var total = 0.0;
  cart.forEach((product, qty) {
    final line = product.price * qty;
    total += line;
    print('${product.name.padRight(6)} x$qty  ${line.toStringAsFixed(2)}');
  });
  final count = cart.values.reduce((a, b) => a + b);
  print('$count items, total ${total.toStringAsFixed(2)}');
}
// Apple  x6  3.00
// Bread  x2  4.50
// 8 items, total 7.50

次のステップ

  • dart.devのDart言語ツアーとチュートリアル。
  • 公式のスタイル・設計ガイド「Effective Dart」。
  • アプリを作りたい場合は、flutter.devのFlutterドキュメント。
  • DartとFlutterのパッケージリポジトリ、pub.dev。

よくある質問

DartはFlutter専用の言語ですか?

いいえ。Dartは汎用言語で、コマンドラインツール、サーバー、Webアプリも作れます。Flutterは、Dartを学ぶ最も人気のある理由というだけです。

Dartはコンパイル言語ですか、インタプリタ言語ですか?

どちらの方式もあります。開発中はVM上でJIT(実行時)コンパイルとホットリロードが働き、リリース時にはAOT(事前)コンパイルでネイティブの機械語に、Web向けにはJavaScriptやWebAssemblyにコンパイルされます。

Dartのfinalとconstの違いは何ですか?

final 変数は一度だけ代入され、その値は実行時に決まっても構いません。const の値はコンパイル時に確定している必要があり、深い不変性を持つため、Flutterは定数ウィジェットを再利用できます。

DartはJavaやJavaScriptに似ていますか?

Cスタイルの構文とクラスは、Java、C#、JavaScriptの開発者には馴染みやすいものです。JavaScriptと違い、Dartは静的型付けで、健全なnull安全を備えています。

Dartの ?? 演算子は何をしますか?

a ?? b は、a がnullでなければ a を、nullなら b を返します。関連する a ??= b は、a がnullの場合にだけ b を a に代入します。

コーディング講座の一覧