目的別のDart構文リファレンス
1. 変数とデータ型 🔢
プログラムで扱うデータを格納するための基本的な要素です。
1.1. 変数宣言
キーワード | 説明 | コード例 |
---|---|---|
var | 型推論を用いて変数を宣言。再代入可能。 |
|
final | 一度だけ代入可能な変数を宣言。実行時に値が決定される。型推論も可能。 |
|
const | コンパイル時定数を宣言。コンパイル時に値が決定される必要がある。暗黙的にfinal 。 |
|
[型名] | 明示的に型を指定して変数を宣言。再代入可能。 |
|
1.2. 基本的なデータ型
型 | 説明 | コード例 |
---|---|---|
int | 整数値を表す型。プラットフォームによってサイズが異なる。Webでは64bit浮動小数点数として扱われる場合がある。 |
|
double | 浮動小数点数を表す型。IEEE 754 標準の64bit倍精度。 |
|
num | int とdouble のスーパータイプ。両方の型を受け入れ可能。 |
|
bool | 真偽値を表す型。true またはfalse のみ。 |
|
String | 文字列を表す型。UTF-16コードユニットのシーケンス。シングルクォート(' )またはダブルクォート(" )で囲む。 |
|
1.3. Null Safety 🛡️
Dart 2.12から導入された、Null参照によるエラーをコンパイル時に防ぐ仕組み。
記号/キーワード | 説明 | コード例 |
---|---|---|
? (Nullable型) | 型名の後に付けることで、その変数がnull を許容することを示す。 |
|
! (Nullチェック演算子) | Nullable型の変数がnull でないことをコンパイラに表明する。もしnull だった場合は実行時エラー。 |
|
late | 変数の初期化を遅延させることを示す。宣言時には初期化せず、最初のアクセスまでに初期化されることを保証。final と併用可能。 |
|
required | 名前付きパラメータが必須であることを示す。Null Safety以前の@required アノテーションに代わるもの。 |
|
1.4. コレクション 📚
複数の値をまとめて扱うための型。
List (リスト)
順序付けられた要素のコレクション。インデックスでアクセス可能。デフォルトは可変長。
操作 | コード例 |
---|---|
リテラルでの作成 |
|
要素へのアクセス |
|
要素の追加・削除 |
|
イテレーション |
|
スプレッド演算子 |
|
Collection if / for |
|
Set (セット)
順序のない、一意な要素のコレクション。
操作 | コード例 |
---|---|
リテラルでの作成 |
|
要素の確認・追加・削除 |
|
集合演算 |
|
Map (マップ)
キーと値のペアのコレクション。キーは一意である必要がある。
操作 | コード例 |
---|---|
リテラルでの作成 |
|
要素へのアクセス |
|
要素の追加・更新・削除 |
|
イテレーション |
|
1.5. その他の型
型 | 説明 | コード例 |
---|---|---|
dynamic | 動的な型。型チェックが実行時に行われる。型安全性が低下するため、使用は慎重に。 |
|
Object | Dartの全てのクラスのルートクラス(Null を除く、Null Safety有効下)。dynamic と違い、メソッド呼び出しには型キャストが必要な場合がある。 |
|
Runes | 文字列のUTF-32コードポイント(Unicode文字)を表現する。絵文字や特殊文字の扱いに。 |
|
Symbol | Dartプログラムで宣言された演算子や識別子を表すオブジェクト。リフレクションやメタプログラミングで使用されることがあるが、一般的ではない。 |
|
2. 制御フロー 🔀
プログラムの実行順序を制御するための構文。
構文 | 説明 | コード例 |
---|---|---|
if / else if / else | 条件に基づいて処理を分岐させる。 |
|
for ループ | 指定された回数、または条件が満たされる間、処理を繰り返す。 |
|
while ループ | 条件が真である間、処理を繰り返す。最初に条件を評価。 |
|
do-while ループ | 処理を一度実行してから、条件が真である間、処理を繰り返す。最後に条件を評価。 |
|
switch / case | 特定の値に基づいて処理を分岐させる。int , String , コンパイル時定数で利用可能。Dart 3.0以降はより強力なパターンマッチングが可能。 |
|
break | 現在のループ(for , while , do-while )またはswitch 文を終了させる。 |
|
continue | 現在のループの残りの処理をスキップし、次のイテレーションに進む。 |
|
assert | 開発中に条件が真であることを表明する。Flutterのデバッグモードやdart --enable-asserts で実行した場合のみ有効。本番ビルドでは無視される。 |
|
3. 関数 ⚙️
特定のタスクを実行するコードのまとまり。
3.1. 関数の定義と呼び出し
形式 | 説明 | コード例 |
---|---|---|
基本形 | 戻り値の型、関数名、パラメータリスト、関数本体で構成される。戻り値がない場合はvoid 。 |
|
アロー構文 (=> ) | 関数本体が一つの式のみの場合に使える短縮記法。{ return 式; } と同等。 |
|
戻り値の型省略 | 戻り値の型を省略するとdynamic として扱われるが、明示することが推奨される。 |
|
3.2. パラメータ
関数に渡す値。
種類 | 説明 | コード例 |
---|---|---|
必須位置パラメータ | 最も基本的なパラメータ。定義された順序で値を渡す必要があり、省略不可。 |
|
名前付きパラメータ | { } で囲み、呼び出し時にパラメータ名を指定する。順序は任意。デフォルトでオプショナル(Null Safety下では? かrequired かデフォルト値が必要)。 |
|
オプション位置パラメータ | [ ] で囲み、呼び出し時に省略可能。定義順に値を渡す。デフォルト値も設定可能。 |
|
デフォルトパラメータ値 | 名前付きパラメータとオプション位置パラメータには= を使ってデフォルト値を設定できる。デフォルト値はコンパイル時定数である必要がある。 |
|
3.3. 匿名関数 (クロージャ / ラムダ)
名前を持たない関数。変数に代入したり、他の関数に引数として渡したりできる。
void main() {
// 変数に関数を代入
var loudify = (String msg) => '!!! ${msg.toUpperCase()} !!!';
print(loudify('hello')); // !!! HELLO !!!
// リストの各要素に適用
var numbers = [1, 2, 3];
numbers.forEach((number) {
print('Number: $number');
});
// 引数として渡す
List<int> performOperation(List<int> list, int Function(int) action) {
return list.map(action).toList();
}
var doubled = performOperation(numbers, (x) => x * 2);
print(doubled); // [2, 4, 6]
}
3.4. 高階関数
関数を引数として受け取るか、関数を戻り値として返す関数。
// 関数を引数として受け取る (例: forEach, map, where)
var numbers = [1, 2, 3, 4, 5];
numbers.where((n) => n % 2 == 0) // 偶数のみフィルタリング (関数を渡す)
.forEach(print); // 各要素を出力 (関数を渡す) -> 2, 4
// 関数を返す関数
Function makeAdder(int addBy) {
return (int i) => addBy + i;
}
void main() {
var add2 = makeAdder(2); // addBy=2 の関数を生成
var add4 = makeAdder(4); // addBy=4 の関数を生成
print(add2(3)); // 5 (2 + 3)
print(add4(3)); // 7 (4 + 3)
}
3.5. ジェネレータ関数 🏭
一連の値を遅延して生成する関数。sync*
(同期) または async*
(非同期) と yield
を使う。
種類 | 説明 | コード例 |
---|---|---|
sync* / yield | 同期的に値のシーケンス (Iterable ) を生成する。yield で値を一つずつ返す。 |
|
async* / yield | 非同期的に値のシーケンス (Stream ) を生成する。イベントやデータの流れを表現するのに使う。 |
|
4. 演算子 ➕➖✖️➗
値に対して操作を行うための記号。
種類 | 演算子 | 説明・コード例 |
---|---|---|
算術演算子 | + | 加算: 5 + 2 // 7 |
- | 減算: 5 - 2 // 3 | |
-expr | 単項マイナス: -5 | |
* | 乗算: 5 * 2 // 10 | |
/ | 除算 (常にdouble を返す): 5 / 2 // 2.5 | |
~/ | 整数除算 (結果を整数にする): 5 ~/ 2 // 2 | |
% | 剰余 (余り): 5 % 2 // 1 | |
比較演算子 | == | 等価: 5 == 5 // true |
!= | 非等価: 5 != 2 // true | |
> | より大きい: 5 > 2 // true | |
< | より小さい: 5 < 2 // false | |
>= | 以上: 5 >= 5 // true | |
<= | 以下: 5 <= 2 // false | |
論理演算子 | !expr | 論理否定 (NOT): !true // false |
|| | 論理和 (OR): true || false // true | |
&& | 論理積 (AND): true && false // false | |
代入演算子 | = | 代入: int a = 5; |
+= | 加算代入: a += 2; // a = a + 2 | |
-= | 減算代入: a -= 1; // a = a - 1 | |
*= | 乗算代入: a *= 3; // a = a * 3 | |
/= | 除算代入: double b = 6; b /= 2; // b = b / 2 (bは3.0) | |
~/= | 整数除算代入: a ~/= 2; // a = a ~/ 2 | |
%= | 剰余代入: a %= 3; // a = a % 3 | |
Null関連演算子 | ?? (Null合体演算子) | 左辺がnull の場合に右辺の値を返す: String? name; String displayName = name ?? 'Guest'; // displayName は 'Guest' |
??= (Null合体代入演算子) | 左辺がnull の場合のみ右辺の値を代入: int? score; score ??= 0; // score は 0 になる | |
?. (Null Awareアクセス) | 左辺がnull でない場合にのみ右辺のプロパティやメソッドにアクセス。null の場合はnull を返す: String? message; int? len = message?.length; // len は null | |
型テスト演算子 | is | オブジェクトが指定した型であるかチェック: if (obj is String) { ... } |
is! | オブジェクトが指定した型でないかチェック: if (obj is! int) { ... } | |
ビット演算子 | & | ビットAND: 5 & 1 // 1 (0101 & 0001 = 0001) |
| | ビットOR: 5 | 1 // 5 (0101 | 0001 = 0101) | |
^ | ビットXOR: 5 ^ 1 // 4 (0101 ^ 0001 = 0100) | |
~expr | ビットNOT (ビット反転): ~5 // -6 (プラットフォーム依存) | |
<< | 左シフト: 5 << 1 // 10 (0101 << 1 = 1010) | |
>> | 右シフト: 5 >> 1 // 2 (0101 >> 1 = 0010) | |
>>> | 符号なし右シフト: Dart 3.0以降 | |
カスケード記法 | .. | 同一オブジェクトに対して連続してメソッド呼び出しやプロパティ設定を行う:
|
スプレッド演算子 | ... | コレクション(List, Set, Map)の要素を展開して別のコレクションに挿入: var list1 = [1, 2]; var list2 = [0, ...list1, 3]; // [0, 1, 2, 3] |
...? (Null Aware) | 元のコレクションがnull の場合、展開をスキップ: List<int>? list3; var list4 = [0, ...?list3, 4]; // [0, 4] | |
条件式 (三項演算子) | condition ? expr1 : expr2 | condition がtrue ならexpr1 、false ならexpr2 を評価: int points = 10; String rank = points > 5 ? 'High' : 'Low'; // rank は 'High' |
5. クラスとオブジェクト 🏛️
オブジェクト指向プログラミングの基本。データと振る舞いをまとめた設計図(クラス)と、その実体(オブジェクト)。
5.1. クラス定義とインスタンス化
class Point {
// インスタンス変数 (フィールド)
double x = 0; // 初期値を設定
double y = 0;
// コンストラクタ (クラス名と同じ名前のメソッド)
Point(double initialX, double initialY) {
x = initialX;
y = initialY;
}
// メソッド (振る舞い)
double distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy); // sqrt は dart:math から import 必要
}
}
void main() {
// インスタンス化 (オブジェクトの生成)
var p1 = Point(3, 4);
var p2 = Point(0, 0);
// プロパティへのアクセス
print('p1.x: ${p1.x}'); // 3.0
// メソッドの呼び出し
double dist = p1.distanceTo(p2);
print('Distance: $dist'); // 5.0
}
5.2. コンストラクタ
オブジェクト生成時に初期化を行うための特別なメソッド。
種類 | 説明 | コード例 |
---|---|---|
デフォルトコンストラクタ | クラス名と同じ名前。引数を受け取り、フィールドを初期化。糖衣構文 (this.fieldName ) が使える。 |
|
名前付きコンストラクタ | ClassName.identifierName() の形式。複数のコンストラクタを定義したい場合や、特定の目的の初期化を行いたい場合に使う。 |
|
ファクトリコンストラクタ | factory キーワードを使用。必ずしも新しいインスタンスを生成するとは限らない。キャッシュされたインスタンスを返したり、サブクラスのインスタンスを返したりできる。 |
|
定数コンストラクタ | const キーワードを使用。全てのインスタンス変数がfinal である必要があり、コンパイル時定数としてオブジェクトを生成できる。パフォーマンス向上に繋がる。 |
|
リダイレクトコンストラクタ | コンストラクタ本体を持たず、: this(...) または : super(...) のように、同じクラスの別のコンストラクタやスーパークラスのコンストラクタに処理を委譲する。 |
|
初期化子リスト | コンストラクタの: の後に記述し、super() の呼び出しやfinal 変数の初期化を行う。コンストラクタ本体が実行される前に実行される。 |
|
5.3. ゲッターとセッター
インスタンス変数へのアクセスを制御するための特別なメソッド。get
とset
キーワードを使う。
class Temperature {
double celsius;
Temperature(this.celsius);
// ゲッター: ファーレンハイト度を計算して返す
double get fahrenheit => celsius * 1.8 + 32;
// セッター: ファーレンハイト度からセルシウス度を設定
set fahrenheit(double value) => celsius = (value - 32) / 1.8;
}
void main() {
var temp = Temperature(25); // セルシウス度で初期化
// ゲッター呼び出し (メソッド呼び出しのように () は不要)
print('${temp.celsius}°C is ${temp.fahrenheit}°F'); // 25.0°C is 77.0°F
// セッター呼び出し (代入のように使う)
temp.fahrenheit = 86;
print('Now it is ${temp.celsius}°C'); // Now it is 30.0°C
}
5.4. 継承 (extends)
既存のクラス(スーパークラス)のプロパティとメソッドを引き継いで新しいクラス(サブクラス)を作成する仕組み。コードの再利用性を高める。Dartは単一継承。
// スーパークラス
class Vehicle {
String model;
int year;
Vehicle(this.model, this.year);
void honk() {
print('Beep beep!');
}
}
// サブクラス (Vehicleを継承)
class Car extends Vehicle {
int numberOfDoors;
// スーパークラスのコンストラクタを呼び出す (superを使用)
Car(String model, int year, this.numberOfDoors) : super(model, year);
// メソッドのオーバーライド (@overrideアノテーション推奨)
@override
void honk() {
print('Honk honk!'); // 車固有の音に変更
}
void drive() {
print('$model is driving...');
}
}
void main() {
var myCar = Car('Tesla Model 3', 2023, 4);
print(myCar.model); // Tesla Model 3 (継承したプロパティ)
myCar.honk(); // Honk honk! (オーバーライドしたメソッド)
myCar.drive(); // Tesla Model 3 is driving... (Car固有のメソッド)
}
5.5. 抽象クラス (abstract)
インスタンス化できないクラス。サブクラスで実装されるべきメソッド(抽象メソッド)のシグネチャを定義するためなどに使う。インターフェースとしても機能する。
abstract class Shape {
// 抽象メソッド (本体を持たない)
double get area;
// 通常のメソッドも持てる
void printDescription() {
print('This is a shape.');
}
}
class Square extends Shape {
double side;
Square(this.side);
// 抽象メソッドの実装が必須
@override
double get area => side * side;
@override
void printDescription() {
print('This is a square with side $side.');
}
}
void main() {
// var shape = Shape(); // エラー: 抽象クラスはインスタンス化できない
var sq = Square(5);
print(sq.area); // 25.0
sq.printDescription(); // This is a square with side 5.0
}
5.6. インターフェース (implements)
クラスが特定のメソッドやゲッター/セッターを持つことを保証する規約。Dartでは、全てのクラスが暗黙的にインターフェースを定義する。implements
キーワードでクラスがインターフェースを実装することを示す。実装するクラスは、インターフェースの全てのメンバー(メソッド、ゲッター、セッター)を実装(オーバーライド)する必要がある。
class Speaker {
void announce(String message) {
print('Announcing: $message');
}
}
class Amplifier {
void amplify(int level) {
print('Amplifying to $level');
}
}
// SpeakerとAmplifierの両方のインターフェースを実装するクラス
class SmartSpeaker implements Speaker, Amplifier {
@override
void announce(String message) {
print('SmartSpeaker says: $message');
}
@override
void amplify(int level) {
print('SmartSpeaker volume set to $level');
}
}
void main() {
var smart = SmartSpeaker();
smart.announce('Hello'); // SmartSpeaker says: Hello
smart.amplify(8); // SmartSpeaker volume set to 8
// インターフェース型として扱うことも可能
Speaker s = smart;
s.announce('World'); // SmartSpeaker says: World
}
5.7. ミックスイン (with)
クラス間でコード(メソッドやフィールド)を再利用するための仕組み。継承と異なり、複数のミックスインを一つのクラスに取り込むことができる。mixin
キーワードでミックスインを定義し、with
キーワードでクラスに適用する。ミックスインはコンストラクタを持てない。特定のスーパータイプを要求することもできる (on
キーワード)。
// ミックスイン定義
mixin Flyer {
void fly() {
print("I'm flying!");
}
}
mixin Walker {
void walk() {
print("I'm walking!");
}
}
// 特定のスーパータイプを要求するミックスイン
mixin Swimmer on Animal { // Animalクラスかそのサブクラスにしか適用できない
void swim() {
print("$name is swimming!"); // Animalクラスのnameプロパティにアクセス
}
}
class Animal {
String name;
Animal(this.name);
}
// ミックスインを適用したクラス
class Bird extends Animal with Flyer, Walker {
Bird(String name) : super(name);
}
class Duck extends Animal with Flyer, Walker, Swimmer {
Duck(String name) : super(name);
}
// class Fish with Swimmer {} // エラー: SwimmerはAnimalを継承していないクラスには使えない
void main() {
var bird = Bird('Sparrow');
bird.fly(); // I'm flying!
bird.walk(); // I'm walking!
var duck = Duck('Donald');
duck.fly(); // I'm flying!
duck.walk(); // I'm walking!
duck.swim(); // Donald is swimming!
}
5.8. 拡張メソッド (extension)
既存のライブラリやクラス(自分で定義したものも含む)を変更せずに、新しいメソッドやゲッター/セッターを追加する機能。
// Stringクラスに新しいメソッドを追加する拡張
extension StringParsing on String {
int? parseIntOrNull() {
return int.tryParse(this);
}
String capitalize() {
if (isEmpty) return this;
return '${this[0].toUpperCase()}${substring(1)}';
}
}
// Listクラスにゲッターを追加する拡張
extension ListUtils<T> on List<T> {
T? get secondOrNull {
return length >= 2 ? this[1] : null;
}
}
void main() {
String numberStr = '123';
String word = 'hello';
String invalid = 'abc';
print(numberStr.parseIntOrNull()); // 123
print(invalid.parseIntOrNull()); // null
print(word.capitalize()); // Hello
var numbers = [10, 20, 30];
var names = ['Alice'];
print(numbers.secondOrNull); // 20
print(names.secondOrNull); // null
}
5.9. 静的メンバー (static)
クラス自体に関連付けられたメンバー(変数やメソッド)。インスタンス化せずにクラス名から直接アクセスできる。
class AppConfig {
// 静的変数 (クラス変数)
static const String appName = 'My Cool App';
static bool loggingEnabled = true;
// 静的メソッド (クラスメソッド)
static void printAppName() {
print('App Name: $appName');
}
static double calculateVat(double price) {
return price * 0.1; // 例: 10%のVAT
}
}
void main() {
// インスタンス化せずにアクセス
print(AppConfig.appName); // My Cool App
AppConfig.printAppName(); // App Name: My Cool App
AppConfig.loggingEnabled = false; // 静的変数の変更
print(AppConfig.loggingEnabled); // false
double price = 100.0;
double vat = AppConfig.calculateVat(price);
print('VAT: $vat'); // VAT: 10.0
}
5.10. Enum (列挙型)
固定された数の定数値を表現するための特殊なクラス。enum
キーワードで定義する。Dart 2.17以降、メソッドやフィールドを持つことも可能になった(Enhanced Enums)。
// 基本的な列挙型
enum Status {
pending,
running,
completed,
failed
}
// Enhanced Enum (メソッドとフィールドを持つ)
enum Color {
red(0xFFF44336, '赤'),
green(0xFF4CAF50, '緑'),
blue(0xFF2196F3, '青'); // セミコロンが必要
final int hexCode;
final String japaneseName;
// 定数コンストラクタ
const Color(this.hexCode, this.japaneseName);
// メソッド
String get hexString => '#${hexCode.toRadixString(16).substring(2).toUpperCase()}';
@override
String toString() => 'Color($japaneseName)';
}
void main() {
Status currentStatus = Status.running;
// switch文での利用
switch (currentStatus) {
case Status.pending:
print('Waiting to start...');
break;
case Status.running:
print('In progress...'); // これが実行される
break;
case Status.completed:
print('Finished successfully.');
break;
case Status.failed:
print('An error occurred.');
break;
// default節は必須ではない (全てのenum値を網羅している場合)
}
// 列挙値のインデックスと名前
print(currentStatus.index); // 1 (定義順のインデックス)
print(currentStatus.name); // 'running' (Dart 2.15以降)
// Enhanced Enumの使用
Color favColor = Color.blue;
print(favColor.japaneseName); // 青
print(favColor.hexCode); // 4280391411 (0xFF2196F3)
print(favColor.hexString); // #2196F3
print(favColor); // Color(青)
// values: 全ての列挙値をリストで取得
print(Color.values); // [Color.red, Color.green, Color.blue]
}
6. 非同期処理 ⏳
時間のかかる処理(ネットワーク通信、ファイルI/Oなど)をメインの処理(UI更新など)をブロックせずに行うための仕組み。
6.1. Future
非同期操作の最終的な結果(値またはエラー)を表すオブジェクト。操作が完了すると、Future
は値またはエラーで「完了」する。
import 'dart:async';
// 時間のかかる処理を模倣する関数 (Futureを返す)
Future<String> fetchData() {
// 3秒後に 'Data fetched successfully!' という文字列を返すFutureを生成
return Future.delayed(Duration(seconds: 3), () {
// ここでエラーが発生する可能性もある
// throw Exception('Failed to fetch data');
return 'Data fetched successfully!';
});
}
void main() {
print('Fetching data...');
final futureResult = fetchData();
// Futureが完了したときの処理 (then)
futureResult.then((result) {
// 成功した場合の処理
print('Success: $result'); // 3秒後に出力される
}).catchError((error) {
// エラーが発生した場合の処理
print('Error: $error');
}).whenComplete(() {
// 成功・失敗に関わらず、完了時に必ず実行される処理
print('Future completed.');
});
print('Doing other work...'); // fetchDataの完了を待たずに実行される
}
6.2. async / await
Future
をより同期的なコードのように書けるようにする糖衣構文。
async
: 関数が非同期であり、内部でawait
を使う可能性があることを示す。この関数は暗黙的にFuture
を返す。await
:Future
が完了するのを待つ。await
はasync
関数内でのみ使用可能。
import 'dart:async';
Future<String> fetchUserData() {
return Future.delayed(Duration(seconds: 2), () => 'User Alice');
}
Future<String> fetchUserPosts(String user) {
return Future.delayed(Duration(seconds: 1), () => 'Posts for $user');
}
// async関数: 内部でawaitを使用
Future<void> displayUserDataAndPosts() async {
print('Start fetching...');
try {
// await: fetchUserDataが完了するまで待つ
String user = await fetchUserData();
print('Got user: $user');
// await: fetchUserPostsが完了するまで待つ
String posts = await fetchUserPosts(user);
print('Got posts: $posts');
} catch (e) {
print('An error occurred: $e');
} finally {
print('Fetching finished.');
}
}
void main() {
print('Main function start.');
displayUserDataAndPosts(); // 非同期関数を呼び出す
print('Main function end.'); // displayUserDataAndPostsの完了を待たずに実行される
}
/* 出力順序の例:
Main function start.
Start fetching...
Main function end.
(2秒後) Got user: User Alice
(さらに1秒後) Got posts: Posts for User Alice
Fetching finished.
*/
6.3. Stream
非同期的なイベント(データのシーケンス)を扱うためのオブジェクト。Future
が一つの結果を返すのに対し、Stream
は複数の値やエラーを時間経過とともに生成できる。ユーザー入力、ファイルの読み込み、ネットワークからのデータ受信などに使われる。
import 'dart:async';
// 1秒ごとに数値を生成するStreamを作成する関数
Stream<int> countStream(int max) async* { // async* でStreamを返すジェネレータ関数
for (int i = 1; i <= max; i++) {
await Future.delayed(Duration(seconds: 1));
yield i; // Streamに値を送る
if (i == 3) {
// エラーを送ることもできる
// yield* Stream.error(Exception('Something went wrong at 3'));
}
}
}
void main() async {
print('Starting stream...');
Stream<int> stream = countStream(5);
// Streamを購読 (listen) してデータを受け取る
StreamSubscription subscription = stream.listen(
(data) {
// データ受信時の処理
print('Received: $data');
},
onError: (error) {
// エラー発生時の処理
print('Error: $error');
},
onDone: () {
// Streamが完了 (閉じた) ときの処理
print('Stream is done.');
},
cancelOnError: false // trueにするとエラー発生時に自動で購読解除
);
// 5秒後に購読をキャンセルする場合
// await Future.delayed(Duration(seconds: 5));
// print('Cancelling subscription...');
// await subscription.cancel();
// await for ループを使ってStreamを処理することも可能 (async関数内)
print('Using await for...');
try {
await for (var value in countStream(3)) {
print('Await for received: $value');
}
print('Await for finished.');
} catch (e) {
print('Await for error: $e');
}
print('Main function continues...');
}
/* 出力例:
Starting stream...
Using await for...
Main function continues...
(1秒後) Received: 1
(1秒後) Await for received: 1
(1秒後) Received: 2
(1秒後) Await for received: 2
(1秒後) Received: 3
(1秒後) Await for received: 3
Await for finished.
(1秒後) Received: 4
(1秒後) Received: 5
Stream is done.
*/
Streamの種類:
- Single-subscription streams: 一度に一つのリスナーしか持つことができない。データシーケンス全体が順番に配信されることを想定(例: ファイル読み込み)。
- Broadcast streams: 複数のリスナーを持つことができる。いつでも購読開始でき、購読開始後に発生したイベントを受け取る(例: マウスイベント)。
stream.asBroadcastStream()
で変換可能。
6.4. Isolate
Dartで並行処理(本当の意味での並列処理)を行うための仕組み。各Isolateは自身のメモリ空間とイベントループを持つ独立した実行単位。重い計算処理をバックグラウンドで行い、メインIsolate(UIを担当)をブロックさせないために使う。
Isolate間の通信はメッセージパッシング(SendPort
と ReceivePort
)で行う。
import 'dart:isolate';
import 'dart:async';
// 新しいIsolateで実行される関数 (トップレベル関数またはstaticメソッドである必要あり)
void heavyComputation(SendPort sendPort) {
print('[Isolate] Starting computation...');
int result = 0;
for (int i = 0; i < 1000000000; i++) { // 重い計算の例
result += 1;
}
print('[Isolate] Computation finished.');
// 計算結果をメインIsolateに送信
sendPort.send(result);
}
Future<void> runComputationInIsolate() async {
print('[Main] Creating ReceivePort...');
// メインIsolateでメッセージを受け取るためのポート
final receivePort = ReceivePort();
print('[Main] Spawning Isolate...');
// Isolateを生成し、実行する関数と通信用のSendPortを渡す
final isolate = await Isolate.spawn(heavyComputation, receivePort.sendPort);
print('[Main] Waiting for result from Isolate...');
// Isolateからのメッセージを待つ (Streamとして扱える)
final result = await receivePort.first;
print('[Main] Received result: $result');
print('[Main] Closing ReceivePort and killing Isolate...');
receivePort.close(); // ポートを閉じる
isolate.kill(); // Isolateを終了
print('[Main] Isolate finished.');
}
void main() {
print('[Main] App started.');
runComputationInIsolate();
// Isolateが動いている間もメインスレッドは他の処理を行える
print('[Main] Doing other work while Isolate runs...');
}
より簡単にIsolateを利用するために、Isolate.run()
(Dart 2.19以降) や compute
関数 (flutter/foundation.dart
, Flutterプロジェクトの場合) が用意されています。
// Isolate.run() の例 (Dart 2.19+)
import 'dart:isolate';
int performHeavyTask(int value) {
print('[Isolate.run] Starting task with $value...');
int result = 0;
for (int i = 0; i < value * 100000000; i++) {
result++;
}
print('[Isolate.run] Task finished.');
return result;
}
void main() async {
print('[Main] Calling Isolate.run...');
// Isolate.run は Future<R> を返す
final result = await Isolate.run(() => performHeavyTask(5));
print('[Main] Result from Isolate.run: $result');
print('[Main] Finished.');
}
7. エラーハンドリング 🐛
プログラム実行中に発生する可能性のある例外やエラーに対処するための仕組み。
構文/キーワード | 説明 | コード例 |
---|---|---|
try | 例外が発生する可能性のあるコードブロックを囲む。 |
|
catch | try ブロック内で発生した例外を捕捉し、処理する。特定の型の例外のみを捕捉することも可能 (on を使用)。例外オブジェクトとスタックトレースを受け取ることもできる。 | |
finally | try ブロックの処理が完了した後、例外が発生したかどうかに関わらず、必ず実行されるコードブロック。リソースの解放処理などに使う。 | |
on | catch と組み合わせて、特定の型の例外のみを捕捉する。 |
|
throw | 意図的に例外を発生させる。任意のオブジェクトをスローできるが、Exception またはError のサブクラスを使うことが推奨される。 |
|
rethrow | catch ブロック内で使用し、捕捉した例外を再度スローする。例外を部分的に処理し、呼び出し元にも通知したい場合などに使う。 |
|
Exception vs Error
- Exception: 予期される可能性のあるエラー状態を示す。プログラムで捕捉し、回復処理を行うことが想定される(例:
IOException
,FormatException
)。 - Error: プログラムのバグなど、通常は回復できない深刻な問題を示す(例:
ArgumentError
,NoSuchMethodError
,OutOfMemoryError
)。基本的には捕捉せず、プログラムを修正すべき。
8. ライブラリとパッケージ 📦
コードの再利用と整理、外部機能の利用。
8.1. ライブラリのインポート (import)
他のファイル(ライブラリ)で定義された機能(クラス、関数、変数など)を利用するために使用する。
形式 | 説明 | コード例 |
---|---|---|
基本形 | 指定したライブラリの全ての公開メンバーを利用可能にする。 |
|
プレフィックス付き (as ) | インポートしたライブラリのメンバーにアクセスする際にプレフィックス(接頭辞)を付ける。名前の衝突を避けるために使う。 |
|
部分的なインポート (show ) | ライブラリの中から指定したメンバーだけをインポートする。 |
|
部分的なインポート (hide ) | ライブラリの中から指定したメンバーを除外して、それ以外をインポートする。名前の衝突を避けるためにも使える。 |
|
遅延読み込み (deferred as ) | ライブラリを必要になった時点で初めてロードする。初期ロード時間を短縮したい場合や、特定の機能がまれにしか使われない場合に有用。Webアプリで特に効果的。 |
|
8.2. パッケージ管理
Dartのパッケージ(ライブラリの配布単位)を管理するための仕組み。pubspec.yaml
ファイルとdart pub
(または flutter pub
) コマンドを使用する。
pubspec.yaml
: プロジェクトの設定ファイル。プロジェクト名、説明、バージョン、依存パッケージなどを記述する。dependencies
: アプリケーションが実行時に必要とするパッケージ。dev_dependencies
: 開発中にのみ必要とするパッケージ(テスト、ビルドツールなど)。dart pub get
(orflutter pub get
):pubspec.yaml
に記述された依存関係を解決し、パッケージをダウンロード・取得するコマンド。dart pub add <package_name>
: パッケージをdependencies
に追加し、pub get
を実行する。dart pub remove <package_name>
: パッケージをdependencies
から削除し、pub get
を実行する。dart pub upgrade
: 依存パッケージをpubspec.yaml
の制約内で最新バージョンに更新する。- pub.dev: DartとFlutterの公式パッケージリポジトリ。利用可能なパッケージを検索できる。
# pubspec.yaml の例
name: my_awesome_app
description: A sample Dart application.
version: 1.0.0
# repository: https://github.com/user/my_awesome_app
environment:
sdk: '>=3.0.0 <4.0.0' # 対応するDart SDKのバージョン範囲
dependencies:
http: ^1.1.0 # httpパッケージ (バージョン1.1.0以上、2.0.0未満)
intl: ^0.18.1 # intlパッケージ (バージョン0.18.1以上、0.19.0未満)
path: any # pathパッケージ (任意のバージョン)
dev_dependencies:
lints: ^2.0.0 # 静的解析ルール
test: ^1.21.0 # テストフレームワーク
# flutter: # Flutterプロジェクトの場合
# sdk: flutter
# uses-material-design: true
# assets:
# - images/a_dot_burr.jpeg
コマンドラインでの操作例:
# http パッケージを依存関係に追加
dart pub add http
# test パッケージを開発時依存関係に追加
dart pub add --dev test
# 依存関係を取得/更新
dart pub get
# 依存関係を最新バージョンに更新
dart pub upgrade
# 不要になったパッケージを削除
dart pub remove path
9. その他 📝
その他の便利な構文や機能。
項目 | 説明 | コード例 |
---|---|---|
コメント | コード内に説明やメモを残すための記述。コンパイラには無視される。 |
|
メタデータ (アノテーション) | コードに追加情報を提供するための構文。@ 記号で始まる。コンパイラやツールが解釈して利用する。@override , @required , @deprecated などが組み込みで用意されている。カスタムアノテーションも定義可能。 |
|
Typedef | 関数型に別名を付ける機能。複雑な関数シグネチャを簡潔に表現したり、コードの可読性を向上させたりするのに役立つ。Dart 2.13以降では、関数型だけでなく、任意の型(クラス、ジェネリック型など)にも別名を付けられるようになった。 |
|
コメント