本篇为flutter 项目集成高德地图flutter插件,话不多说,直接上代码
iOS 端
创建账号
创建高德账号流程我忘记截图,也就不放了,十分简单,我的 demo 测试是个人账号,手机号注册,然后绑定下支付宝即可,企业账号依据流程注册
iOS 获取 key 值流程十分简单(安卓注册 key 幺蛾子就多起来了,后面再说)
导入包
iOS 使用 map 和定位不需要在原生端导入,直接在 flutter中 pubspec.yaml中引入即可
# 高德地图
amap_flutter_map: ^3.0.0
# 定位
amap_flutter_location: ^3.0.0
但是我要注意提醒你们的是,这样安装的高德SDK 是包含 IDFA 的,不过说实话避免使用 idfa真的是太累人了,有太多SDK其实都用 idfa 了,OpenInstall,友盟,bugly,等等,我的建议是,还不如直接就声明自己的 APP 使用了 idfa,这玩意儿现在习惯了我感觉用户其实没啥反感的,因为几乎每个 APP 都会弹这个请求弹窗.
配置权限
Xcode 打开iOS 下Runner.xcworkspace,右键info.plist选择
把下面的内容按自己的需求粘进去即可,文案什么的,越详细越好,说清楚自己拿这个权限想干啥,被拒过的人都懂
<key>NSCameraUsageDescription</key>
<string>请点击“好”以允许访问。若不允许,你将无法使用相机来拍照和视频</string>
<key>NSLocalNetworkUsageDescription</key>
<string>此 APP 不会连接到您所用网络上的设备,只会检测与您本地网关的连通性</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>请点击“允许”以允许访问。 若不允许,你所在的城市将无法出现在你的动态、个人主页。</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>请点击“允许”以允许访问。 若不允许,你所在的城市将无法出现在你的动态、个人主页。</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>请点击“允许”以允许访问。 若不允许,你所在的城市将无法出现在你的动态、个人主页。</string>
<key>NSMicrophoneUsageDescription</key>
<string>请点击“好”以允许访问。若不允许,你将无法使用麦克风录制发送语音</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>请点击“好”以允许访问。若不允许,你将无法使用无法使用相册照片发送消息、上传头像和发布动态。</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>请点击“好”以允许访问。若不允许,你将无法使用无法使用相册照片发送消息、上传头像和发布动态。</string>
<key>UILaunchStoryboardName</key>
<key>NSUserTrackingUsageDescription</key>
<string>请放心, 爱泡炸无法获取你在应用内的隐私,该权限仅用于标识设备以保障服务安全与提升浏览体验</string>
业务代码
业务代码我没有进行封装,直接在作为测试 demo 展示,也没做什么动画的渐变啥的
import 'package:amap_flutter_map/amap_flutter_map.dart';
import 'package:amap_flutter_base/amap_flutter_base.dart';
import 'package:amap_flutter_location/amap_flutter_location.dart';
import 'package:amap_flutter_location/amap_location_option.dart';
import 'package:flutter/material.dart';
import 'package:flutter_psd/common/utils/psdllog.dart';
import 'package:permission_handler/permission_handler.dart';
class GaodeMapPage extends StatefulWidget {
const GaodeMapPage({Key? key}) : super(key: key);
@override
_GaodeMapPageState createState() => _GaodeMapPageState();
}
class _GaodeMapPageState extends State<GaodeMapPage> {
static const AMapApiKey amapApiKeys = AMapApiKey(
iosKey: '9af9e11da0573c280a514f2f6372df5f',
androidKey: 'eb909b4ee863e2bd0aa18098b7acc9ba');
AMapController? mapController;
AMapFlutterLocation? location;
PermissionStatus? permissionStatus;
CameraPosition? currentLocation;
@override
void initState() {
super.initState();
AMapFlutterLocation.setApiKey(
"eb909b4ee863e2bd0aa18098b7acc9ba", "9af9e11da0573c280a514f2f6372df5f");
AMapFlutterLocation.updatePrivacyAgree(true);
AMapFlutterLocation.updatePrivacyShow(true, true);
requestPermission();
}
Future<void> requestPermission() async {
final status = await Permission.location.request();
permissionStatus = status;
switch (status) {
case PermissionStatus.denied:
psdllog("拒绝");
break;
case PermissionStatus.granted:
requestLocation();
break;
case PermissionStatus.limited:
psdllog("限制");
break;
default:
psdllog("其他状态");
requestLocation();
break;
}
}
void requestLocation() {
location = AMapFlutterLocation()
..setLocationOption(AMapLocationOption())
..onLocationChanged().listen((event) {
psdllog(event);
double? latitude = double.parse(event['latitude'] as String);
double? longitude = double.parse(event['longitude'] as String);
if (latitude != null && longitude != null) {
setState(() {
currentLocation = CameraPosition(
target: LatLng(latitude, longitude),
zoom: 10,
);
});
}
})
..startLocation();
}
@override
void dispose() {
location?.destroy();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"高德地图",
style: TextStyle(),
),
),
body: currentLocation == null
? Container()
: Center(
child: SizedBox(
child: AMapWidget(
apiKey: amapApiKeys,
// 初始化地图中心店
initialCameraPosition: currentLocation!,
//定位小蓝点
myLocationStyleOptions: MyLocationStyleOptions(
true,
),
// 普通地图normal,卫星地图satellite,夜间视图night,导航视图 navi,公交视图bus,
mapType: MapType.normal,
// 缩放级别范围
minMaxZoomPreference: MinMaxZoomPreference(3, 20),
// 隐私政策包含高德 必须填写
privacyStatement: AMapPrivacyStatement(
hasAgree: true, hasContains: true, hasShow: true),
// 地图创建成功时返回AMapController
onMapCreated: (AMapController controller) {
mapController = controller;
},
),
)),
);
}
}
代码在上面,然后我讲一些 iOS权限错误还有和Android 通用的高德错误.
- 1.iOS双定位权限
[MAMapKit] 要在iOS 11及以上版本使用定位服务, 需要在Info.plist中添加NSLocationAlwaysAndWhenInUseUsageDescription和NSLocationWhenInUseUsageDescription字段。
这是两个权限说明必须要全部配置,顺便提一嘴,iOS 隐私权限不管你在哪用,进入APP 第一时间就要弹出,定位权限没有这个要求.
- permission_handler报错
* What went wrong:
Execution failed for task ':permission_handler:compileDebugJavaWithJavac'.
一开始以为是 flutter 版本问题(今天测试 demo的时候,看到有人说2.10.0版本的 flutter 有个系统 bug),又看到有篇 stackoverflow 说升级版本就能解决这个 bug,所以升级到了2.10.1,然而还是没有解决.于是就去翻permission_handler的 issue,终于找到了,很幸运的是
permission_handler的作者刚刚好在昨天解释了这个 bug地址戳这
我当时使用的版本是7.* 升级后我就解决了这个问题
代码说明
高德 flutter 文档地址: 戳这儿
iOSbug少,基本按照文档来即可,而且由于是中文文档,看起来一点也不费劲,这边建议先获取到定位后在展示地图,这样,直接能够显示到中心的小蓝点.
// 隐私政策包含高德 必须填写
privacyStatement: AMapPrivacyStatement(hasAgree: true, hasContains: true, hasShow: true),
定位的隐私政策也会一样,不然直接拋异常,高德希望你在隐私网址中提到高德地图的使用,正确合规的做法是在同意隐私政策的时候设置权限为 true
AMapFlutterLocation..updatePrivacyAgree(true)..updatePrivacyShow(true, true);
原生里的 key优先级小于 Widget 代码中.
定位中获取的参数map 经纬度为 string.
{locTime: 2022-02-11 21:51:03, province: 浙江省, callbackTime: 2022-02-11 21:51:03, district: 西湖区, country: 中国, street: ****, speed: -1.0, latitude: 30.287363, city: 杭州市, streetNumber: 177-1号, bearing: -1.0, accuracy: 35.0, adCode: 330106, altitude: 11.046348571777344, locationType: 1, longitude: 120.128650, cityCode: 0571, address: 浙江省杭州市西湖区*****, description: 浙江省杭州市西湖区*****}
高德的 flutterAPI 接口提供的确实很少,甚至感觉有点简陋,其实这东西如果以后有精力的话,可以尝试自己封装一下,当然安卓端我搞起来确实难度有点大,主要是要做好公共接口,做好channel 通信,剩下的就是各端的小伙伴去努力实现了,顺便可以练练插件.高德源码我也看了下,通道目前做的还不是太多,希望后面能丰富起来.
AMapController
效果展示
安卓端
安卓端配置真的是一言难尽,iOS 的过度顺利导致我以为安卓端的兼容也十分简单,我感觉周五一下午的时间全用来 碰 bug 上了,下面叙述下我的血泪史
首先尝试安卓模拟器运行项目
-
运行,报错
跟着改成31
-
再运行,再报错,kotlin gradle 插件版本有问题
修改根build.gradle
buildscript {
ext.kotlin_version = '1.6.10'
*****
}
运行正常,进入地图界面的时候拋异常
- java.lang.NoClassDefFoundError: Failed resolution of: Lcom/amap/api/location/AMapLocationClient;
很明显,连类都找不到,一番查询后,得知Android 需要手动导入 sdk
在官方文档中,我采用手动拖入sdk+so
- 再运行,再报错errorCode: 10 *******请检查AndroidManifest.xml文件是否配置了APSService定位服务
AndroidManifest.xml增加
<service android:name="com.amap.api.location.APSService"></service>
- APSService标红
网上查询到app/build.gradle 下增加
dependencies {
// implementation files('src/libs/AMap3DMap_AMapLocation/AMap3DMap_9.0.0_AMapLocation_5.6.2_20220113.jar')
implementation('com.amap.api:location:5.2.0')
}
这里我一开始查到是手动右键将 jar 包增加到library
但是报了一个什么错我忘记记录了
然后我查了下,查到下面的写法
implementation('com.amap.api:location:5.2.0')
目前不知道这两者的区别再运行,在报错
-
errorCode: 12, errorInfo: 缺少定位权限 请到http://lbs.amap.com/api/android-location-sdk/guide/utilities/errorcode/查看错误码说明,错误详细信息:定位权限被禁用,请授予应用定位权限#1201#定位权限被禁用,请授予应用定位权限#1201
-
errorInfo: WIFI信息不足 请到http://lbs.amap.com/api/android-location-sdk/guide/utilities/errorcode/查看错误码说明,错误详细信息:当前基站为伪基站,并且搜到的WIFI数量不足,请移动到WIFI比较丰富的区域#0202#当前基站为伪基站,并且搜到的WIFI数量不足,请移动到WIFI比较丰富的区域#0202}
这两个我后面换安卓真机运行就消失了,应该是项目重启,或者 APP 卸载,或者 flutter clean , gradle clean 等等,上面的其他几个 bug 我有些有尝试过清缓存解决,也许没提到,但是大家心里要有个印象
再运行,再报错 -
Flutter multidex handling is disabled. If you wish to let the tool configure multidex, use the --mutidex flag.
这个我请了清缓存,重启下就好了 -
再运行,再报错
其实到这儿,我心理已经大概有点底了,项目可以正常运行,只是定位无法提供,这里设计到 Android key 的创建,这里的出错应该是我没配好,其实最开始应该要写下如何创建 Android 的高德 key 的,后面补上,文章先写到这,明儿再找时间调调,然后把文章更新下,应该只需要亿点点时间.(主要是 bug 太多,再不写我就忘记我遇到过哪些 bug 了)
2022-02-12 09:57:09 更新 Android真机高德地图定位成功
昨天有个 bug 是鉴权失败导致定位失败
我按照昨天最后高德给出的鉴权失败流程一点点尝试,并没有解决,我怀疑我获取的 key 并不是我注册的 key,今天尝试用另外一种方法解决
在创建 debug.storekey 成功后, 创建 key 的方法戳这,我采用高德提供的获取 key 方式获取cd ~/.android
然后将该 SHA1更新到高德 key 中
最后鉴权成功.
再运行,成功获取到坐标
然而地图黑屏了,仅显示左下角一个 logo, 控制台打印报错
查了很多资料,最后怀疑是 so 文件导入出错,高德给了两种导入方法
在下载 sdk 的时候,高德附带了各个平台的 so 文件
我直接使用第一种方法,可能我导入的确实有点问题,并没有成功,于是我改成第二种方法
顺便提一下,对于 jar 包的引入,我还是改成了本地 jar 包
这是因为在尝试网上给出的implementation('com.amap.api:location:5.2.0')
后,我发现报了一个找不到 MapView 啥的实现方法,推断出'com.amap.api:location:5.2.0'
只能实现高德location库的方法,我想着还是用本地的 jar 包才对,要不然放那就是白导入了.
运行报错如果发现
可以尝试增加
效果展示
最后的最后,安卓真机地图的导入才算是结束了,最后展示下效果吧
PS
这次的安卓高德地图的配置,感觉基本把能踩的全踩了个遍,也是因为自己对安卓系统不太熟,平时也常用 iOS 真机或者模拟器测,也基本没啥问题,昨天也痛定思痛,以后再也不搞 iOS 模拟器了,下了个 momo,以后全部用安卓测代码,
顺便贴个momo模拟器 配置
system_profiler SPUSBDataType
复制ID/Vender ID到/.android/adb_usb.ini
adb kill-server
adb start-server
adb devices
网友评论