【问题标题】:Convert Firestore DocumentSnapshot to Map in Flutter在 Flutter 中将 Firestore DocumentSnapshot 转换为 Map
【发布时间】:2019-11-05 09:10:47
【问题描述】:

我需要使用 Flutter 在 Firestore 中更新包含嵌套数组的文档。

所以我需要将完整的文档放入一个 Map 中,在“sections”数组中重新排序这些地图,然后将数据存储回文档中。

但是,我不熟悉如何将快照 (DocumentSnapshot) 的数据放入地图中。

下面是一个对我尝试实现的目标不起作用的示例:

final Map<String, dynamic> doc = snapshot.data as Map<String, dynamic>;

“snapshot.data”包含文档的值。文档的结构如下所示:

{
  name: "Course 1"
  sections: [
    {name: "Section 1"},
    {name: "Section 2"}
  ]
}

sections 数组中的地图重新排序后,我需要将数据保存回文档中。

  • 问题 1:如何将 snapshot.data 的内容读入 Map?
  • 问题 2:我会删除文档,然后重新添加吗?或者我可以只更新所有内容吗?

这里是完整的功能。相关代码在“onDragFinish”中。

 // Build editable list with draggable list tiles and swipe to delete
  List<Widget> buildListViewEdit() {
    final course = db.collection("school").document("3kRHuyk20UggHwm4wrUI")
      .collection("course").document("74UsE9x7Bsgnjz8zKozv").snapshots();

    return [
      StreamBuilder(
        stream: course,
        builder: (context, snapshot) {
          if (!snapshot.hasData) return const Text("Loading...");

          return Expanded(
            child: DragAndDropList(
              snapshot.data["sections"].length,
              itemBuilder: (context, index) {
                return Card(
                  child: ListTile(
                    title: Text(snapshot.data["sections"][index]["name"]),
                    onTap: () {
                      print("hello");
                    }                    
                  )
                );
              },
              onDragFinish: (before, after) {
                print('on drag finish $before $after');

                //final docString = snapshot.data.toString();

                final Map <String, dynamic> doc = snapshot.data;

                //final tempSections = List.castFrom(snapshot.data["sections"]).toList();

                //Map data = tempSections[before];

                //tempSections.removeAt(before);
                //tempSections.insert(after,data);

                //snapshot.data["sections"] = tempSections;

                //db.collection("school").document("3kRHuyk20UggHwm4wrUI")
                  //.collection("course").document("74UsE9x7Bsgnjz8zKozv").updateData(snapshot.data);

                //var line = snapshot.data["sections"][before];

                //snapshot.data["sections"].removeAt(before);
                //snapshot.data["sections"].insert(after,line);

                /*
                List<Map> sections = docCopy["sections"];

                Map data = docCopy["sections"][before];
                sections.removeAt(before);
                sections.insert(after, data);
                print(sections);   
                */         
              },
              canDrag: (index) {
                print('can drag $index');
                return index != 3;
              },
              canBeDraggedTo: (one, two) => true,
              dragElevation: 8.0,
            )
          );
        }
      )
    ];   
  }

尝试将 snapshot.data 复制到另一个变量时出错:

flutter: ══╡ EXCEPTION CAUGHT BY GESTURE LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown while routing a pointer event:
flutter: type 'DocumentSnapshot' is not a subtype of type 'Map<String, dynamic>'
flutter:
flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially
flutter: more information in this error message to help you determine and fix the underlying cause.
flutter: In either case, please report this assertion by filing a bug on GitHub:
flutter:   https://github.com/flutter/flutter/issues/new?template=BUG.md
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      _SectionScreenState.buildListViewEdit.<anonymous closure>.<anonymous closure> (package:teach_mob/screens/section_screen.dart:150:45)

工作示例

感谢大家的帮助。这是一个对我有用的完整示例:

  // Build editable list with draggable list tiles and swipe to delete
  List<Widget> buildListViewEdit() {
    final course = db.collection("school").document("3kRHuyk20UggHwm4wrUI")
      .collection("course").document("74UsE9x7Bsgnjz8zKozv").snapshots();

    return [
      StreamBuilder(
        stream: course,
        builder: (context, snapshot) {
          if (!snapshot.hasData) return const Text("Loading...");

          return Expanded(
            child: DragAndDropList(
              snapshot.data["sections"].length,
              itemBuilder: (context, index) {
                return Card(
                  child: ListTile(
                    title: Text(snapshot.data["sections"][index]["name"]),
                    onTap: () {
                      print("hello");
                    }                    
                  )
                );
              },
              onDragFinish: (before, after) {
                print('on drag finish $before $after');

                // Convert AsyncSnapshot to DocumentSnapshot and then
                // create a map that can be changed and updated.
                final Map <String, dynamic> doc = snapshot.data.data;

                // Convert fixed length list to dynamic list, because items in
                // fixed length lists can't be added / removed.
                final tempSections = List.castFrom(doc["sections"]).toList();

                // Get the data of the list item to be dragged
                // Remove the data from the current position
                // Add the data to the new position of the list
                Map data = tempSections[before];

                tempSections.removeAt(before);
                tempSections.insert(after,data);

                // Overwrite sections with new list array
                doc["sections"] = tempSections;

                // Store the data back into the firestore document
                db.collection("school")
                  .document("3kRHuyk20UggHwm4wrUI")
                  .collection("course")
                  .document("74UsE9x7Bsgnjz8zKozv")
                  .updateData(doc);
              },
              canDrag: (index) {
                print('can drag $index');
                return index != 3;
              },
              canBeDraggedTo: (one, two) => true,
              dragElevation: 8.0,
            )
          );
        }
      )
    ];   
  }

【问题讨论】:

  • 您遇到什么样的错误? ,snapshot.data 是一张地图
  • type 'DocumentSnapshot' 不是类型转换中的 'Map' 类型的子类型
  • 我也许可以将 snapshot.data 转换为字符串,然后从那里转换为地图。但不确定,这是否是最有效的方法。我很确定编写 firestore 插件的人会想到一种更有效的方法。
  • 您不需要这样做,如果快照是 DocumentSnapshot 则 snapshot.data 是地图。我认为问题来自另一个来源,您能否分享更多产生此错误的工作示例。
  • 好的,看到 snapshot.data 将返回 Map。好的,将发布更多代码。

标签: flutter google-cloud-firestore


【解决方案1】:

根据我们的讨论,快照不是DocumentSnapshot,而是AsyncSnapshot

要获取 DocumentSnaphot,请使用 snapshot.data

要获取实际地图,您可以使用snapshot.data.data()

这将返回您正在寻找的 Map

【讨论】:

  • 什么是snapshot.data.data()?看来snapshot.data中没有data()
  • @Beginner_ 你的快照类型是什么?
  • 抱歉,我说的是 DocumentSnapshot 而不是 SyncSnapshot。请忘记我的评论。
  • snapshot.data.data() 未定义如何接受此答案?
  • 像这样定义快照变量:- QuerySnapshot> snapshot = await collectionrefrence .doc(docId) .get();
【解决方案2】:

2021 年 5 月更新

请参阅迁移到 cloud_firestore 2.0.0 here

//Replace this:
- StreamBuilder<DocumentSnapshot>(
//With this:
+ StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(

创建变量时必须定义类型:

Future<void> example(
    -  DocumentReference documentReference,
    +  DocumentReference<Map<String, dynamic>> documentReference,
    -  CollectionReference collectionReference,
    +  CollectionReference<Map<String, dynamic>> collectionReference,
    -  Query query,
    +  Query<Map<String, dynamic>> query,
) {

【讨论】:

  • 非常感谢。在将 documentID 传递给小部件后,我搜索了 5 天后如何解决获取文档数据的问题,我发现这个答案很有帮助。
【解决方案3】:

2020 年 9 月更新

要从快照文档中获取数据,您现在必须调用 snapshot.data() (cloud_firestore 0.14.0 or higher)

【讨论】:

    【解决方案4】:

    从文档快照中获取地图,请使用snapshot.data.data

    【讨论】:

      【解决方案5】:

      看起来可能是因为你有一个流构建器,所以 Snapshot 是一个 AsyncSnapshot&lt;dynamic&gt;,当你抓取它的 .data 时,你会得到一个 动态,它返回一个 DocumentSnapshot然后您需要在此对象上调用 .data 以获取正确的 Map&lt;String, dynamic&gt; data

      builder: (context, snapshot) {
      final DocumentSnapshot  ds = snapshot.data;
      final Map<String, dynamic> map = ds.data;
      }
      

      您还可以使用内置函数附加到数组,但看起来您想做一些疯狂的排序,一切都很好。

      【讨论】:

        【解决方案6】:

        出于示例的目的需要简化

        class ItemsList extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            // get the course document using a stream
            Stream<DocumentSnapshot> courseDocStream = Firestore.instance
                .collection('Test')
                .document('4b1Pzw9MEGVxtnAO8g4w')
                .snapshots();
        
            return StreamBuilder<DocumentSnapshot>(
                stream: courseDocStream,
                builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
                  if (snapshot.connectionState == ConnectionState.active) {
        
                    // get course document
                    var courseDocument = snapshot.data.data;
        
                    // get sections from the document
                    var sections = courseDocument['sections'];
        
                    // build list using names from sections
                    return ListView.builder(
                      itemCount: sections != null ? sections.length : 0,
                      itemBuilder: (_, int index) {
                        print(sections[index]['name']);
                        return ListTile(title: Text(sections[index]['name']));
                      },
                    );
                  } else {
                    return Container();
                  }
                });
          }
        }
        

        结果

        【讨论】:

          猜你喜欢
          • 2021-11-11
          • 1970-01-01
          • 2021-03-17
          • 1970-01-01
          • 2021-02-26
          • 2019-01-25
          • 2021-01-10
          • 2020-01-16
          • 2022-01-08
          相关资源
          最近更新 更多