在 Flutter 中将 Firestore DocumentSnapshot 转换为 Map
Posted
技术标签:
【中文标题】在 Flutter 中将 Firestore DocumentSnapshot 转换为 Map【英文标题】:Convert Firestore DocumentSnapshot to Map in Flutter 【发布时间】: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 是一张地图根据我们的讨论,快照不是DocumentSnapshot
,而是AsyncSnapshot
要获取 DocumentSnaphot,请使用 snapshot.data
要获取实际地图,您可以使用snapshot.data.data()
这将返回您正在寻找的 Map
【讨论】:
什么是snapshot.data.data()
?看来snapshot.data
中没有data()
@Beginner_ 你的快照类型是什么?
抱歉,我说的是 DocumentSnapshot 而不是 SyncSnapshot。请忘记我的评论。
snapshot.data.data()
未定义如何接受此答案?
像这样定义快照变量:- QuerySnapshot【参考方案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<dynamic>
,当你抓取它的 .data 时,你会得到一个 动态,它返回一个 DocumentSnapshot,然后您需要在此对象上调用 .data 以获取正确的 Map<String, dynamic> 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();
);
结果
【讨论】:
以上是关于在 Flutter 中将 Firestore DocumentSnapshot 转换为 Map的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Firestore -Flutter 中将所有文档从一个集合复制到另一个集合?
如何在flutter中将用户数据保存在云Firestore中