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

如何计算两个GeoPoint间方位角?变量无法保留历史值问题排查

问题:方位角计算时新旧坐标始终一致,历史坐标无法持久化

开发逻辑为:从服务器获取经纬度点,用bearingLat和bearingLon存储上一次的坐标,driverLat和driverLon存储新接收的坐标,调用Geolocator.bearingBetween(driverLat!, driverLon!, bearingLat, bearingLon)计算方位角。但日志显示新旧坐标始终相同,手动验证函数逻辑无问题,核心问题是bearingLat和bearingLon无法保留上一次的坐标值。

经排查,每次触发Geofire.onKeyMoved时都会新建UpdateDrivers实例,导致类内的bearingLat和bearingLon每次都会被初始化为0,随后被赋值为当前坐标,完全无法保留历史值。

核心代码

class UpdateDrivers {
  MapController osmController;
  bool removeDriverIcon;
  String? removeDriverKey;
  double? driverLon;
  String? driverKey;
  double? driverLat;
  double bearingLat = 0;
  double bearingLon = 0;
  static const double twoPi = 2 * pi;

  UpdateDrivers({
    required this.osmController,
    required this.removeDriverIcon,
    this.removeDriverKey,
  });

  void updateAvailableDriversOnMap() async {
    if (removeDriverIcon) {
      await osmController.setStaticPosition([], removeDriverKey!);
      removeDriverIcon = false;
      removeDriverKey = null;
    } else {
      if (GeoFireAssistant.nearByAvailableDriversList.length > 0)
        for (NearByAvailableDrivers driver
            in GeoFireAssistant.nearByAvailableDriversList) {
          driverKey = driver.key!;
          driverLat = driver.latitude!;
          driverLon = driver.longitude!;
          double z = Geolocator.bearingBetween(
              driverLat!, driverLon!, bearingLat, bearingLon);
          print("heading $z");
          bearingLat = driverLat!;
          bearingLon = driverLon!;
          if (z.isNegative) {
            z = (twoPi + z);
          }
          print("heading " +
              driverLat.toString() +
              ", " +
              driverLon.toString() +
              ", " +
              bearingLat.toString() +
              ", " +
              bearingLon.toString() +
              ", ");
        }
    }
  }
}

调用代码

case Geofire.onKeyMoved:
              NearByAvailableDrivers nearByAvailableDrivers =
                  NearByAvailableDrivers(
                      map['key'], map['latitude'], map['longitude']);
              GeoFireAssistant.updateDriverNearByLocation(
                  nearByAvailableDrivers);
              UpdateDrivers updateDrivers = UpdateDrivers(
                osmController: osmController,
                removeDriverIcon: false,
              );
              updateDrivers.updateAvailableDriversOnMap();
              break;

日志信息

I/flutter (13573): heading 27.77351097, 85.36021822, 27.77351097, 85.36021822,
I/flutter (13573): heading 62.148877298990115
I/flutter (13573): heading 27.77351071, 85.36021885, 27.77351071, 85.36021885,
I/flutter (13573): heading 62.14887839428785

解决方案

核心是让bearingLat和bearingLon的状态在多次调用间保留,不能每次初始化新实例,以下是三种可行方案:

方案1:复用同一个UpdateDrivers实例

在调用代码的作用域(比如State类)中提前创建实例,每次触发事件时复用:

// 在State类中定义成员变量
late UpdateDrivers updateDrivers;

// 在initState中初始化一次
@override
void initState() {
  super.initState();
  updateDrivers = UpdateDrivers(
    osmController: osmController,
    removeDriverIcon: false,
  );
}

// 触发事件时直接复用实例
case Geofire.onKeyMoved:
              NearByAvailableDrivers nearByAvailableDrivers =
                  NearByAvailableDrivers(
                      map['key'], map['latitude'], map['longitude']);
              GeoFireAssistant.updateDriverNearByLocation(
                  nearByAvailableDrivers);
              updateDrivers.updateAvailableDriversOnMap();
              break;

方案2:将历史坐标变量提升到外部

把bearingLat和bearingLon放在持有状态的父类/State中,通过参数传递给UpdateDrivers:

// 在State类中定义成员变量
double bearingLat = 0;
double bearingLon = 0;

// 修改UpdateDrivers类,接收外部坐标引用
class UpdateDrivers {
  MapController osmController;
  bool removeDriverIcon;
  String? removeDriverKey;
  double? driverLon;
  String? driverKey;
  double? driverLat;
  static const double twoPi = 2 * pi;
  double bearingLat;
  double bearingLon;

  UpdateDrivers({
    required this.osmController,
    required this.removeDriverIcon,
    required this.bearingLat,
    required this.bearingLon,
    this.removeDriverKey,
  });

  // 原有updateAvailableDriversOnMap逻辑不变
}

// 调用时传入外部变量
case Geofire.onKeyMoved:
              NearByAvailableDrivers nearByAvailableDrivers =
                  NearByAvailableDrivers(
                      map['key'], map['latitude'], map['longitude']);
              GeoFireAssistant.updateDriverNearByLocation(
                  nearByAvailableDrivers);
              UpdateDrivers updateDrivers = UpdateDrivers(
                osmController: osmController,
                removeDriverIcon: false,
                bearingLat: bearingLat,
                bearingLon: bearingLon,
              );
              updateDrivers.updateAvailableDriversOnMap();
              break;

方案3:使用单例模式实现UpdateDrivers

让UpdateDrivers全局只有一个实例,确保状态持久化:

class UpdateDrivers {
  static final UpdateDrivers _instance = UpdateDrivers._internal();
  MapController osmController;
  bool removeDriverIcon;
  String? removeDriverKey;
  double? driverLon;
  String? driverKey;
  double? driverLat;
  double bearingLat = 0;
  double bearingLon = 0;
  static const double twoPi = 2 * pi;

  // 私有构造函数
  UpdateDrivers._internal({
    required this.osmController,
    required this.removeDriverIcon,
    this.removeDriverKey,
  });

  // 工厂构造函数返回单例
  factory UpdateDrivers({
    required MapController osmController,
    required bool removeDriverIcon,
    String? removeDriverKey,
  }) {
    _instance.osmController = osmController;
    _instance.removeDriverIcon = removeDriverIcon;
    if (removeDriverKey != null) {
      _instance.removeDriverKey = removeDriverKey;
    }
    return _instance;
  }

  // 原有updateAvailableDriversOnMap逻辑不变
}

调用时直接获取单例即可,无需每次新建:

case Geofire.onKeyMoved:
              NearByAvailableDrivers nearByAvailableDrivers =
                  NearByAvailableDrivers(
                      map['key'], map['latitude'], map['longitude']);
              GeoFireAssistant.updateDriverNearByLocation(
                  nearByAvailableDrivers);
              UpdateDrivers updateDrivers = UpdateDrivers(
                osmController: osmController,
                removeDriverIcon: false,
              );
              updateDrivers.updateAvailableDriversOnMap();
              break;

验证效果

修改后,bearingLat和bearingLon会保留上一次的坐标值,调用Geolocator.bearingBetween时能正确计算新旧坐标的方位角,日志中会显示不同的新旧坐标值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 16:46:09