Flutter:将两个流串流到一个屏幕中?

时间:2018-08-16 15:13:08

标签: stream dart flutter

我有两个流从两个不同的api获取。

Stream<Month> get monthOutStream => monthOutController.stream;
Stream<MySchedule> get resultOutStream => resultController.stream;

我正在应用程序的两种不同状态下获取这些数据,这是在用户发生一些事件后的开始和几个月后得出的结果。

MyScheduleBloc(){
  initialData();
}

Future initialData() async {
  MySchedule mySchedule = await myScheduleViewModel.importMySchedule(now.id);
  resultController.add(mySchedule);
}

我的屏幕上的流生成器​​为

Widget build(BuildContext context) {
final webCalenderBloc = WebCalenderBloc();
return StreamBuilder(
  stream: webCalenderBloc.resultOutStream,
  builder: (BuildContext context , snapdata){
    if(!snapdata.hasData){
      return Center(
        child: CircularProgressIndicator(),
      );
    }
    return body(snapdata.data);
   },
 );
}

因为主要的窗口小部件构建方法将带有resultoutstream的StreamBuilder窗口小部件作为流。我在哪里获取另一个流monthoutStream。 我可以在流中获取流吗?我在处理两个流时会丢失任何东西吗?我不想从monthoutstream构建任何小部件,但想检查其中的数据。

4 个答案:

答案 0 :(得分:13)

如果需要,您可以嵌套StreamBuilder。没有什么可以阻止您执行以下操作:

StreamBuilder(
  stream: stream1,
  builder: (context, snapshot1) {
    return StreamBuilder(
      stream: stream2,
      builder: (context, snapshot2) {
        // do some stuff with both streams here
      },
    );
  },
)

这对您有意义的另一种解决方案是:流被设计为可合并/转换的。您可以制作第三个流,该流是后面两个流的合并。

理想地,对于复杂的流操作,您将希望使用rxdart,因为它提供了一些有用的转换器。

使用rxdart,将两个Observable(它们是Stream的子类)的融合如下:

Observable<bool> stream1;
Observable<String> stream2;

final fusion = stream1.withLatestFrom(stream2, (foo, bar) {
  return MyClass(foo: foo, bar: bar);
});

答案 1 :(得分:1)

Observable.combineLatest2(
        aStream,
        bStream,
        (a, b, c) =>
        a != '' && b != '');

combineLatestN返回组合流

答案 2 :(得分:1)

我正在使用一种 BLoC,其中我广播了 Stream<Null>,它只是在某些事情发生变化时通知听众。有点像 Qt 的信号和插槽。无论如何,对于我想听不止一个流的情况,我做了这门课。它基本上是 StreamBuilder,但您可以收听多个流,它会丢弃流中的任何数据。

import 'dart:async';

import 'package:flutter/widgets.dart';

typedef MultiStreamWidgetBuilder<T> = Widget Function(BuildContext context);

// A widget that basically re-calls its builder whenever any of the streams
// has an event.
class MultiStreamBuilder extends StatefulWidget {
  const MultiStreamBuilder({
    required this.streams,
    required this.builder,
    Key? key,
  }) : super(key: key);

  final List<Stream<dynamic>> streams;
  final MultiStreamWidgetBuilder builder;

  Widget build(BuildContext context) => builder(context);

  @override
  State<MultiStreamBuilder> createState() => _MultiStreamBuilderState();
}

class _MultiStreamBuilderState extends State<MultiStreamBuilder> {
  final List<StreamSubscription<dynamic>> _subscriptions = [];

  @override
  void initState() {
    super.initState();
    _subscribe();
  }

  @override
  void didUpdateWidget(MultiStreamBuilder oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.streams != widget.streams) {
      // Unsubscribe from all the removed streams and subscribe to all the added ones.
      // Just unsubscribe all and then resubscribe. In theory we could only
      // unsubscribe from the removed streams and subscribe from the added streams
      // but then we'd have to keep the set of streams we're subscribed to too.
      // This should happen infrequently enough that I don't think it matters.
      _unsubscribe();
      _subscribe();
    }
  }

  @override
  Widget build(BuildContext context) => widget.build(context);

  @override
  void dispose() {
    _unsubscribe();
    super.dispose();
  }

  void _subscribe() {
    for (final s in widget.streams) {
      final subscription = s.listen(
        (dynamic data) {
          setState(() {});
        },
        onError: (Object error, StackTrace stackTrace) {
          setState(() {});
        },
        onDone: () {
          setState(() {});
        },
      );
      _subscriptions.add(subscription);
    }
  }

  void _unsubscribe() {
    for (final s in _subscriptions) {
      s.cancel();
    }
    _subscriptions.clear();
  }
}

示例使用:

class AppWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiStreamBuilder(
      streams: [appState.repoListChanged, appState.selectedRepoChanged],
      builder: _buildMain,
    );
  }

  Widget _buildMain(BuildContext context) {
    return Scaffold(
      body: Row(
...

我只是写了它,所以我没有太多测试。我认为从理论上讲,您可以制作一个为您提供状态的系统,尽管我不确定 Dart 的类型系统是否先进到足以让您无需到处求助于 dynamic 就可以做到这一点。

答案 3 :(得分:0)

在我的情况下,我更喜欢将多个流合并到一个流中,如果它们来自同一类型,那么您可以使用:

import 'package:async/async.dart' show StreamGroup;
...
StreamGroup.merge([stream1,stream2]);