Flutter Firebase 创建一个动态的流列表

Flutter Firebase create a dynamic list of streams

如何创建流的动态列表?

我需要一个构建流列表和 return 流的函数。

我的功能(尝试 return 这样的值 List<Stream<'dynamic'>>:

List<Stream<dynamic>> _buildStreamList() async* {
  var chat_overview_object = await query_chat_overview_id();

  List<Stream<dynamic>> stream_list = [];

  for (var i = 0; i < chat_overview_object.length; i++) {
    stream_list.add(
      FirebaseFirestore.instance
          .collection('chat')
          .doc('chat_overview_data')
          .collection('data')
          .doc('chat_overview_id_1234')
          .snapshots(),
    
    );
    
  }
  
yield stream_list;
}

为什么我需要这个?

我尝试在文档上构建具有多个流的流生成器​​。

流可以不同(例如,一个流查看集合 a 文档,另一个流查看集合 b 文档 b)。

该示例有效,但仅适用于修复流。现在我想实现一个 ** 函数,其中 return 一个动态的流列表。

这是我的代码:

var _data = [];

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(() {
            _data.add(data);
            print('data: ' + _data.toString());
          });
        },
        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: _buildStreamList(),
       [
      FirebaseFirestore.instance
            .collection('chat')
            .doc('chat_overview_data')
            .collection('data')
            .doc('chat_overview_id_1233')
            .snapshots(),
        FirebaseFirestore.instance
            .collection('chat')
            .doc('chat_overview_data')
            .collection('data')
            .doc('chat_overview_id_1234')
            .snapshots(),
        FirebaseFirestore.instance
            .collection('chat')
            .doc('chat_overview_data')
            .collection('data')
            .doc('chat_overview_id_1232')
            .snapshots()
      ],
      builder: _buildMain,
    );
  }
}

Widget _buildMain(BuildContext context) {
  return MaterialApp(
      home: Row(
    children: [
      Text(
        '_data: ' + _data.toString(),
        style: TextStyle(fontSize: 15),
      )
    ],
  ));
}

编辑!
过了一会儿我得到了解决方案:

//multi stream widget builder
typedef MultiStreamWidgetBuilder<T> = Widget Function(BuildContext context);

// 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();
}

//multi streambuilder
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(() {
            //check object contain id
            var object = chat_overview_object.singleWhere(
                (element) => element.chat_overview_id[0].trim() == data.id);

            //add to object data
            object.last_message_send = data['last_message_send'];
            object.last_update = data['last_update'];
            object.members_id = data['members_id'];
            object.new_message = data['new_message'];
            object.user_display_name = data['user_display_name'];
          });

          //set index
          index++;

          //check all data load
          if (index == chat_overview_object.length) {
            //set data has load
            data_load.value = false;
          }
        },
        onError: (Object error, StackTrace stackTrace) {
          setState(() {});
        },
        onDone: () {
          setState(() {});
        },
      );
      _subscriptions.add(subscription);
    }
  }

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

您使用了错误的方法:您不应为每个 document 构建一个单独的 stream,然后再决定您使用的是哪个 stream。相反,您应该只构建 1 stream,returns 您可能需要的所有 documents

然后,在您的 StreamBuilder 中,您可以应用您的 if 条件来决定您需要使用哪个 document。这样,文档将始终是最新的。

确保应用正确的 where 子句从数据库中调用适当的 documents

编辑 1:

所以你需要的是一个集合组:

db.collectionGroup('data').where('yourField', isEqualTo: 'whatever').snapshots()