📦

【Flutter】Streamの概念の理解と基本的な使い方

に公開

Streamを初めて使う方に向けたブログです。非同期処理やリアクティブなUI更新に興味はあるけれど、Streamって何?どう使うの?という方に、概念から基本的な使い方まで、分かりやすく解説します。

この記事で学べること

  • Streamの概念
  • データの流し方
  • UIをリアクティブに描画する

Streamの概念をイメージで掴む

まずは、この絵を見てください

Streamは『川』、データは『荷物』だと思ってください

川に荷物を流すと、下流で受け取れますよね?
Flutterでは、この受け取った荷物を使ってUIを更新します

このシンプルなイメージを持って、以下の基本的な使い方を見ていきましょう

基本的な使い方

シンプルなカウンターアプリをStreamを使用して実装してみます

データの流し方

まずはStreamを定義

final _counterStreamController = StreamController<int>();

FloatingActionButton押下した時にデータをStreamに流す

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Stream')),
      body: Center(...),
      floatingActionButton: FloatingActionButton(
        onPressed: onTapPlusButton,
        child: Icon(Icons.add),
      ),
    );
  }

  void onTapPlusButton() {
    _counter++;
    // 以下2つともやってることは同じ
    // 明示的に書き込みと表しているのは「sink」の方
    _counterStreamController.add(_counter);
  }

UIをリアクティブに描画する

Streamを購読し、データが流れてきたら描画更新されるStreamBuilderを使う

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Stream')),
      body: Center(
        // StreamBuilderのstreamに購読したいStreamを指定する
        child: StreamBuilder(
          stream: _counterStreamController.stream,
          builder: (context, snapshot) {
            // データの有無をsnapshot.hasDataで確認し、それぞれの描画を実装できる
            if (snapshot.hasData) {
              // データがあればカウントした数を描画
              return Text('カウント: ${snapshot.data}');
            } else {
              // データがまだ流れてきてなければ、ローディング表示
              return CircularProgressIndicator();
            }
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: onTapPlusButton,
        child: Icon(Icons.add),
      ),
    );
  }

Streamの破棄

適切に破棄しないとメモリリークが起こる可能性があるため、close()を呼び出しStreamControllerを破棄します

  
  void dispose() {
    // 画面のDispose時にStreamを破棄する
    _counterStreamController.close();
    super.dispose();
  }

まとめ

今回は、Streamの概念から基本的な使い方までを、初心者向けに解説しました

Streamは「川」、データは「荷物」というイメージで理解すると分かりやすい
StreamControllerでデータを流し、StreamBuilderでUIをリアクティブに更新できる
dispose()で必ずclose()を呼び、リソースを適切に破棄することが重要

この基本を押さえれば、非同期処理やリアクティブUIの第一歩を踏み出せます。
次は、BlocパターンやRiverpodのStreamProviderなど、より実践的なアプローチに挑戦してみましょう

Discussion