0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Dart】DartのStream — `await for`で非同期データを扱う

Posted at

はじめに

前回の記事で紹介した 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-catchawait 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}');
    },
  );
}

StreamBuilderFutureBuilder の兄弟のような存在です。


まとめ

概念 内容
Stream 非同期的に複数のデータを流す仕組み
async* / yield ストリームを生成する構文
await for 非同期ストリームを逐次処理する
listen() イベント購読(複数リスナー対応可)
StreamController 手動でイベントを発行するクラス

Stream を理解すると、Flutterでリアルタイム通信・状態管理・イベント処理がスムーズに扱えるようになります。
Future が「点」なら、Stream は「線」。
アプリの動的な挙動を支える強力な非同期の基盤です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?