flutter 集成高徳地图,获取定位以及展示地图高徳地图

发布于:2024-04-25 ⋅ 阅读:(16) ⋅ 点赞:(0)

flutter 集成高徳地图sdk步骤

补充:

详细配置与使用可以看官网

审稿人:

1、打包项目,获取项目证书文件

(1.1)点击 android studio 界面左上方 Build>Generate Signed Bundle / APK , 选择 APK 然后 next 。

补充:

**到了这个一步,点击 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 清除缓存重新启动