You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Flutter+Firebase实现类社交平台通讯录好友添加功能咨询

嘿,我之前做过类似的Flutter+Firebase社交应用,刚好踩过这个通讯录匹配好友的坑,给你分享下我验证过的、类似FB/Instagram的高效实现方案:

核心实现逻辑

本质是把通讯录里的手机号/邮箱,和Firestore中用户的注册信息做精准匹配,但要注意隐私合规和性能,绝对不能把整个用户库拉到客户端比对——既慢又有安全风险。

第一步:获取通讯录权限并导出联系人数据

在Flutter里用contacts_service包就能快速拿到通讯录,记得先申请权限:iOS要在Info.plistNSContactsUsageDescription,Android要在AndroidManifest.xmlREAD_CONTACTS权限。

示例代码:

import 'package:contacts_service/contacts_service.dart';
import 'package:permission_handler/permission_handler.dart';

Future<List<Contact>> getDeviceContacts() async {
  final permissionStatus = await Permission.contacts.request();
  if (permissionStatus.isGranted) {
    // 关闭缩略图获取,提升性能
    return await ContactsService.getContacts(withThumbnails: false);
  } else {
    // 处理权限拒绝的弹窗提示
    return [];
  }
}

拿到联系人后,要统一格式化手机号(比如去掉空格、特殊符号,转成国际标准格式,像+8613xxxxxxxxx),避免因为格式不一致匹配失败。

第二步:Firestore端高效匹配已注册用户

直接遍历通讯录每个号码去Firestore单独查询会触发几十上百次请求,性能拉胯。推荐两种高效方案:

方案A:用Cloud Functions做批量匹配(首选)

把匹配逻辑放到云函数里,客户端只传通讯录号码数组,云函数批量查询后返回结果——既安全又高效,还能规避Firestore客户端查询的限制。

云函数代码(Node.js):

exports.findRegisteredUsers = functions.https.onCall(async (data, context) => {
  let phoneNumbers = data.phoneNumbers;
  // 过滤空值+统一格式
  const cleanedNumbers = phoneNumbers
    .filter(num => num.trim() !== '')
    .map(num => num.replace(/\s|\-|\(|\)/g, ''));

  // Firestore的whereIn最多支持10个参数,所以分批查询
  const queryBatches = [];
  for (let i = 0; i < cleanedNumbers.length; i += 10) {
    const batch = cleanedNumbers.slice(i, i + 10);
    queryBatches.push(
      admin.firestore().collection('users')
        .where('phoneNumber', 'in', batch)
        .get()
    );
  }

  const queryResults = await Promise.all(queryBatches);
  const registeredUsers = [];
  queryResults.forEach(snapshot => {
    snapshot.forEach(doc => {
      registeredUsers.push({
        uid: doc.id,
        displayName: doc.data().displayName,
        avatarUrl: doc.data().photoURL,
        phoneNumber: doc.data().phoneNumber
      });
    });
  });

  return registeredUsers;
});

Flutter端调用云函数:

final HttpsCallable matchCallable = FirebaseFunctions.instance.httpsCallable('findRegisteredUsers');
final response = await matchCallable.call({
  'phoneNumbers': formattedPhoneList, // 之前格式化好的手机号数组
});
final List<dynamic> matchedUsers = response.data;

方案B:客户端分批查询(适合小用户量场景)

如果你的用户量不大,也可以在客户端用whereIn分批查询,但要注意每批最多10个号码。另外要确保Firestore规则只允许用户访问其他用户的公开资料,避免信息泄露。

第三步:实现加好友逻辑

拿到匹配的用户列表后,加好友本质是在Firestore中建立双向的关系记录,参考社交平台的常规设计:

  • 给对方的friendRequests子集合添加一条请求记录(包含发送者ID、昵称、头像、时间戳、状态)
  • 在自己的pendingFriends子集合添加一条待确认记录

示例代码:

Future<void> sendFriendRequest(String targetUid, String targetName, String targetAvatar) async {
  final currentUid = FirebaseAuth.instance.currentUser!.uid;
  final currentName = '当前用户昵称'; // 从本地缓存或Firestore获取
  final currentAvatar = '当前用户头像URL';

  // 给对方发好友请求
  await FirebaseFirestore.instance
      .collection('users')
      .doc(targetUid)
      .collection('friendRequests')
      .doc(currentUid)
      .set({
    'senderUid': currentUid,
    'senderName': currentName,
    'senderAvatar': currentAvatar,
    'timestamp': FieldValue.serverTimestamp(),
    'status': 'pending'
  });

  // 自己这边记录待确认请求
  await FirebaseFirestore.instance
      .collection('users')
      .doc(currentUid)
      .collection('pendingFriends')
      .doc(targetUid)
      .set({
    'receiverUid': targetUid,
    'receiverName': targetName,
    'receiverAvatar': targetAvatar,
    'timestamp': FieldValue.serverTimestamp(),
    'status': 'pending'
  });
}

必做的隐私合规事项

  • 必须在权限申请弹窗和隐私政策里明确告知用户:获取通讯录仅用于匹配已注册好友,不会存储或滥用
  • 匹配完成后,立即删除本地缓存的通讯录原始数据
  • Firestore规则要严格限制:用户只能读取其他用户的公开字段(比如昵称、头像),不能访问私有信息(比如邮箱、地址)

额外优化技巧

  • 缓存匹配结果,比如24小时内不用重复获取通讯录和匹配,减少请求量
  • 支持邮箱匹配(如果用户允许用邮箱注册),逻辑和手机号完全一致
  • 展示列表时自动过滤已添加的好友,避免重复发送请求

内容的提问来源于stack exchange,提问作者OddlyGhost

火山引擎 最新活动