查询Firestore时ListView数据重复问题的原因排查
问题描述
通过Firestore监听器获取当前用户所属群组列表并用ListView展示,初始用户属于3个群组(名称1、2、3),调用_createGroupDialog()创建新群组(名称4)后跳转至群聊页面,返回群组列表时显示11条重复条目,实际应显示4条。
实际显示内容
3 2 1 3 4 2 1 2 3 4 1
预期显示内容
1 2 3 4
问题根源
- 临时列表未清空:每次监听器触发时,仅清空了
tempGroupsList和groups,但tempGroupDataList未被重置,导致新数据不断累加旧数据,出现重复条目。 - 异步遍历时序错误:使用
forEach结合async/await时,setState会在所有异步请求完成前执行,后续返回的异步数据会继续添加到tempGroupDataList,再次触发setState时就会带上历史重复数据。
修复方案
- 监听器触发时先清空所有临时列表,避免数据累加;
- 使用
Future.wait等待所有群组信息的异步请求完成后,再统一更新状态,解决时序问题; - 确保每次状态更新都基于最新的群组ID列表。
修改后的代码
class Home extends StatefulWidget { //My Groups screen Home({Key? key}) : super(key: key); @override State<Home> createState() => _HomeState(); } class _HomeState extends State<Home> { var groups = []; var tempGroupsList = []; var tempGroupDataList = []; final groupNameController = TextEditingController(); final database = Database(); String username = ''; String tempUsername = ''; String uid = ''; String tempUID = ''; var groupsListener; var lastReadTimestampListener; var groupChatListener; late Stream<QuerySnapshot> groupsStream; bool eventHappened = false; @override void initState() { super.initState(); initialize(); } @override dispose() { groupsListener.cancel(); groupChatListener.cancel(); lastReadTimestampListener.cancel(); super.dispose(); } void initialize() async { tempUID = await database.getUID(); tempUsername = await database.getUsername(); getGroupsListener(); } void getGroupsListener() async { await globals.userGlobals(); //Required if using globals final groupsCollectionPath = globals.groupsCollection(); groupsStream = FirebaseFirestore.instance.collection(groupsCollectionPath).snapshots(); groupsListener = groupsStream.listen((event) async { // 清空所有临时列表,避免数据累加 tempGroupsList = []; tempGroupDataList = []; for (var doc in event.docs) { tempGroupsList.add(doc.id); } // 使用Future.wait等待所有异步请求完成后再更新状态 final futures = tempGroupsList.map((element) => database.getGroupInfo(groupID: element)); tempGroupDataList = await Future.wait(futures); setState(() { groups = tempGroupDataList; }); }); } Future<void> _createGroupDialog(BuildContext context) { return showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Create Group'), content: TextField( controller: groupNameController, decoration: InputDecoration( hintText: 'Group Name', ) ), actions: [ TextButton( onPressed: () async { if (groupNameController.text.isNotEmpty) { final groupID = await database.createGroup(groupName: groupNameController.text); if (groupID == '') { Fluttertoast.showToast(msg: 'Error creating group'); } else { groupNameController.clear(); Navigator.of(context).pop(); //Close the dialog box Navigator.push( context, MaterialPageRoute(builder: (context) => ScreenChat(groupID: groupID,)), ); } } else { Fluttertoast.showToast(msg: 'Enter a group name'); } }, child: Text('OK') ), TextButton(onPressed: () {Navigator.of(context).pop();}, child: Text('Cancel')), ], ); } ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Color.fromRGBO(26, 26, 26, 1.0), appBar: AppBar( title: Text('Groups'), backgroundColor: Color.fromRGBO(39, 38, 38, 1.0), elevation: 0.0, actions: <Widget>[ ElevatedButton( onPressed: () { _createGroupDialog(context); }, child: Icon(Icons.add) ) ], ), body: Column( children: [ Expanded( child: ListView.builder( itemCount: groups.length ?? 0, itemBuilder: (context, index) { final group = ModelMainGroup.fromJson(groups[index]); final groupName = group.groupName; final groupID = group.groupID; return GestureDetector( child: groupWidget(groupName, groupID, false), onTap: () { Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenChat(groupID: groupID,)),); }, ); }, ), ) ], ), ); } }
内容的提问来源于stack exchange,提问作者whatwhatwhat
相关产品推荐
相关产品推荐

