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

使用Geoflutterfire2查询Firebase附近用户的首次查询异常问题

问题解决:Geoflutterfire2首次查询仅返回当前用户及替代方案

一、Geoflutterfire2问题修复

你的问题核心是首次查询取到了本地缓存的当前用户数据,而完整的服务器数据在后续流发射中才返回。以下是修复后的代码:

import 'package:geoflutterfire2/geoflutterfire2.dart';
import 'package:firebase_auth/firebase_auth.dart';

final geo = GeoFlutterFire();

Future<List<UsersRecord>?> getNearbyUsers(
  LatLng centerPoint,
  double maxDistance,
) async {
  GeoFirePoint center = geo.point(
      latitude: centerPoint.latitude, longitude: centerPoint.longitude);

  var collectionReference = FirebaseFirestore.instance.collection('users');

  double radius = maxDistance;
  String field = 'location';
  
  String currentUserId = FirebaseAuth.instance.currentUser!.uid;

  // 等待第一个非仅含当前用户的快照,避免缓存数据干扰
  var snapshots = await geo
      .collection(collectionRef: collectionReference)
      .within(center: center, radius: radius, field: field, strictMode: true)
      .firstWhere(
        (snapshots) => snapshots.length != 1 || snapshots.first.id != currentUserId,
        orElse: () => [],
      ).timeout(const Duration(seconds: 5), onTimeout: () => []);

  // 过滤掉当前用户
  return snapshots
      .where((snapshot) => snapshot.id != currentUserId)
      .map((snapshot) => UsersRecord.fromSnapshot(snapshot))
      .toList();
}

关键修改点:

  1. 添加当前用户ID过滤,确保结果中不含自身
  2. 使用firstWhere替代first,等待包含完整数据的快照
  3. 增加超时机制,防止无限等待

二、无需Geoflutterfire2的一次性地理查询方案

直接通过Geohash范围查询+手动距离计算实现,无需依赖第三方库:

import 'package:geohash/geohash.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:math';

Future<List<UsersRecord>?> getNearbyUsersWithoutGeoFlutterFire(
  LatLng centerPoint,
  double maxDistance, // 单位:千米
) async {
  // 1. 根据半径确定Geohash精度(可根据需求调整)
  int precision = 5; // 精度5对应约1.2km范围,覆盖1km查询需求
  String centerGeohash = Geohash.encode(centerPoint.latitude, centerPoint.longitude, precision: precision);
  
  // 2. 获取所有相邻的Geohash前缀
  Set<String> nearbyHashes = {centerGeohash};
  nearbyHashes.addAll(Geohash.neighbors(centerGeohash));
  
  // 3. 查询Firestore中包含这些Geohash前缀的用户
  QuerySnapshot snapshot = await FirebaseFirestore.instance
      .collection('users')
      .where('location.geohash', whereIn: nearbyHashes.toList())
      .get(GetOptions(source: Source.server)); // 强制从服务器获取数据,避免缓存
  
  // 4. 手动计算距离并过滤结果
  List<UsersRecord> nearbyUsers = [];
  String currentUserId = FirebaseAuth.instance.currentUser!.uid;
  
  for (var doc in snapshot.docs) {
    if (doc.id == currentUserId) continue; // 跳过当前用户
    
    GeoPoint geopoint = doc['location']['geopoint'];
    LatLng docLocation = LatLng(geopoint.latitude, geopoint.longitude);
    
    double distance = _calculateHaversineDistance(centerPoint, docLocation);
    if (distance <= maxDistance) {
      nearbyUsers.add(UsersRecord.fromSnapshot(doc));
    }
  }
  
  return nearbyUsers;
}

// 哈弗辛公式计算两点间距离(单位:千米)
double _calculateHaversineDistance(LatLng a, LatLng b) {
  const double earthRadius = 6371;
  double latDiff = _degreesToRadians(b.latitude - a.latitude);
  double lonDiff = _degreesToRadians(b.longitude - a.longitude);
  
  double aLatRad = _degreesToRadians(a.latitude);
  double bLatRad = _degreesToRadians(b.latitude);
  
  double temp = 
    sin(latDiff / 2) * sin(latDiff / 2) +
    sin(lonDiff / 2) * sin(lonDiff / 2) * cos(aLatRad) * cos(bLatRad);
  double c = 2 * atan2(sqrt(temp), sqrt(1 - temp));
  
  return earthRadius * c;
}

double _degreesToRadians(double degrees) {
  return degrees * (pi / 180);
}

方案说明:

  1. 通过Geohash前缀缩小查询范围,减少需要计算距离的文档数量
  2. 使用哈弗辛公式精确计算两点间直线距离
  3. 强制从服务器获取数据,避免缓存导致的不完整结果
  4. 直接过滤当前用户,无需额外处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 08:14:56