Flutter Firestore点赞按钮行为异常问题求助
Firebase点赞功能按钮状态异常修复
问题描述
通过Firebase实现点赞功能,预期点击按钮时:
- 当前用户的
sentHearts字段存入被点赞用户ID - 被点赞用户的
receivedHearts字段存入当前用户ID
目前数据库数据存储正常,但按钮状态切换混乱(表现为点击后状态反复跳动)。
问题代码
1. 接收/发送点赞的StreamBuilder
// receivedLike Widget _receivedHearts(BuildContext context) { final db = FirebaseFirestore.instance; final uid = FirebaseAuth.instance.currentUser?.uid; return StreamBuilder( stream: db .collection(AppKeys.users) .where('receivedHearts', arrayContains: uid) .snapshots(), builder: (context, snapshot) { print(uid); print('snapshot : ${snapshot.data}'); List<UsersModel> user = []; for (var docSnapshot in snapshot.data?.docs ?? []) { print('snapshot : ${snapshot.data?.docs}'); user.add(UsersModel.fromDocument(docSnapshot)); } if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } switch (snapshot.connectionState) { case ConnectionState.waiting: return CircularProgressIndicator(); default: return Container( // sendLike Widget _sendHeart(BuildContext context) { final db = FirebaseFirestore.instance; final uid = FirebaseAuth.instance.currentUser?.uid; return StreamBuilder( stream: db .collection(AppKeys.users) .where('sentHearts', arrayContains: uid) .snapshots(), builder: (context, snapshot) { List<UsersModel> user = []; for (var docSnapshot in snapshot.data?.docs ?? []) { user.add(UsersModel.fromDocument(docSnapshot)); } if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } switch (snapshot.connectionState) { case ConnectionState.waiting: return CircularProgressIndicator(); default: return Container(
2. 点赞/取消点赞方法
Future<void> likePost( BuildContext context, UsersModel item, bool isLike) async { final uid = FirebaseAuth.instance.currentUser?.uid; try { await FirebaseFirestore.instance .collection(AppKeys.users) .doc(item.id) .update( { 'receivedHearts': FieldValue.arrayUnion([uid]) }, ); await FirebaseFirestore.instance .collection(AppKeys.users) .doc(uid) .update( { 'sentHearts': FieldValue.arrayUnion([item.id]), }, ); print('sent 값 : ${item.sentHearts}'); print('receive 값 : ${item.receivedHearts}'); } catch (e) { print(e); } } Future<void> dislikePost( BuildContext context, UsersModel item, bool isLike) async { final uid = FirebaseAuth.instance.currentUser?.uid; try { await FirebaseFirestore.instance .collection(AppKeys.users) .doc(item.id) .update( { 'receivedHearts': FieldValue.arrayRemove([uid]) }, ); await FirebaseFirestore.instance .collection(AppKeys.users) .doc(uid) .update( { 'sentHearts': FieldValue.arrayRemove([item.id]), }, ); } catch (e) { print(e); } }
3. 点赞按钮代码
GestureDetector( onTap: () async { bool newLikeState = !isLike; if (newLikeState) { print('newlike : $newLikeState'); try { await likePost(context, item, isLike); print(' : $isLike'); setState(() { isLike = newLikeState; }); } catch (e) { print('faild: $e'); } } else { try { await dislikePost(context, item, isLike); print('dislike : $isLike'); setState(() { isLike = !newLikeState; }); } catch (e) { print('failed: $e'); } } print('final like : $isLike'); }, child: isLike ? SvgPicture.asset('assets/icons/smallHeart_on.svg') : SvgPicture.asset('assets/icons/smallHeart.svg'), )
问题根源
- 本地状态与远程数据脱节:按钮状态依赖本地
isLike变量维护,未与Firestore实时数据同步,导致StreamBuilder收到数据更新时,本地状态未刷新,显示异常。 - StreamBuilder查询逻辑错误:
_sendHeart中用where('sentHearts', arrayContains: uid)查询整个用户集合,实际是找其他用户的sentHearts包含当前用户ID的记录,而非当前用户自己的sentHearts数组。 - 旧数据打印无效:点赞后打印的
item.sentHearts是本地缓存的旧数据,不会自动同步Firestore的更新。
修复方案
1. 重构按钮状态监听逻辑
用StreamBuilder监听当前用户的文档,实时获取sentHearts数组,直接从远程数据判断点赞状态:
Widget _likeButton(BuildContext context, UsersModel item) { final uid = FirebaseAuth.instance.currentUser?.uid; if (uid == null) return const SizedBox(); return StreamBuilder<DocumentSnapshot>( stream: FirebaseFirestore.instance .collection(AppKeys.users) .doc(uid) .snapshots(), builder: (context, snapshot) { if (snapshot.hasError) return const Icon(Icons.error_outline); if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(size: 20); } // 从Firestore实时获取当前用户的sentHearts数组 final userData = snapshot.data?.data() as Map<String, dynamic>?; final List<String>? sentHearts = userData?['sentHearts']?.cast<String>(); final isLiked = sentHearts?.contains(item.id) ?? false; return GestureDetector( onTap: () async { if (isLiked) { await dislikePost(context, item); } else { await likePost(context, item); } // 无需手动setState,Stream会自动刷新状态 }, child: isLiked ? SvgPicture.asset('assets/icons/smallHeart_on.svg') : SvgPicture.asset('assets/icons/smallHeart.svg'), ); }, ); }
2. 简化点赞/取消点赞方法
去掉不必要的isLike参数,直接基于实时数据操作:
Future<void> likePost(BuildContext context, UsersModel item) async { final uid = FirebaseAuth.instance.currentUser?.uid; if (uid == null) return; try { // 更新被点赞用户的receivedHearts await FirebaseFirestore.instance .collection(AppKeys.users) .doc(item.id) .update({ 'receivedHearts': FieldValue.arrayUnion([uid]) }); // 更新当前用户的sentHearts await FirebaseFirestore.instance .collection(AppKeys.users) .doc(uid) .update({ 'sentHearts': FieldValue.arrayUnion([item.id]), }); } catch (e) { print('点赞失败: $e'); } } Future<void> dislikePost(BuildContext context, UsersModel item) async { final uid = FirebaseAuth.instance.currentUser?.uid; if (uid == null) return; try { // 更新被点赞用户的receivedHearts await FirebaseFirestore.instance .collection(AppKeys.users) .doc(item.id) .update({ 'receivedHearts': FieldValue.arrayRemove([uid]) }); // 更新当前用户的sentHearts await FirebaseFirestore.instance .collection(AppKeys.users) .doc(uid) .update({ 'sentHearts': FieldValue.arrayRemove([item.id]), }); } catch (e) { print('取消点赞失败: $e'); } }
3. 优化原StreamBuilder逻辑(可选)
如果需要展示已点赞/收到点赞的用户列表,修正查询逻辑:
// 展示当前用户发送的点赞列表 Widget _sendHeart(BuildContext context) { final uid = FirebaseAuth.instance.currentUser?.uid; if (uid == null) return const SizedBox(); return StreamBuilder<DocumentSnapshot>( stream: FirebaseFirestore.instance .collection(AppKeys.users) .doc(uid) .snapshots(), builder: (context, snapshot) { if (snapshot.hasError) return Text('Error: ${snapshot.error}'); if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } final userData = snapshot.data?.data() as Map<String, dynamic>?; final List<String>? sentHearts = userData?['sentHearts']?.cast<String>(); if (sentHearts == null || sentHearts.isEmpty) { return const Text('暂无点赞记录'); } // 查询所有被当前用户点赞的用户 return StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance .collection(AppKeys.users) .where(FieldPath.documentId, whereIn: sentHearts) .snapshots(), builder: (context, snapshot) { if (snapshot.hasError) return Text('Error: ${snapshot.error}'); if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } List<UsersModel> likedUsers = []; for (var doc in snapshot.data?.docs ?? []) { likedUsers.add(UsersModel.fromDocument(doc)); } // 构建列表UI return ListView.builder( itemCount: likedUsers.length, itemBuilder: (context, index) => ListTile( title: Text(likedUsers[index].name ?? ''), ), ); }, ); }, ); }
修复说明
- 按钮状态完全依赖Firestore实时数据,避免本地状态与远程数据不一致的问题。
- 简化操作逻辑,去掉冗余参数,减少出错概率。
- 修正列表查询逻辑,确保获取的是正确的点赞记录。
内容的提问来源于stack exchange,提问作者Loolii
相关产品推荐
相关产品推荐

