amap_flutter_location.dart 10.1 KB
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'amap_location_option.dart';

/// 高德定位Flutter插件入口类
/// 提供定位功能的主要接口,包括开始定位、停止定位、设置参数等功能
class AMapFlutterLocation {
  // 定义与原生平台交互的通道名称
  /// 方法通道名称,用于调用原生方法
  static const String _CHANNEL_METHOD_LOCATION = "csp_amap_flutter_location";

  /// 事件通道名称,用于接收原生端的定位结果
  static const String _CHANNEL_STREAM_LOCATION =
      "csp_amap_flutter_location_stream";

  /// 方法通道,用于调用原生方法
  static const MethodChannel _methodChannel =
      MethodChannel(_CHANNEL_METHOD_LOCATION);

  /// 事件通道,用于接收原生端的定位结果流
  static const EventChannel _eventChannel =
      EventChannel(_CHANNEL_STREAM_LOCATION);

  /// 定位结果的广播流,将原生端的定位结果转换为Map类型
  static final Stream<Map<String, Object>> _onLocationChanged = _eventChannel
      .receiveBroadcastStream()
      .asBroadcastStream()
      .map<Map<String, Object>>((element) => element.cast<String, Object>());

  /// 逆地理位置编码结果的广播流,将原生端的逆地理位置编码结果转换为Map类型
  static final Stream<Map<String, Object>> _onGeocodeSearchChanged =
      _eventChannel
          .receiveBroadcastStream()
          .asBroadcastStream()
          .map<Map<String, Object>>(
              (element) => element.cast<String, Object>());

  /// 定位结果的流控制器
  StreamController<Map<String, Object>>? _receiveStream;

  /// 逆地理位置编码结果的流控制器
  StreamController<Map<String, Object>>? _receiveGeocodeSearchStream;

  /// 定位结果的流订阅
  StreamSubscription<Map<String, Object>>? _subscription;

  /// 逆地理位置编码结果的流订阅
  StreamSubscription<Map<String, Object>>? _subscriptionGeocodeSearch;

  /// 插件实例的唯一标识符
  String? _pluginKey;

  /// 适配iOS 14定位新特性,获取系统定位精度授权状态
  /// 只在iOS平台有效
  /// 返回:定位精度授权状态枚举值
  Future<AMapAccuracyAuthorization> getSystemAccuracyAuthorization() async {
    int result = -1;
    if (Platform.isIOS) {
      result = await _methodChannel.invokeMethod(
          "getSystemAccuracyAuthorization", {'pluginKey': _pluginKey});
    }
    if (result == 0) {
      return AMapAccuracyAuthorization.AMapAccuracyAuthorizationFullAccuracy;
    } else if (result == 1) {
      return AMapAccuracyAuthorization.AMapAccuracyAuthorizationReducedAccuracy;
    }
    return AMapAccuracyAuthorization.AMapAccuracyAuthorizationInvalid;
  }

  /// 初始化定位插件
  /// 创建唯一的插件标识符
  AMapFlutterLocation() {
    _pluginKey = DateTime.now().millisecondsSinceEpoch.toString();
  }

  /// 开始定位
  /// 调用原生端开始定位的方法
  void startLocation() {
    _methodChannel.invokeMethod('startLocation', {'pluginKey': _pluginKey});
    return;
  }

  /// 开始逆地理位置编码
  /// 调用原生端开始定位的方法
  Future<String> onGeocodeSearch(bool once, List<num> points) async {
    String result = await _methodChannel.invokeMethod('onGeocodeSearch',
        {'pluginKey': _pluginKey, 'points': points, 'once': once});
    return result;
  }


  

  /// 圆形搜索
  Future<List<dynamic>>poiSearchAround(String keyword,double longitude,double latitude) async {
   List<dynamic> result = await _methodChannel.invokeMethod('poiSearchAround',
        {'pluginKey': _pluginKey, 'keyword': keyword, 'longitude': longitude,'latitude':latitude});
    return result;
  }



  
    /// 输入提示

  Future<List<dynamic>> doInputtipsRequest(String keyword, {String city=''}) async {
    List<dynamic> result = await _methodChannel.invokeMethod('doInputtipsRequest',
        {'pluginKey': _pluginKey, 'keyword': keyword, 'city': city});
    return result;
  }

  /// 停止定位
  /// 调用原生端停止定位的方法
  void stopLocation() {
    _methodChannel.invokeMethod('stopLocation', {'pluginKey': _pluginKey});
    return;
  }

  /// 设置Android和iOS的apikey
  /// 建议在widget初始化时设置
  /// apiKey的申请请参考高德开放平台官网
  /// @param androidKey Android平台的key
  /// @param iosKey iOS平台的key
  /// @param ohosKey 鸿蒙平台的key(可选)
  static void setApiKey(String androidKey, String iosKey, {String? ohosKey}) {
    _methodChannel.invokeMethod(
        'setApiKey', {'android': androidKey, 'ios': iosKey, 'ohos': ohosKey});
  }

  /// 设置定位参数
  /// @param locationOption 定位参数对象
  void setLocationOption(AMapLocationOption locationOption) {
    Map option = locationOption.getOptionsMap();
    option['pluginKey'] = _pluginKey;
    _methodChannel.invokeMethod('setLocationOption', option);
  }

  /// 销毁定位
  /// 释放定位资源,取消流订阅
  void destroy() {
    _methodChannel.invokeListMethod('destroy', {'pluginKey': _pluginKey});
    if (_subscription != null) {
      _receiveStream?.close();
      _subscription?.cancel();
      _receiveStream = null;

      _subscription = null;
    }
    if (_subscriptionGeocodeSearch != null) {
      _receiveGeocodeSearchStream?.close();
      _subscriptionGeocodeSearch?.cancel();
      _receiveGeocodeSearchStream = null;
      _subscriptionGeocodeSearch = null;
    }
  }

  /// 定位结果回调
  ///
  /// 定位结果以map的形式透出,其中包含的key已经含义如下:
  ///
  /// `callbackTime`:回调时间,格式为"yyyy-MM-dd HH:mm:ss"
  ///
  /// `locationTime`:定位时间, 格式为"yyyy-MM-dd HH:mm:ss"
  ///
  /// `locationType`:  定位类型, 具体类型可以参考https://lbs.amap.com/api/android-location-sdk/guide/utilities/location-type
  ///
  /// `latitude`:纬度
  ///
  /// `longitude`:精度
  ///
  /// `accuracy`:精确度
  ///
  /// `altitude`:海拔, android上只有locationType==1时才会有值
  ///
  /// `bearing`: 角度,android上只有locationType==1时才会有值
  ///
  /// `speed`:速度, android上只有locationType==1时才会有值
  ///
  /// `country`: 国家,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `province`: 省,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `city`: 城市,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `district`: 城镇(区),android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `street`: 街道,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `streetNumber`: 门牌号,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `cityCode`: 城市编码,android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `adCode`: 区域编码, android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `address`: 地址信息, android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `description`: 位置语义, android上只有通过[AMapLocationOption.needAddress]为true时才有可能返回值
  ///
  /// `errorCode`: 错误码,当定位失败时才会返回对应的错误码, 具体错误请参考:https://lbs.amap.com/api/android-location-sdk/guide/utilities/errorcode
  ///
  /// `errorInfo`: 错误信息, 当定位失败时才会返回
  Stream<Map<String, Object>> onLocationChanged() {
    if (_receiveStream == null) {
      _receiveStream = StreamController();
      _subscription = _onLocationChanged.listen((Map<String, Object> event) {
        if (event['pluginKey'] == _pluginKey) {
          try {
            Map<String, Object> newEvent = Map<String, Object>.of(event);
            newEvent.remove('pluginKey');
            _receiveStream?.add(newEvent);
          } catch (e) {
            print("注册定位结果监听 error : $e");
          }
        }
        // _receiveStream?.add(event);
      });
    }
    return _receiveStream!.stream;
  }

  ///逆地理位置编码结果回调

  Stream<Map<String, Object>> onGeocodeSearchChange() {
    if (_receiveGeocodeSearchStream == null) {
      _receiveGeocodeSearchStream = StreamController();
      _subscription =
          _onGeocodeSearchChanged.listen((Map<String, Object> event) {
        if (event['pluginKey'] == _pluginKey) {
          try {
            Map<String, Object> newEvent = Map<String, Object>.of(event);
            newEvent.remove('pluginKey');
            _receiveGeocodeSearchStream?.add(newEvent);
          } catch (e) {
            print("注册定位结果监听 error : $e");
          }
        }
        // _receiveStream?.add(event);
      });
    }
    return _receiveGeocodeSearchStream!.stream;
  }

  /// 设置是否已经包含高德隐私政策并弹窗展示显示用户查看
  /// 如果未包含或者没有弹窗展示,高德定位SDK将不会工作
  /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy
  /// 必须保证在调用定位功能之前调用,建议首次启动App时弹出《隐私政策》并取得用户同意
  /// @param hasContains 隐私声明中是否包含高德隐私政策说明
  /// @param hasShow 隐私权政策是否弹窗展示告知用户
  static void updatePrivacyShow(bool hasContains, bool hasShow) {
    _methodChannel.invokeMethod('updatePrivacyStatement',
        {'hasContains': hasContains, 'hasShow': hasShow});
  }

  /// 设置是否已经取得用户同意
  /// 如果未取得用户同意,高德定位SDK将不会工作
  /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy
  /// 必须保证在调用定位功能之前调用,建议首次启动App时弹出《隐私政策》并取得用户同意
  /// @param hasAgree 隐私权政策是否已经取得用户同意
  static void updatePrivacyAgree(bool hasAgree) {
    _methodChannel
        .invokeMethod('updatePrivacyStatement', {'hasAgree': hasAgree});
  }
}