はじめに
前回の記事で紹介した Future は「一度だけ結果を返す非同期処理」でした。
一方、Stream は「複数回のデータを非同期で受け取る」仕組みです。
リアルタイムチャット、センサーの値、ネットワークのストリーム、ファイルの逐次読み込みなど、
時間の経過とともに発生するデータ を扱う場面で非常に便利です。
1. Streamとは?
Stream は「データの流れ(非同期の連続イベント)」を表すクラスです。
Future が「1回の非同期結果」なら、Stream は「0回以上の非同期結果の列」です。
| 比較 | Future | Stream |
|---|---|---|
| 結果 | 1回だけ | 複数回 |
| 使用例 | APIリクエスト | WebSocket・センサー値 |
| 取得方法 | await |
await for / listen()
|
2. Streamの基本構造
次の例は、1秒ごとに数字を出力するストリームです。
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
-
async*… 非同期で複数値を返す関数 -
yield… 1つの値をストリームに送信
この関数は Stream<int> を返し、呼び出した側で購読できます。
3. await for 構文での受け取り
Stream のデータを順に受け取るには、await for を使います。
void main() async {
await for (final value in countStream(5)) {
print('受信: $value');
}
print('完了');
}
出力結果:
受信: 1
受信: 2
受信: 3
受信: 4
受信: 5
完了
💡
await forは、Streamのデータを1件ずつ受け取りながら非同期的に処理します。
4. listen()で購読する
await for 以外にも、listen() メソッドでストリームを購読できます。
final stream = countStream(3);
stream.listen(
(value) => print('値: $value'),
onError: (e) => print('エラー: $e'),
onDone: () => print('完了!'),
);
listen() はイベント駆動的にデータを処理します。
await for と違ってループ構文ではなく「購読コールバック」です。
5. エラー処理
Stream では途中でエラーが発生する可能性があります。
try-catch と await for を組み合わせることで安全に処理できます。
Stream<int> faultyStream() async* {
for (int i = 1; i <= 5; i++) {
if (i == 3) throw Exception('3でエラー発生');
yield i;
}
}
void main() async {
try {
await for (final v in faultyStream()) {
print('受信: $v');
}
} catch (e) {
print('捕捉: $e');
}
}
6. StreamControllerで手動生成
StreamController を使うと、自分でイベントを流すこともできます。
import 'dart:async';
void main() {
final controller = StreamController<String>();
controller.stream.listen(
(data) => print('受信: $data'),
onDone: () => print('完了!'),
);
controller.add('こんにちは');
controller.add('Streamの世界');
controller.close();
}
controller.add()でイベントを流し、close()で完了を通知します。
7. Streamの種類
| 種類 | 説明 |
|---|---|
| Single-subscription stream | 一度しか購読できない(例:async*) |
| Broadcast stream | 複数のリスナーが同時購読できる(例:イベント通知) |
例:Broadcastに変換する
final broadcast = countStream(3).asBroadcastStream();
broadcast.listen((v) => print('A: $v'));
broadcast.listen((v) => print('B: $v'));
8. StreamとFutureの違い(まとめ)
| 特徴 | Future | Stream |
|---|---|---|
| 値の数 | 1つだけ | 複数 |
| 構文 | await |
await for |
| 処理完了 | 一度きり | 継続的 |
| 使用例 | API呼び出し | WebSocket・センサー・入力監視 |
9. Flutterでの応用例:StreamBuilder
Flutterでは StreamBuilder を使って、
ストリームのデータをUIにリアルタイム反映できます。
Stream<int> timerStream() =>
Stream.periodic(Duration(seconds: 1), (count) => count + 1);
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: timerStream(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Text('待機中...');
return Text('カウント: ${snapshot.data}');
},
);
}
StreamBuilderはFutureBuilderの兄弟のような存在です。
まとめ
| 概念 | 内容 |
|---|---|
Stream |
非同期的に複数のデータを流す仕組み |
async* / yield |
ストリームを生成する構文 |
await for |
非同期ストリームを逐次処理する |
listen() |
イベント購読(複数リスナー対応可) |
StreamController |
手動でイベントを発行するクラス |
Stream を理解すると、Flutterでリアルタイム通信・状態管理・イベント処理がスムーズに扱えるようになります。
Future が「点」なら、Stream は「線」。
アプリの動的な挙動を支える強力な非同期の基盤です。