dart 在Streambuilder中合并两个流并返回一个ListView(Flutter)

snz8szmq  于 2023-07-31  发布在  Flutter
关注(0)|答案(2)|浏览(183)

我尝试将来自两个不同集合(第一个是'avvenimenti',第二个是'prospettive')的两个流合并到一个返回一个ListView的StreamBuilder中。
我已经尝试了许多选项(使用Rx,StreamBuilder返回StremBuilder返回ListView等),但未能使它们适应我的项目。
在ListView的children属性上,我只能添加一个List:

return StreamBuilder(
      stream: Global.avvenimentiRef.streamData(),
      builder: (context, snap1) {
        if (snap1.hasData) {
          List<Avvenimento> avvenimenti = snap1.data;
          return StreamBuilder(
            stream: Global.prospettiveRef.streamData(),
            builder: (context, snap2) {
              if (snap2.hasData) {
                List<Prospettive> prospettive = snap2.data;
                return Scaffold(
                  //this is the single ListView i want to use
                  body: ListView(
                    primary: false,
                    padding: const EdgeInsets.only(top: 20, bottom: 20),
                    children: 
                    prospettive.map((prospettiveId) => ProspettiveItem(prospettiveId: prospettiveId)).toList(),
                    avvenimenti
                        .map((avvenimentoId) =>
                            AvvenimentoItem(avvenimentoId: avvenimentoId))
                        .toList(),
                  
                  ),
                );
              } else {
                return LoadingScreen();
              }
            },
          );
        } else {
          return LoadingScreen();
        }
      },
    );
  }

字符串
我的数据库:

lass Document<T> {
  final FirebaseFirestore _db = FirebaseFirestore.instance;
  final String path;
  DocumentReference ref;

  Document({this.path}) {
    ref = _db.doc(path);
  }

  Future<T> getData() {
    return ref.get().then((v) => Global.models[T](v.data()) as T);
  }

  Stream<T> streamData() {
    return ref.snapshots().map((v) => Global.models[T](v.data()) as T);
  }

  Future<void> upsert(Map data) {
    return ref.set(Map<String, dynamic>.from(data));
  }
}

class Collection<T> {
  final FirebaseFirestore _db = FirebaseFirestore.instance;
  final String path;
  CollectionReference ref;

  Collection({this.path}) {
    ref = _db.collection(path);
  }

  Future<List<T>> getData() async {
    var snapshot = await ref.get();
    return snapshot.docs
        .map((doc) => Global.models[T](doc.data()) as T)
        .toList();
  }

  Stream<List<T>> streamData() {
    return ref.snapshots().map((list) =>
        list.docs.map((doc) => Global.models[T](doc.data()) as T).toList());
  }
}


型号:

class ElencoProspettive {
  String fonte;
  String title;
  
  ElencoProspettive({this.fonte, this.title});

  ElencoProspettive.fromMap(Map data) {
    fonte = data['data'] ?? '';
    title = data['title'] ?? '';
  }
}

class Prospettive {
  String id;
  String titlePro;
  List<ElencoProspettive> elenco;

  Prospettive({this.id, this.titlePro, this.elenco});

  factory Prospettive.fromMap(Map data) {
    return Prospettive(
        id: data['data'] ?? '',
        titlePro: data['title'] ?? '',
        elenco: (data['elenco'] as List ?? [])
            .map((v) => ElencoProspettive.fromMap(v))
            .toList());
  }
}


型号:

class Global {
  //
  // App Data
  //
  static final String title = 'Annales';

  static final FirebaseAnalytics analytics = FirebaseAnalytics();

  static final Map models = {
    Avvenimento: (data) => Avvenimento.fromMap(data),
    Prospettive: (data) => Prospettive.fromMap(data),
  };

  static final Collection<Avvenimento> avvenimentiRef =
      Collection<Avvenimento>(path: 'avvenimenti');

  static final Collection<Prospettive> prospettiveRef =
      Collection<Prospettive>(path: 'propsettive');
}

lh80um4z

lh80um4z1#

Rx使用CombineLatestStream,因为您提到已经尝试过它。

StreamBuilder(
      stream: CombineLatestStream.list([
        Global.avvenimentiRef.streamData(),
        Global.prospettiveRef.streamData(),
      ]),
      builder: (context, snap1)

字符串

  • 请注意,如果其中一个流发生了更改,则将重新加载You StreamBuilder
  • 您可以使用snap1.data[0]snap1.data[1] ..等访问流返回的数据

对于列表,您可以尝试

ListView(
                    primary: false,
                    padding: const EdgeInsets.only(top: 20, bottom: 20),
                    children: 
                    [...(prospettive.map((prospettiveId) => ProspettiveItem(prospettiveId: prospettiveId)).toList()),
                    ...(avvenimenti
                        .map((avvenimentoId) =>
                            AvvenimentoItem(avvenimentoId: avvenimentoId))
                        .toList())],
                  
                  ),

vsnjm48y

vsnjm48y2#

如果你不想安装一个包,也不想嵌套你的StreamBuilder,你可以试试这个Dart vanilla解决方案:我通过在任何提供的流勾选某些内容时勾选单个流控制器来实现:

/// Merge all [streams] events into a single [Stream] that emits void
/// when any of the [streams] emits an event.
Stream<void> combineStreams(List<Stream<dynamic>> streams) {
  late final StreamController<void> controller;
  final subscriptions = <StreamSubscription<dynamic>>[];

  Future<void> onCancel() async {
    for (final subscription in subscriptions) {
      await subscription.cancel();
    }
  }

  void onListen() {
    for (final stream in streams) {
      subscriptions.add(
        stream.listen(
          (event) {
            controller.add(null);
          },
        ),
      );
    }
  }

  void onPause() {
    for (final subscription in subscriptions) {
      subscription.pause();
    }
  }

  void onResume() {
    for (final subscription in subscriptions) {
      subscription.resume();
    }
  }

  controller = StreamController<void>(
    onCancel: onCancel,
    onListen: onListen,
    onPause: onPause,
    onResume: onResume,
  );

  return controller.stream;
}

字符串
返回的流的类型是Stream<void>,所以你不能从它访问事件值,你需要引用原始的流引用来获取它的当前值。
Flutter中将两个cubit流合并为单个StreamBuilder的示例用法:

StreamBuilder<void>(
  stream: combineStreams([
    isLoadingCubit.stream,
    isVisibleCubit.stream,
  ]),
  builder: (context, snapshot) {
    if (!isVisibleCubit.state) {
      return Container();
    }

    if (isLoadingCubit.state) {
      return MyLoadingSpinner();
    }

    return MyDataWidget();
  },
)

相关问题