You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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'),
  )

问题根源

  1. 本地状态与远程数据脱节:按钮状态依赖本地isLike变量维护,未与Firestore实时数据同步,导致StreamBuilder收到数据更新时,本地状态未刷新,显示异常。
  2. StreamBuilder查询逻辑错误:_sendHeart中用where('sentHearts', arrayContains: uid)查询整个用户集合,实际是找其他用户的sentHearts包含当前用户ID的记录,而非当前用户自己的sentHearts数组。
  3. 旧数据打印无效:点赞后打印的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 23:12:33