flutter 集成高徳地图sdk步骤
- 打包项目,获取项目证书文件,SHA1
- 高德地图官方 appkey 申请
- 使用 amap_flutter_location 插件完成定位 定位示例
- 使用 amap_flutter_map 展示高德地图 地图展示示例
补充:
- android studio 左上角Build中没有 Generate Signed Bundle / APK
- 关于android.compileSdkVersion 版本的报错
- 安装完 amap_flutter_map 插件之后Dart文件导入时找不到包
详细配置与使用可以看官网 显示地图-创建地图-开发指南-Android 地图SDK | 高德地图API
审稿人: Taxze
1、打包项目,获取项目证书文件
(1.1)点击 android studio 界面左上方 Build>Generate Signed Bundle / APK , 选择 APK 然后 next 。
补充:android studio 左上角Build中没有 Generate Signed Bundle / APK
**到了这个一步,点击 Create new ... 创建证书
填写证书信息,创建一个项目的打包证书,创建完成后点击 next,进入选择打包类型界面
这里选择 relesse 打包,点击 Create 完成打包后就可以在指定的目录看到证书**
(1.2 使用 keytool(jdk自带工具)获取 SHA1
在控制台中定位到之前生成的证书文件夹中,输入命令 keytool -list -v -keystore 证书文件名
。提示输入密钥库口令,发布模式的密码是为 apk 的 keystore 设置的密码。
这时就能在控制台中看到该证书的 SHA1
keytool -list -v -keystore key10001.jks
2、高德地图官方 appkey 申请
(2.1)进入高徳控制台,创建新应用,创建 appkey
高德控制台 没有账号可以注册一个,进入 应用管理>我的应用 ,点击右上角创建新应用*,在弹出界面中输入应用名称和类型
新建完的应用会出现在我的应用中,点击该应用右侧添加Key,输入证书以及包名信息,选择需要的服务,就可以提交了。
AndroidManifest.xml
创建完成的 key
3. 使用 amap_flutter_location 插件完成定位
(3.1)安装 amap_flutter_location 插件以及 permission_handler 权限申请插件
我当前使用版本:
amap_flutter_location: ^3.0.0
permission_handler: ^11.0.1
(3.2) 项目权限以及前置配置
1. 在 /app/build.gradle 文件中加入签名文件以及配置 implementation
配置证书文件
signingConfigs {
//storeFile file : 填写证书文件路径,我将证书文件放在了该 build.gradle 的同级目录中所以这里直接填写文件名
//keyAlias : 证书别名,默认为 key0
//storePassword,keyPassword : 创建证书时候设置的密码
release {//发布版本的签名配置
storeFile file('key10001.jks')
keyAlias "key0"
storePassword "123456"
keyPassword "123456"
}
debug {//调试版本的签名配置
storeFile file('key10001.jks')
keyAlias "key0"
storePassword "123456"
keyPassword "123456"
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
}
debug {
// TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
配置 implementation dependencies
加入 implementation('com.amap.api:location:5.2.0')
dependencies {
implementation('com.amap.api:location:5.2.0')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
2. 修改 android/app/src/main/AndroidManifest.xml
增加权限
<!--访问网络-->
<uses-permission android:name="android.permission.INTERNET" />
<!--粗略定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--精确定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--申请调用 A-GPS 模块-->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<!--用于获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--用于访问 wifi 网络信息,wifi 信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--用于获取 wifi 的获取权限,wifi 信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!--用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--用于写入缓存数据到扩展存储卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
配置定位 Service
<application
<!-- 配置定位 Service -->
<service android:name="com.amap.api.location.APSService"/>
</application>
(3.3)定位示例
注意:需要将示例中放入 appkey 的地方放入自己生成的 key
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:amap_flutter_location/amap_flutter_location.dart';
import 'package:amap_flutter_location/amap_location_option.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
Map _locationResult = {};
late StreamSubscription<Map<String, Object>> _locationListener;
AMapFlutterLocation _locationPlugin = new AMapFlutterLocation();
@override
void initState() {
super.initState();
//私权政策是否弹窗展示告知用户
AMapFlutterLocation.updatePrivacyShow(true, true);
// [hasAgree] 隐私权政策是否已经取得用户同意
AMapFlutterLocation.updatePrivacyAgree(true);
/// 动态申请定位权限
requestPermission();
///设置 Android 和 iOS 的 apiKey<br> /// /// 定位 Flutter 插件提供了单独的设置 ApiKey 的接口,
/// 使用接口的优先级高于通过 Native 配置 ApiKey 的优先级(通过 Api 接口配置后,
///通过 Native 配置文件设置的 key 将不生效),
/// 使用时可根据实际情况决定使用哪种方式
///
///key 的申请请参考高德开放平台官网说明<br>
/// ///Android: https: //lbs.amap.com/api/android-location-sdk/guide/create-project/get-key
///
///iOS: https://lbs.amap.com/api/ios-location-sdk/guide/create-project/get-key
AMapFlutterLocation.setApiKey("7d5328bb6c61c4fec61c56e8599594b9", "ios ApiKey");
///iOS 获取 native 精度类型
if (Platform.isIOS) {
requestAccuracyAuthorization();
}
///注册定位结果监听
_locationListener = _locationPlugin
.onLocationChanged()
.listen((Map<String, Object> result) {
setState(() {
_locationResult = result;
});
});
}
@override
void dispose() {
super.dispose();
///移除定位监听
if (null != _locationListener) {
_locationListener.cancel();
}
///销毁定位
if (null != _locationPlugin) {
_locationPlugin.destroy();
}
}
///设置定位参数
void _setLocationOption() {
if (null != _locationPlugin) {
AMapLocationOption locationOption = new AMapLocationOption();
///是否单次定位
locationOption.onceLocation = false;
///是否需要返回逆地理信息
locationOption.needAddress = true;
///逆地理信息的语言类型
locationOption.geoLanguage = GeoLanguage.DEFAULT;
locationOption.desiredLocationAccuracyAuthorizationMode =
AMapLocationAccuracyAuthorizationMode.ReduceAccuracy;
locationOption.fullAccuracyPurposeKey = "AMapLocationScene";
///设置 Android 端连续定位的定位间隔
locationOption.locationInterval = 2000;
///设置 Android 端的定位模式<br>
///可选值:<br>
///<li>[AMapLocationMode.Battery_Saving]</li> ///<li>[AMapLocationMode.Device_Sensors]</li> ///<li>[AMapLocationMode.Hight_Accuracy]</li> locationOption.locationMode = AMapLocationMode.Hight_Accuracy;
///设置 iOS 端的定位最小更新距离<br>
locationOption.distanceFilter = -1;
///设置 iOS 端期望的定位精度
/// 可选值:<br>
/// <li>[DesiredAccuracy.Best] 最高精度</li>
/// <li>[DesiredAccuracy.BestForNavigation] 适用于导航场景的高精度 </li> /// <li>[DesiredAccuracy.NearestTenMeters] 10 米 </li> /// <li>[DesiredAccuracy.Kilometer] 1000 米</li>
/// <li>[DesiredAccuracy.ThreeKilometers] 3000 米</li>
locationOption.desiredAccuracy = DesiredAccuracy.Best;
///设置 iOS 端是否允许系统暂停定位
locationOption.pausesLocationUpdatesAutomatically = false;
///将定位参数设置给定位插件
_locationPlugin.setLocationOption(locationOption);
}
}
///开始定位
void _startLocation() {
if (null != _locationPlugin) {
///开始定位之前设置定位参数
_setLocationOption();
_locationPlugin.startLocation();
}
}
///停止定位
void _stopLocation() {
if (null != _locationPlugin) {
_locationPlugin.stopLocation();
}
}
Container _createButtonContainer() {
return new Container(
alignment: Alignment.center,
child: new Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new ElevatedButton(
onPressed: _startLocation, child: new Text('开始定位')),
new Container(width: 20.0),
new ElevatedButton(
onPressed: _stopLocation, child: new Text('停止定位'))
],
));
}
Widget _resultWidget(key, value) {
return new Container(
child: new Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
alignment: Alignment.centerRight,
width: 100.0,
child: new Text('$key :'),
),
new Container(width: 5.0),
new Flexible(child: new Text('$value', softWrap: true)),
],
),
);
}
@override
Widget build(BuildContext context) {
List<Widget> widgets = [];
widgets.add(_createButtonContainer());
if (_locationResult != null) {
_locationResult.forEach((key, value) {
widgets.add(_resultWidget(key, value));
});
}
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('AMap Location plugin example app'),
),
body: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: widgets,
),
));
}
///获取 iOS native 的 accuracyAuthorization 类型
void requestAccuracyAuthorization() async {
AMapAccuracyAuthorization currentAccuracyAuthorization =
await _locationPlugin.getSystemAccuracyAuthorization();
if (currentAccuracyAuthorization ==
AMapAccuracyAuthorization.AMapAccuracyAuthorizationFullAccuracy) {
print("精确定位类型");
} else if (currentAccuracyAuthorization ==
AMapAccuracyAuthorization.AMapAccuracyAuthorizationReducedAccuracy) {
print("模糊定位类型");
} else {
print("未知定位类型");
}
}
/// 动态申请定位权限
void requestPermission() async {
// 申请权限
bool hasLocationPermission = await requestLocationPermission();
if (hasLocationPermission) {
print("定位权限申请通过");
} else {
print("定位权限申请不通过");
}
}
/// 申请定位权限
/// 授予定位权限返回 true, 否则返回 false
Future<bool> requestLocationPermission() async {
//获取当前的权限
var status = await Permission.location.status;
if (status == PermissionStatus.granted) {
//已经授权
return true;
} else {
//未授权则发起一次申请
status = await Permission.location.request();
if (status == PermissionStatus.granted) {
return true;
} else {
return false;
}
}
}
}
定位结果
4. 使用 amap_flutter_map 展示高德地图
(4.1)安装 amap_flutter_map 插件
注:仍然需要 permission_handler 插件
amap_flutter_map: ^3.0.0
(4.2)项目权限以及前置配置
app/src/main/AndroidManifest.xml
权限配置,已全部被上面的 location 定位包含
//地图SDK(包含其搜索功能)需要的基础权限
<!--允许程序打开网络套接字-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序设置内置sd卡的写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允许程序访问WiFi网络信息-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--允许程序访问CellID或WiFi热点来获取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
设置高徳key (可以不设置,不过要在后期代码里配置高徳key
<application>
......
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="申请的用户Key"/>
......
</application>
在 /app/build.gradle 文件中配置 implementation
将之前的 implementation('com.amap.api:location:5.2.0')
替换成 implementation('com.amap.api:3dmap:9.4.0
。 如果同时保留会冲突报错,在替换后仍然能使用 location 定位功能。
implementation('com.amap.api:3dmap:9.4.0')
(4.3)地图展示
示例
注意:需要在示例中放入 appkey 的地方使用自己的 appkey
import 'package:amap_flutter_map/amap_flutter_map.dart';
import 'package:flutter/material.dart';
import 'package:amap_flutter_base/amap_flutter_base.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp( MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: GDMap(),
),
);
}
}
class GDMap extends StatefulWidget{
@override
State<StatefulWidget> createState() =>GDMapState();
}
class GDMapState extends State<GDMap> {
late AMapController mapController;
// 获取审图号
void getApprovalNumber() async {
//普通地图审图号
String? mapContentApprovalNumber =
await mapController?.getMapContentApprovalNumber();
//卫星地图审图号
String? satelliteImageApprovalNumber =
await mapController?.getSatelliteImageApprovalNumber();
}
AMapApiKey _amapApiKeys = AMapApiKey(
androidKey: '7d5328bb6c61c4fec61c56e8599594b9',
iosKey: '申请的iOS平台的key');
AMapPrivacyStatement _amapPrivacyStatement = AMapPrivacyStatement(hasContains: true, hasShow: true, hasAgree: true);
_onMapCreated(AMapController controller) {
setState(() {
mapController = controller;
getApprovalNumber();
});
}
/// 动态申请定位权限
void requestPermission() async {
// 申请权限
bool hasLocationPermission = await requestLocationPermission();
if (hasLocationPermission) {
print("定位权限申请通过");
} else {
print("定位权限申请不通过");
}
}
/// 申请定位权限
/// 授予定位权限返回 true, 否则返回 false
Future<bool> requestLocationPermission() async {
//获取当前的权限
var status = await Permission.location.status;
if (status == PermissionStatus.granted) {
//已经授权
return true;
} else {
//未授权则发起一次申请
status = await Permission.location.request();
if (status == PermissionStatus.granted) {
return true;
} else {
return false;
}
}}
@override
void initState() {
// TODO: implement initState
super.initState();
requestPermission();
}
@override
Widget build(BuildContext context) {
final AMapWidget map = AMapWidget(
privacyStatement: _amapPrivacyStatement,
apiKey: _amapApiKeys,
onMapCreated: _onMapCreated,
);
return Container(
height: MediaQuery
.of(context)
.size
.height,
width: MediaQuery
.of(context)
.size
.width,
child: map,
);
}
}
运行结果:
补充
1. android studio 左上角Build中没有 Generate Signed Bundle / APK
^3f7756
解决办法:
进入 android/app/src/main/AndroidManifest.xml
点击右上角的 open for editing in Android Studio , 等下方Gradle async 加载完就可以在Build这中找到 Generate Signed Bundle / APK 了。
如果Gradle async 加载太慢或者加载失败,可以在 android/build.gradle
中修改下载源为阿里云
buildscript {
ext.kotlin_version = '1.6.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
//阿里云镜像下载地址
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
}
}
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/google' }
mavenLocal()
mavenCentral()
/* google()
mavenCentral()*/ }
}
2. 关于android.compileSdkVersion 版本的报错
解决方法
android.compileSdkVersion 改为33
3. 安装完 amap_flutter_map 插件之后Dart文件导入时找不到包
解决方法
android studio 清除缓存重新启动