本篇為flutter 項(xiàng)目集成高德地圖flutter插件,話不多說,直接上代碼
iOS 端
創(chuàng)建賬號(hào)
創(chuàng)建高德賬號(hào)流程我忘記截圖,也就不放了,十分簡(jiǎn)單,我的 demo 測(cè)試是個(gè)人賬號(hào),手機(jī)號(hào)注冊(cè),然后綁定下支付寶即可,企業(yè)賬號(hào)依據(jù)流程注冊(cè)
iOS 獲取 key 值流程十分簡(jiǎn)單(安卓注冊(cè) key 幺蛾子就多起來了,后面再說)
導(dǎo)入包
iOS 使用 map 和定位不需要在原生端導(dǎo)入,直接在 flutter中 pubspec.yaml中引入即可
# 高德地圖
amap_flutter_map: ^3.0.0
# 定位
amap_flutter_location: ^3.0.0
但是我要注意提醒你們的是,這樣安裝的高德SDK 是包含 IDFA 的,不過說實(shí)話避免使用 idfa真的是太累人了,有太多SDK其實(shí)都用 idfa 了,OpenInstall,友盟,bugly,等等,我的建議是,還不如直接就聲明自己的 APP 使用了 idfa,這玩意兒現(xiàn)在習(xí)慣了我感覺用戶其實(shí)沒啥反感的,因?yàn)閹缀趺總€(gè) APP 都會(huì)彈這個(gè)請(qǐng)求彈窗.
配置權(quán)限
Xcode 打開iOS 下Runner.xcworkspace,右鍵info.plist選擇
把下面的內(nèi)容按自己的需求粘進(jìn)去即可,文案什么的,越詳細(xì)越好,說清楚自己拿這個(gè)權(quán)限想干啥,被拒過的人都懂
<key>NSCameraUsageDescription</key>
<string>請(qǐng)點(diǎn)擊“好”以允許訪問古戴。若不允許致燥,你將無(wú)法使用相機(jī)來拍照和視頻</string>
<key>NSLocalNetworkUsageDescription</key>
<string>此 APP 不會(huì)連接到您所用網(wǎng)絡(luò)上的設(shè)備琳彩,只會(huì)檢測(cè)與您本地網(wǎng)關(guān)的連通性</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>請(qǐng)點(diǎn)擊“允許”以允許訪問。 若不允許,你所在的城市將無(wú)法出現(xiàn)在你的動(dòng)態(tài)队他、個(gè)人主頁(yè)。</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>請(qǐng)點(diǎn)擊“允許”以允許訪問。 若不允許十绑,你所在的城市將無(wú)法出現(xiàn)在你的動(dòng)態(tài)、個(gè)人主頁(yè)酷勺。</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>請(qǐng)點(diǎn)擊“允許”以允許訪問本橙。 若不允許,你所在的城市將無(wú)法出現(xiàn)在你的動(dòng)態(tài)脆诉、個(gè)人主頁(yè)甚亭。</string>
<key>NSMicrophoneUsageDescription</key>
<string>請(qǐng)點(diǎn)擊“好”以允許訪問贷币。若不允許,你將無(wú)法使用麥克風(fēng)錄制發(fā)送語(yǔ)音</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>請(qǐng)點(diǎn)擊“好”以允許訪問亏狰。若不允許役纹,你將無(wú)法使用無(wú)法使用相冊(cè)照片發(fā)送消息、上傳頭像和發(fā)布動(dòng)態(tài)暇唾。</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>請(qǐng)點(diǎn)擊“好”以允許訪問促脉。若不允許,你將無(wú)法使用無(wú)法使用相冊(cè)照片發(fā)送消息策州、上傳頭像和發(fā)布動(dòng)態(tài)瘸味。</string>
<key>UILaunchStoryboardName</key>
<key>NSUserTrackingUsageDescription</key>
<string>請(qǐng)放心, 愛泡炸無(wú)法獲取你在應(yīng)用內(nèi)的隱私,該權(quán)限僅用于標(biāo)識(shí)設(shè)備以保障服務(wù)安全與提升瀏覽體驗(yàn)</string>
業(yè)務(wù)代碼
業(yè)務(wù)代碼我沒有進(jìn)行封裝,直接在作為測(cè)試 demo 展示,也沒做什么動(dòng)畫的漸變啥的
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("其他狀態(tài)");
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!,
//定位小藍(lán)點(diǎn)
myLocationStyleOptions: MyLocationStyleOptions(
true,
),
// 普通地圖normal,衛(wèi)星地圖satellite,夜間視圖night,導(dǎo)航視圖 navi,公交視圖bus,
mapType: MapType.normal,
// 縮放級(jí)別范圍
minMaxZoomPreference: MinMaxZoomPreference(3, 20),
// 隱私政策包含高德 必須填寫
privacyStatement: AMapPrivacyStatement(
hasAgree: true, hasContains: true, hasShow: true),
// 地圖創(chuàng)建成功時(shí)返回AMapController
onMapCreated: (AMapController controller) {
mapController = controller;
},
),
)),
);
}
}
代碼在上面,然后我講一些 iOS權(quán)限錯(cuò)誤還有和Android 通用的高德錯(cuò)誤.
- 1.iOS雙定位權(quán)限
[MAMapKit] 要在iOS 11及以上版本使用定位服務(wù), 需要在Info.plist中添加NSLocationAlwaysAndWhenInUseUsageDescription和NSLocationWhenInUseUsageDescription字段。
這是兩個(gè)權(quán)限說明必須要全部配置,順便提一嘴,iOS 隱私權(quán)限不管你在哪用,進(jìn)入APP 第一時(shí)間就要彈出,定位權(quán)限沒有這個(gè)要求.
- permission_handler報(bào)錯(cuò)
* What went wrong:
Execution failed for task ':permission_handler:compileDebugJavaWithJavac'.
一開始以為是 flutter 版本問題(今天測(cè)試 demo的時(shí)候,看到有人說2.10.0版本的 flutter 有個(gè)系統(tǒng) bug),又看到有篇 stackoverflow 說升級(jí)版本就能解決這個(gè) bug,所以升級(jí)到了2.10.1,然而還是沒有解決.于是就去翻permission_handler的 issue,終于找到了,很幸運(yùn)的是
permission_handler的作者剛剛好在昨天解釋了這個(gè) bug地址戳這
我當(dāng)時(shí)使用的版本是7.* 升級(jí)后我就解決了這個(gè)問題
代碼說明
高德 flutter 文檔地址: 戳這兒
iOSbug少,基本按照文檔來即可,而且由于是中文文檔,看起來一點(diǎn)也不費(fèi)勁,這邊建議先獲取到定位后在展示地圖,這樣,直接能夠顯示到中心的小藍(lán)點(diǎn).
// 隱私政策包含高德 必須填寫
privacyStatement: AMapPrivacyStatement(hasAgree: true, hasContains: true, hasShow: true),
定位的隱私政策也會(huì)一樣,不然直接拋異常,高德希望你在隱私網(wǎng)址中提到高德地圖的使用,正確合規(guī)的做法是在同意隱私政策的時(shí)候設(shè)置權(quán)限為 true
AMapFlutterLocation..updatePrivacyAgree(true)..updatePrivacyShow(true, true);
原生里的 key優(yōu)先級(jí)小于 Widget 代碼中.
定位中獲取的參數(shù)map 經(jīng)緯度為 string.
{locTime: 2022-02-11 21:51:03, province: 浙江省, callbackTime: 2022-02-11 21:51:03, district: 西湖區(qū), country: 中國(guó), street: ****, speed: -1.0, latitude: 30.287363, city: 杭州市, streetNumber: 177-1號(hào), bearing: -1.0, accuracy: 35.0, adCode: 330106, altitude: 11.046348571777344, locationType: 1, longitude: 120.128650, cityCode: 0571, address: 浙江省杭州市西湖區(qū)*****, description: 浙江省杭州市西湖區(qū)*****}
高德的 flutterAPI 接口提供的確實(shí)很少,甚至感覺有點(diǎn)簡(jiǎn)陋,其實(shí)這東西如果以后有精力的話,可以嘗試自己封裝一下,當(dāng)然安卓端我搞起來確實(shí)難度有點(diǎn)大,主要是要做好公共接口,做好channel 通信,剩下的就是各端的小伙伴去努力實(shí)現(xiàn)了,順便可以練練插件.高德源碼我也看了下,通道目前做的還不是太多,希望后面能豐富起來.
效果展示
安卓端
安卓端配置真的是一言難盡,iOS 的過度順利導(dǎo)致我以為安卓端的兼容也十分簡(jiǎn)單,我感覺周五一下午的時(shí)間全用來 碰 bug 上了,下面敘述下我的血淚史
首先嘗試安卓模擬器運(yùn)行項(xiàng)目
-
運(yùn)行,報(bào)錯(cuò)
跟著改成31
-
再運(yùn)行,再報(bào)錯(cuò),kotlin gradle 插件版本有問題
修改根build.gradle
buildscript {
ext.kotlin_version = '1.6.10'
*****
}
運(yùn)行正常,進(jìn)入地圖界面的時(shí)候拋異常
- java.lang.NoClassDefFoundError: Failed resolution of: Lcom/amap/api/location/AMapLocationClient;
很明顯,連類都找不到,一番查詢后,得知Android 需要手動(dòng)導(dǎo)入 sdk
在官方文檔中,我采用手動(dòng)拖入sdk+so
- 再運(yùn)行,再報(bào)錯(cuò)errorCode: 10 *******請(qǐng)檢查AndroidManifest.xml文件是否配置了APSService定位服務(wù)
AndroidManifest.xml增加
<service android:name="com.amap.api.location.APSService"></service>
- APSService標(biāo)紅
網(wǎng)上查詢到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')
}
這里我一開始查到是手動(dòng)右鍵將 jar 包增加到library
但是報(bào)了一個(gè)什么錯(cuò)我忘記記錄了
然后我查了下,查到下面的寫法
implementation('com.amap.api:location:5.2.0')
目前不知道這兩者的區(qū)別再運(yùn)行,在報(bào)錯(cuò)
errorCode: 12, errorInfo: 缺少定位權(quán)限 請(qǐng)到http://lbs.amap.com/api/android-location-sdk/guide/utilities/errorcode/查看錯(cuò)誤碼說明,錯(cuò)誤詳細(xì)信息:定位權(quán)限被禁用,請(qǐng)授予應(yīng)用定位權(quán)限#1201#定位權(quán)限被禁用,請(qǐng)授予應(yīng)用定位權(quán)限#1201
errorInfo: WIFI信息不足 請(qǐng)到http://lbs.amap.com/api/android-location-sdk/guide/utilities/errorcode/查看錯(cuò)誤碼說明,錯(cuò)誤詳細(xì)信息:當(dāng)前基站為偽基站够挂,并且搜到的WIFI數(shù)量不足旁仿,請(qǐng)移動(dòng)到WIFI比較豐富的區(qū)域#0202#當(dāng)前基站為偽基站,并且搜到的WIFI數(shù)量不足孽糖,請(qǐng)移動(dòng)到WIFI比較豐富的區(qū)域#0202}
這兩個(gè)我后面換安卓真機(jī)運(yùn)行就消失了,應(yīng)該是項(xiàng)目重啟,或者 APP 卸載,或者 flutter clean , gradle clean 等等,上面的其他幾個(gè) bug 我有些有嘗試過清緩存解決,也許沒提到,但是大家心里要有個(gè)印象
再運(yùn)行,再報(bào)錯(cuò)Flutter multidex handling is disabled. If you wish to let the tool configure multidex, use the --mutidex flag.
這個(gè)我請(qǐng)了清緩存,重啟下就好了-
再運(yùn)行,再報(bào)錯(cuò)
其實(shí)到這兒,我心理已經(jīng)大概有點(diǎn)底了,項(xiàng)目可以正常運(yùn)行,只是定位無(wú)法提供,這里設(shè)計(jì)到 Android key 的創(chuàng)建,這里的出錯(cuò)應(yīng)該是我沒配好,其實(shí)最開始應(yīng)該要寫下如何創(chuàng)建 Android 的高德 key 的,后面補(bǔ)上,文章先寫到這,明兒再找時(shí)間調(diào)調(diào),然后把文章更新下,應(yīng)該只需要億點(diǎn)點(diǎn)時(shí)間.(主要是 bug 太多,再不寫我就忘記我遇到過哪些 bug 了)
2022-02-12 09:57:09 更新 Android真機(jī)高德地圖定位成功
昨天有個(gè) bug 是鑒權(quán)失敗導(dǎo)致定位失敗
我按照昨天最后高德給出的鑒權(quán)失敗流程一點(diǎn)點(diǎn)嘗試,并沒有解決,我懷疑我獲取的 key 并不是我注冊(cè)的 key,今天嘗試用另外一種方法解決
在創(chuàng)建 debug.storekey 成功后, 創(chuàng)建 key 的方法戳這,我采用高德提供的獲取 key 方式獲取cd ~/.android
然后將該 SHA1更新到高德 key 中
最后鑒權(quán)成功.
再運(yùn)行,成功獲取到坐標(biāo)
然而地圖黑屏了,僅顯示左下角一個(gè) logo, 控制臺(tái)打印報(bào)錯(cuò)
查了很多資料,最后懷疑是 so 文件導(dǎo)入出錯(cuò),高德給了兩種導(dǎo)入方法
在下載 sdk 的時(shí)候,高德附帶了各個(gè)平臺(tái)的 so 文件
我直接使用第一種方法,可能我導(dǎo)入的確實(shí)有點(diǎn)問題,并沒有成功,于是我改成第二種方法
順便提一下,對(duì)于 jar 包的引入,我還是改成了本地 jar 包
這是因?yàn)樵趪L試網(wǎng)上給出的implementation('com.amap.api:location:5.2.0')
后,我發(fā)現(xiàn)報(bào)了一個(gè)找不到 MapView 啥的實(shí)現(xiàn)方法,推斷出'com.amap.api:location:5.2.0'
只能實(shí)現(xiàn)高德location庫(kù)的方法,我想著還是用本地的 jar 包才對(duì),要不然放那就是白導(dǎo)入了.
運(yùn)行報(bào)錯(cuò)如果發(fā)現(xiàn)
可以嘗試增加
效果展示
最后的最后,安卓真機(jī)地圖的導(dǎo)入才算是結(jié)束了,最后展示下效果吧
PS
這次的安卓高德地圖的配置,感覺基本把能踩的全踩了個(gè)遍,也是因?yàn)樽约簩?duì)安卓系統(tǒng)不太熟,平時(shí)也常用 iOS 真機(jī)或者模擬器測(cè),也基本沒啥問題,昨天也痛定思痛,以后再也不搞 iOS 模擬器了,下了個(gè) momo,以后全部用安卓測(cè)代碼,
順便貼個(gè)momo模擬器 配置
system_profiler SPUSBDataType
復(fù)制ID/Vender ID到/.android/adb_usb.ini
adb kill-server
adb start-server
adb devices