Open4

Flutter/Dartで複数のストリームを1つにまとめる

ピン留めされたアイテム
welchiwelchi

結論としては、次でできた。

import 'package:rxdart/rxdart.dart';
final mergedStream = Rx.combineLatest2(
    fruitsStream,
    vegetablesStream,
    (List<String> a, List<String> b) => a + b,
  );

// mergedStreamは2つのList<String>を束ねて次を出力する
// ['apple','banana','orange','carrot','radish','potate']

flutter - How we can mix streams in dart? - Stack Overflow

welchiwelchi

やりたかったことは、Firestoreが出力するStreamを束ねて1つにすること。

例えば、次のようなList<String>を出力するStreamを束ねて1つにし、同じタイムラインに表示したかった。

import 'package:cloud_firestore/cloud_firestore.dart';
// ['apple','banana','orange']を出力
final fruitsStream = FirebaseFirestore.instance.collection('fruits').snapshots();
// ['carrot','radish','potate']を出力
final vegetablesStream = FirebaseFirestore.instance.collection('vegetables').snapshots();

// これらを束ねて、次を出力するStreamを作りたい
// ['apple','banana','orange','carrot','radish','potate']
welchiwelchi

当初はStreamの概念が掴めておらず、試行錯誤した。
例えば、試してダメだったのは、次のような束ね方。

import 'package:async/async.dart';
final mergedStream = StreamZip([fruitsStream,vegetablesStream]);

StreamZipは各ストリームが出力する最新の値を、ペアとして出力していく。
つまり、fruitsStreamvegetablesStreamStreamZipで束ねた場合、

mergedStream
// mergedStreamは次を順々に出力していく
// ['apple','carrot']
// ['banana','radish']
// ['orange','potate']

と、最終的にはタイムラインには ['orange','potate']しか表示できない。
また、ペアを生成することを前提としているためか、どちらかのStreamがnull(一つも要素が出力されない)だと、うまく処理できない部分があった。

StreamZip class - async library - Dart API
AsyncSnapshot class - widgets library - Dart API

welchiwelchi

あと、今回の目的だと次もダメだった。

final mergedStream = StreamGroup.merge([
  fruitsStream,
  vegetablesStream,
]);

StreamGroup.merge()は、各Streamを束ねてSingle-Subscription Streamを作るため、

mergedStream
// mergedStreamは次を順々に出力していく
// ['apple','banana','orange']
// ['carrot','radish','potate']

と、後から出力された vegetablesStreamしか表示されない。

StreamGroup class - async library - Dart API
Dart 2 Language Guide