【 Flutter 】Flutter を 基礎 から 学習 ( Dart編 ) part32 Dartの特徴

基礎 から 学ぶ Flutter 」という書籍で  学習 したことを ブログでアウトプットしていこうと思います。今回は Dart編 ( part32 )です。

前回

【 Flutter 】Flutter を 基礎 から 学習 ( Dart編 ) part31 Dartの特徴

今回はStreamについての学習です。

Dartの特徴

Stream

リアクティブプログラムの基礎です。
リアクティブプログラムというのはデータを受け取るたびに関連したプログラムが反応(リアクション)して処理を行うようにするプログラミングの考え方です。

C#のRx(リアクティブエクステンション)がリアクティブプログラムを実現するものになるでしょうか。

はじめてのRx超入門 – Qiita

StreamFutureのように成功、失敗を非同期で取得可能です。
Futureは値を取得すると処理が終わりますがStreamは閉じないと(遮断しないと)ずっと値が流れ込んできます。

動かして理解する!dartのStreamとrxdart! – Qiita

発行する側と購読する側があって、発行する側が「ぽいっ」ってデータを投げると購読する側が検知して、なんらかの処理をする。そんな感じのイメージです。

上記のサイトで紹介されているサンプルコードを少し改良してみました。
※本書のサンプルコードがどんどん雑なものになってきているように感じます。

import 'dart:async';

var controller = StreamController();

// async/awaitについては別の章で説明があります。
// この時点では私は見よう見まねで実装しているだけです。
void main() async {
  // listenさせます(購読する)
  var subscription = koudokusya1();

  // streamを流します。(購読している人へ新聞配達する感じ)
  controller.sink.add('新聞だょ〜(´・ω・`)b'); 
  await Future.delayed(Duration(seconds: 2));
  
  controller.sink.add('今日の夕刊新聞だょ〜(´・ω・`)b');
  await Future.delayed(Duration(seconds: 2));
  
  
  subscription.cancel();
  controller.close();
}

StreamSubscription koudokusya1() {
  var subscription = controller.stream.listen((data){
    print('購読している人1');
    print(data);
  });
  
  return subscription;
}

実行するとこうなります。

うまく伝わらないかもしれせんがsink.addする度にcontroller.stream.listenに渡した関数が実行されています。
C#よりもすごくお手軽な感じが良いですね。

StreamControllerクラスはdart:asyncを読み込んでおかないと使用できません。

listenするとStreamSubscriptionオブジェクト(購読する人)が1つ生成されます。
複数の購読を行う場合はasBroadcastStream()を使用します。

import 'dart:async';

var controller = StreamController();

// async/awaitについては別の章で説明があります。
// この時点では私は見よう見まねで実装しているだけです。
void main() async {
  // listenさせます(購読する)
  var subscription = koudokusya1();

  // streamを流します。(購読している人へ新聞配達する感じ)
  controller.sink.add('新聞だょ〜(´・ω・`)b'); 
  await Future.delayed(Duration(seconds: 2));
  
  controller.sink.add('今日の夕刊新聞だょ〜(´・ω・`)b');
  await Future.delayed(Duration(seconds: 2));
  
  
  subscription.cancel();
  controller.close();
}

StreamSubscription koudokusya1() {
  // どうやって利用するの?
  var subscription = controller.stream.asBroadcastStream().listen((data){
    print('購読している人1');
    print(data);
  });
  
  return subscription;
}

購読を終了させたい場合はStreamSubscriptionクラスのcancel()を呼びます。
pause()で停止、resume()で再開したりできます。

他にもエラーの場合はonError,処理が完了したときに呼ばれるonDone、エラーになっていもキャンセル(購読終了)させないようにするcancelOnErrorプロパティがあります。

コレクション同様map()where()を使って流れてくるデータを加工・制限することも可能です。
この辺りはJavaのStreamに似ていますね。というかそのままですね!

import 'dart:async';

var controller = StreamController();

// async/awaitについては別の章で説明があります。
// この時点では私は見よう見まねで実装しているだけです。
void main() async {
  // listenさせます(購読する)
  var subscription = koudokusya1();

  // streamを流します。(購読している人へ新聞配達する感じ)
  controller.sink.add('新聞だょ〜(´・ω・`)b'); 
  await Future.delayed(Duration(seconds: 2));
  
  controller.sink.add('今日の夕刊新聞だょ〜(´・ω・`)b');
  await Future.delayed(Duration(seconds: 2));
  
  
  subscription.cancel();
  controller.close();
}

StreamSubscription koudokusya1() {
  // 夕刊が不要なのでデータを流さない
  // さらに流れてきたデータの末尾に文字を付加する
  var subscription = controller.stream
    .where((data) => !data.contains('夕刊') )
    .map((data) => data + ' by なぞなぞ新聞')
    .listen((data){
    print('購読している人1');
    print(data);
  });
  
  return subscription;
}

最後に

リアクティブは「考え方」が重要です。「データを取りに行く」ではなく「データが来る」という意識をもって実装するとよいと思いました。

今日はここまで!