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

问题描述 投票:0回答:2

我正在尝试将来自两个不同集合的两个流(第一个是“avvenimenti”,第二个是“prospettive”)合并到一个返回一个 ListView 的 StreamBuilder 中。

我尝试了很多选项(使用 Rx、StreamBuilder 返回一个返回 ListView 的 StremBuilder 等..),但未能使它们适应我的项目。

在 ListView 的 Children 属性上,我只能添加一个列表:

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');
}
flutter dart google-cloud-firestore stream
2个回答
2
投票

使用 Rx 中的

CombineLatestStream
,因为您提到您已经尝试过。

StreamBuilder(
      stream: CombineLatestStream.list([
        Global.avvenimentiRef.streamData(),
        Global.prospettiveRef.streamData(),
      ]),
      builder: (context, snap1)
  • 请注意,如果其中一个流发生更改,您
    StreamBuilder
    将重新加载
  • 您可以使用
    snap1.data[0]
    snap1.data[1]
    ..ect 访问流返回的数据

对于列表,你可以尝试

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())],
                  
                  ),

0
投票

如果您不想安装软件包并且不想嵌套您的

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 中将两个肘节流合并为一个的示例用法

StreamBuilder
:

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

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

    return MyDataWidget();
  },
)
© www.soinside.com 2019 - 2024. All rights reserved.