【问题标题】:Flutter Firebase create a dynamic list of streamsFlutter Firebase 创建流的动态列表
【发布时间】:2021-12-11 19:18:36
【问题描述】:

如何创建流的动态列表?

我需要一个构建流列表并返回流的函数。

我的功能(尝试返回一个像这样的值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)。

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

这是我的代码:

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

【问题讨论】:

    标签: firebase flutter google-cloud-firestore stream stream-builder


    【解决方案1】:

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

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

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

    编辑 1:

    所以你需要的是一个collectionGroup:

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

    【讨论】:

    • 你好@Canada,谢谢你的回答。我的问题是,我需要多文档来显示我的数据。我建立了一个聊天室概述。为此,我需要不同的文档,因为一个聊天室的每个数据都存储在一个文档中。如果我想显示 multitpe 聊天室,我需要 multitpe 文档。首先,我使用 Futurebuilder,但我会实时更改数据。那是因为我尝试构建一个 multitpe 流构建器。
    • 你知道 1 个流给你多个文档,对吧?
    • 是的,但我的流在不同的集合中。例如:用户在三个聊天室写。每个聊天室数据都包含在 chat_room_1、chat_room_2 和 chat_room_3 中。现在我想建立一个包含所有聊天室的列表视图。为此,我需要所有收藏。我的问题是其他用户在此集合中也有她的聊天室数据。解释起来很沉重。如果您有任何问题,请随时问我。
    • 我现在明白了...您是否考虑过使用 CollectionGroups:这是 Firestore 中添加的一项新功能,用于从多个集合中获取文档link
    • 不,谢谢你的想法。我会试试的(:
    猜你喜欢
    • 2019-08-08
    • 1970-01-01
    • 2020-03-21
    • 2023-03-31
    • 1970-01-01
    • 2022-01-20
    • 1970-01-01
    • 2021-10-12
    • 2020-11-18
    相关资源
    最近更新 更多