Firebase实现关注/取消关注功能时按钮状态不更新问题求助
问题原因与解决方案
问题分析
- UI状态依赖缓存数据:按钮的
isFollowing状态判断依赖allUserProvider.currentUser,但该对象未同步Firestore的最新更新,导致状态判断始终是旧值。 - notifyListeners()调用时机错误:在Firebase事务内部调用
notifyListeners(),此时事务尚未提交到Firestore,数据未真正更新,触发的UI重建无法获取最新状态。 - StreamBuilder未关联当前用户最新数据:StreamBuilder监听整个用户集合,但判断关注状态时未使用流中当前用户的实时数据,而是依赖provider缓存。
解决方案
方案1:在StreamBuilder中直接使用当前用户实时数据判断状态
修改UI代码,从Stream的快照中获取当前用户的最新following列表,替代provider缓存的判断逻辑:
StreamBuilder( stream: FirebaseFirestore.instance.collection('users_info').snapshots(), builder: (context, snapshot) { if (snapshot.hasError) { return Center(child: Text(snapshot.error.toString())); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 从快照中获取当前用户的最新数据 final currentUserDoc = snapshot.data!.docs.firstWhere( (doc) => doc.id == allUserProvider.currentUser?.uid, orElse: () => throw Exception("Current user not found"), ); final currentUserData = UserData.fromSnapshot(currentUserDoc.data(), currentUserDoc.id); final List<UserData> allUsers = snapshot.data!.docs .map((doc) => UserData.fromSnapshot(doc.data(), doc.id)) .toList(); return ListView.builder( shrinkWrap: true, itemCount: allUsers.length, itemBuilder: (context, index){ UserData user = allUsers[index]; bool isCurrentUser = user.uid == currentUserData.uid; // 使用当前用户实时的following列表判断状态 final bool isFollowing = currentUserData.following?.contains(user.uid ?? '') ?? false; if(user.uid == currentUserId){ return Container(); } return ListTile( leading: CircleAvatar( backgroundImage: NetworkImage(user.image ?? ''), ), title: Text( user.name ?? '', style: const TextStyle( fontFamily: GlobalVariables.fontsPally, fontWeight: FontWeight.bold, ), ), subtitle: Text( user.uid ?? 'no id', style: const TextStyle( color: Color(0xFFC1C0C9), fontFamily: GlobalVariables.fontsSatoshi, ), ), trailing: !isCurrentUser ? ElevatedButton( onPressed: (){ print('${userId} and ${user.uid}'); allUserProvider.followUser(user.uid!); }, child: Text(isFollowing ? 'UnFollow' : 'Follow' ), ) : null ); } ); } )
方案2:修正Provider的followUser方法,同步更新currentUser
调整followUser方法,在事务提交后重新获取当前用户的最新数据,更新provider的currentUser后再通知UI更新:
Future<void> followUser(String targetUserId) async { final DocumentReference userDoc = FirebaseFirestore.instance.collection('users_info').doc(_userId); final DocumentReference targetUserDoc = FirebaseFirestore.instance.collection('users_info').doc(targetUserId); // 执行Firebase事务更新数据 await FirebaseFirestore.instance.runTransaction((transaction) async { final userSnapshot = await transaction.get(userDoc); final targetUserSnapshot = await transaction.get(targetUserDoc); if (!userSnapshot.exists || !targetUserSnapshot.exists) { throw Exception('Document does not exist!'); } final Map<String, dynamic> userData = userSnapshot.data() as Map<String, dynamic>; final Map<String, dynamic> targetUserData = targetUserSnapshot.data() as Map<String, dynamic>; List<String> following = List<String>.from(userData['following']); List<String> followers = List<String>.from(targetUserData['followers']); if (!following.contains(targetUserId)) { following.add(targetUserId); userData['followingCount'] = following.length.clamp(0, 1000000); targetUserData['followerCount'] = (followers.length + 1).clamp(0, 1000000); followers.add(_userId!); } else { following.remove(targetUserId); userData['followingCount'] = following.length.clamp(0, 1000000); targetUserData['followerCount'] = (followers.length - 1).clamp(0, 1000000); followers.remove(_userId); } if (userData['followingCount'] < 0 || targetUserData['followerCount'] < 0) { throw Exception('Invalid follow/unfollow operation!'); } transaction.update(userDoc, {'following': following, 'followingCount': userData['followingCount']}); transaction.update(targetUserDoc, {'followers': followers, 'followerCount': targetUserData['followerCount']}); }); // 事务完成后,获取当前用户最新数据并更新provider final updatedUserSnapshot = await userDoc.get(); if (updatedUserSnapshot.exists) { currentUser = UserData.fromSnapshot(updatedUserSnapshot.data() as Map<String, dynamic>, updatedUserSnapshot.id); } // 通知UI更新状态 notifyListeners(); }
额外优化建议
- 避免监听整个
users_info集合,改为合并当前用户和目标用户的文档流,减少不必要的UI重建:Stream<List<DocumentSnapshot>> combineUserStreams() { final currentUserStream = FirebaseFirestore.instance.collection('users_info').doc(_userId).snapshots(); final allUsersStream = FirebaseFirestore.instance.collection('users_info').snapshots(); return StreamZip([currentUserStream, allUsersStream]).map((data) => [data[0], ...data[1].docs]); } - 检查
UserData模型的fromSnapshot方法,确保following字段解析正确,避免空指针或数据解析错误。
内容的提问来源于stack exchange,提问作者Rashid Ali
相关产品推荐
相关产品推荐

