Flutter之百度定位SDK集成使用

2,443 阅读4分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

本篇文章主要讲解在 Flutter 中如何集成百度定位 SDK 定位获取当前位置信息。

百度官网提供了定位的 Flutter SDK,但是在集成过程中可能会存在很多坑,本文将详细介绍如何一步一步集成百度定位 SDK 实现定位功能。

添加依赖

在项目的 pubspec.yaml 中添加百度定位 sdk 库 flutter_bmflocation ,最新版本为 2.0.0 且已支持空安全,如下:

dependencies:
    flutter_bmflocation: ^2.0.0-nullsafety.1

配置

百度控制台创建应用

要使用百度定位 SDK,需先在百度百度地图开放平台控制台创建对应平台的应用:

Android:

image-20220124145238097.png

如上图,填写应用名称,应用类型选择 Android SDK,然后选择需要用到的服务,最后填写应用的签名 SHA1 指纹以及包名点击提交即可。

iOS:

image-20220124145520403.png

如上图,填写应用名称,应用类型选择 iOS SDK,选择需要用到的服务,最后填写应用安全码(Bundle Identifier)点击提交即可。

创建应用后即可在应用列表中获取应用的 AK :

iShot2022-01-24 14.22.12.png

配置 AK

Android

在项目的 android/app/src/main/AndroidManifest.xmlapplication 中添加 meta-data 配置应用的 AK, 如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.loongwind.core.flutter_app_core">
   <application
        android:label="flutter_app_core"
        android:icon="@mipmap/ic_launcher">
       <...>
       <meta-data
           android:name="com.baidu.lbsapi.API_KEY"
           android:value="2o77w1HNochxxxxxxxxxxxxmjNTmFulAYl" />
    </application>
</manifest>

iOS

iOS 配置 AK 可直接在 Flutter 中调用 setApiKey 进行设置, 如下:

if(Platform.isIOS){ 
	LocationFlutterPlugin.setApiKey("GfhocIpWb6uxxxxxxxxxxfpVgtgFwIOg2");
}

iOS 配置

iOS 集成百度 SDK 还需进行如下配置:

  • 权限配置

在项目的 Info.plist 添加定位权限申请, 如图添加 Privacy - Location When In Use Usage Description 并在 Value 填写使用该定位权限的描述。

image-20220124151254022.png

在项目的 Info.plist 添加 App Transport Security Settings 字段,并在其下添加 Allow Arbitrary Loads 项并将值设置为 YES , 如图:

image-20220124151955445.png

  • 环境配置

TARGETS->Build Settings-> Linking -> Other Linker Flags 中添加 -ObjC

image-20220124151658951.png

  • PodFile 配置

因为百度地图定位 SDK 使用到了 permission_handler 库用于权限获取,所以需要在项目的 ios/Podfile 中添加 permission_handler 的定位权限配置。

PodFilepost_install 下添加 permission_handler 的权限配置:

post_install do |installer|
  installer.pods_project.targets.each do |target|
   	target.build_configurations.each do |config|
          config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
                    config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
                      '$(inherited)',

                      ## dart: PermissionGroup.calendar
                      # 'PERMISSION_EVENTS=1',

                      ## dart: PermissionGroup.reminders
                      # 'PERMISSION_REMINDERS=1',

                      ## dart: PermissionGroup.contacts
                      # 'PERMISSION_CONTACTS=1',

                      ## dart: PermissionGroup.camera
                      # 'PERMISSION_CAMERA=1',

                      ## dart: PermissionGroup.microphone
                      # 'PERMISSION_MICROPHONE=1',

                      ## dart: PermissionGroup.speech
                      # 'PERMISSION_SPEECH_RECOGNIZER=1',

                      ## dart: PermissionGroup.photos
                      # 'PERMISSION_PHOTOS=1',

                      ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
                      'PERMISSION_LOCATION=1',

                      ## dart: PermissionGroup.notification
                      # 'PERMISSION_NOTIFICATIONS=1',

                      ## dart: PermissionGroup.mediaLibrary
                      # 'PERMISSION_MEDIA_LIBRARY=1',

                      ## dart: PermissionGroup.sensors
                      # 'PERMISSION_SENSORS=0'
                    ]
      end
    flutter_additional_ios_build_settings(target)
  end
end

因为这里只需要定位的权限,所以将 PERMISSION_LOCATION 的注释放开即可。

关于 permission_handler 的具体使用请查阅官方文档 permission_handler

定位

经过上面的配置,就可以在 Flutter 中使用百度定位 SDK 进行定位了。创建一个 LocationService 类,用于封装定位相关代码。

导入定位相关类文件

import 'package:flutter_bmflocation/bdmap_location_flutter_plugin.dart';
import 'package:flutter_bmflocation/flutter_baidu_location.dart';
import 'package:flutter_bmflocation/flutter_baidu_location_android_option.dart';
import 'package:flutter_bmflocation/flutter_baidu_location_ios_option.dart';

初始化

/// 创建插件对象
final LocationFlutterPlugin _locationPlugin = LocationFlutterPlugin();

/// 初始化
void init(){
  if(Platform.isIOS){
    /// 设置 iOS 应用的 AK
    LocationFlutterPlugin.setApiKey("GfhocIpWb6uxxxxxxxxxxfpVgtgFwIOg2");
  }
}

设置定位参数

/// 设置android端和ios端定位参数
void _setLocOption() {
  /// android 端设置定位参数
  BaiduLocationAndroidOption androidOption = BaiduLocationAndroidOption();
  
  /// 可选,设置返回经纬度坐标类型,默认gcj02
  /// gcj02:国测局坐标;
  /// bd09ll:百度经纬度坐标;
  /// bd09:百度墨卡托坐标;
  /// 海外地区定位,无需设置坐标类型,统一返回wgs84类型坐标
  androidOption.setCoorType("bd09ll"); 
  androidOption.setIsNeedAltitude(true); /// 设置是否需要返回海拔高度信息
  androidOption.setIsNeedAddres(true); /// 设置是否需要返回地址信息
  androidOption.setIsNeedLocationPoiList(true); /// 设置是否需要返回周边poi信息
  androidOption.setIsNeedNewVersionRgc(true); /// 设置是否需要返回最新版本rgc信息
  androidOption.setIsNeedLocationDescribe(true); /// 设置是否需要返回位置描述
  androidOption.setOpenGps(true); /// 设置是否需要使用gps
  
  /// 可选,设置定位模式,可选的模式有高精度、低功耗、仅设备,默认为高精度模式,可选值如下:
  /// 高精度模式: LocationMode.Hight_Accuracy
  /// 低功耗模式:LocationMode.Battery_Saving
  /// 仅设备(Gps)模式:LocationMode.Device_Sensors
  androidOption.setLocationMode(LocationMode.Hight_Accuracy); 
  
  /// 可选,设置发起定位请求的间隔,int类型,单位ms
  /// 如果设置为0,则代表单次定位,即仅定位一次,默认为0
  /// 如果设置非0,需设置1000ms以上才有效
  androidOption.setScanspan(1000); 
  
  /// 可选,设置场景定位参数,包括签到场景、运动场景、出行场景,可选值如下:
  /// 签到场景: BDLocationPurpose.SignIn
  /// 运动场景: BDLocationPurpose.Transport
  /// 出行场景: BDLocationPurpose.Sport
  androidOption.setLocationPurporse(BDLocationPurpose.SignIn);


  Map androidMap = androidOption.getMap();

  /// ios 端设置定位参数
  BaiduLocationIOSOption iosOption = new BaiduLocationIOSOption();
  iosOption.setIsNeedNewVersionRgc(true); /// 设置是否需要返回最新版本rgc信息
  
  ///  可选,设置返回位置的坐标系类型 ,参数为String类型,可选值如下:
  /// "BMKLocationCoordinateTypeBMK09LL" 百度经纬度坐标
  /// "BMKLocationCoordinateTypeBMK09MC" 百度墨卡托坐标
  /// "BMKLocationCoordinateTypeWGS84"  gps坐标
  /// "BMKLocationCoordinateTypeGCJ02" 国测局坐标
  iosOption.setBMKLocationCoordinateType("BMKLocationCoordinateTypeBMK09LL"); 
  
  /// 可选,设置应用位置类型,参数为String类型,可选值如下:
  /// "CLActivityTypeOther"
  /// "CLActivityTypeAutomotiveNavigation"
  /// "CLActivityTypeFitness"
  /// "CLActivityTypeOtherNavigation"
  iosOption.setActivityType("CLActivityTypeAutomotiveNavigation"); 
  iosOption.setLocationTimeout(10); /// 设置位置获取超时时间
  
  /// 可选,设置期望定位精度,参数为String类型,可选值如下:
  /// "kCLLocationAccuracyBest"
  /// "kCLLocationAccuracyNearestTenMeters"
  /// "kCLLocationAccuracyHundredMeters"
  /// "kCLLocationAccuracyKilometer"
  iosOption.setDesiredAccuracy("kCLLocationAccuracyBest");  
  iosOption.setLocationTimeout(10); /// 可选,设置位置获取超时时间,参数为int类型
  iosOption.setReGeocodeTimeout(10); /// 设置获取地址信息超时时间
  iosOption.setDistanceFilter(100); /// 设置定位最小更新距离
  iosOption.setAllowsBackgroundLocationUpdates(true); // 是否允许后台定位
  iosOption.setPauseLocUpdateAutomatically(false); ///  定位是否会被系统自动暂停
  iosOption.setDistanceFilter(10); /// 设置定位的最小更新距离,参数为double类型


  Map iosMap = iosOption.getMap();

  _locationPlugin.prepareLoc(androidMap, iosMap);
}

定位

Future<BaiduLocation?> location() async {
  /// 动态申请定位权限
  _locationPlugin.requestPermission();
  
  Completer<BaiduLocation> completer = Completer();
  
  ///定位回调
  _locationPlugin.onResultCallback().listen((Map<String, Object>? result) {
    try {
      /// 获取定位结果
      BaiduLocation baiduLocation = BaiduLocation.fromMap(result);
      
     	///返回结果
      completer.complete(baiduLocation);
    } catch (e) {
      print(e);
      completer.completeError("location fail");
    }
    _stopLocation();
  }).onError((e) {
    completer.completeError("location fail");
  });
  _startLocation();
  return completer.future;
}

/// 启动定位
void _startLocation() {
  ///设置定位参数
  _setLocOption();
  _locationPlugin.startLocation();
}

/// 停止定位
void _stopLocation() {
  _locationPlugin.stopLocation();
}

使用

void location() async{
    LocationService locationService = LocationService();
    locationService.init();

    BaiduLocation? location = await locationService.location();
    print(""
        "纬度 : ${location?.latitude}  \n"
        "经度 : ${location?.longitude}  \n"
        "地址 : ${location?.address}\n"
        "位置语义化描述 : ${location?.locationDetail}\n"
        "国家 : ${location?.country}\n"
        "省份 : ${location?.province}\n"
        "城市 : ${location?.city}\n"
        "区县 : ${location?.district}\n"
        "街道 : ${location?.street}\n"
        "周边poi信息 : ${location?.poiList}\n"
    );
  }

分别在 Android 和 iOS 的设备上测试,输出结果如下:

//----------android-----------
纬度 : 39.91930
经度 : 116.4180582
地址 : addr = 北京市东城区王府井大街168-6层
位置语义化描述 : 在东安市场附近
国家 : 中国
省份 : 北京市
城市 : 北京市
区县 : 东城区
街道 : 王府井大街
周边poi信息 : 东安市场,购物;购物中心,北京市东城区王府井大街138号|apm,购物;购物中心,北京市东城区王府井大街138号|王府井商业街,购物;其他,北京市东城区东长安街北侧|北京新中国儿童用品商店(王府井大街店),购物;购物中心,北京市东城区王府井大街138号|王府井百货办公大楼,房地产;写字楼,北京市东城区王府井大街255//------------iOS-------------
纬度 : 39.919304161081385  
经度:116.41805829214785  
地址 : addr = 110101 | 0 | 中国 | 北京市 | 131 | 东华门街道  | 王府井大街 | 168-6层 | 东城区 | 北京市 | 在东安市场附近 |
位置语义化描述 : null
国家 : 中国
省份 : 北京市
城市 : 北京市
区县 : 东城区
街道 : 王府井大街
周边poi信息 : 东安市场,购物;购物中心,北京市东城区王府井大街138号|apm,购物;购物中心,北京市东城区王府井大街138号|王府井商业街,购物;其他,北京市东城区东长安街北侧|北京新中国儿童用品商店(王府井大街店),购物;购物中心,北京市东城区王府井大街138号|王府井百货办公大楼,房地产;写字楼,北京市东城区王府井大街255

通过输出结果可以看到,在 Android 和 iOS 上都定位成功了,但是返回的数据结构有所区别:

  • Android 的 addr 返回的是格式化的地址,iOS 返回的是国家省市区等用 | 分割的数据
  • Android 有返回语义化描述字段 locationDetail , 而 iOS 没有该字段值

至此就完成了 Flutter 集成百度定位 SDK 的使用了。