几个链接
高德地图 SDK入门指南 https://lbs.amap.com/api/flutter/gettingstarted
如何申请高德地图 SDKKey https://lbs.amap.com/api/android-location-sdk/guide/create-project/get-key
高德地图 SDK应用管理后台地址 https://console.amap.com/dev/key/app
用到的高德地图相关的Flutter包
权限相关 https://pub.dev/packages/permission_handler/install
Flutter集成高德地图SDK主要几个步骤
- 注册开发者认证、创建应用、添加key
- 配置权限
- 配置Android端的地图库引入与so支持
- 安装相关的flutter包
1. 创建 keystore
# 创建keystore keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key # 获取包名,应用后台管理需要 https://bytenote.net/article/142277605930827777 # 获取sha1 keytool -v -list -keystore /Users/pan/key.jks
2. 创建key的相关配置信息
在安卓项目的根目录下创建一个文件key.properties,内容如下:
storePassword=123456
keyPassword=123456
keyAlias=key
storeFile=/Users/pan/key.jks
- storeFile 使用绝对路径
- 相关信息配置完成后,在高德地图 SDK应用后台管理页面按要求填写包名和sha1
3. 配置签名
路径为: android/app/build.gradle , 主要做了两件事:
- 读取了key.properties的相关配置
- 按需引用高德的相关库
def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) } } def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { flutterVersionName = '1.0' } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" def keystorePropertiesFile = rootProject.file("key.properties") def keystoreProperties = new Properties() keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) android { compileSdkVersion 33 ndkVersion flutter.ndkVersion compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } sourceSets { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.example.flutter_amap" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName ndk { //设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so) abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","arm64-v8a","x86_64" } } signingConfigs { release { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile file(keystoreProperties['storeFile']) storePassword keystoreProperties['storePassword'] } } buildTypes { release { signingConfig signingConfigs.release } } } flutter { source '../..' } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation fileTree(dir: 'libs', include: ['*.jar']) implementation('com.amap.api:location:5.2.0') }
4. Flutter 依赖配置
- amap_flutter_location 高德的定位库
- permission_handler 权限管理的相关库
name: flutter_amap description: A new Flutter project. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. # In Android, build-name is used as versionName while build-number used as versionCode. # Read more about Android versioning at https://developer.android.com/studio/publish/versioning # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: sdk: ">=2.17.6 <3.0.0" dependencies: flutter: sdk: flutter amap_flutter_location: ^3.0.0 cupertino_icons: ^1.0.2 permission_handler: ^10.0.0 dev_dependencies: flutter_test: sdk: flutter # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. flutter: # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: # assets: # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware # For details regarding adding assets from package dependencies, see # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a # "family" key with the font family name, and a "fonts" key with a # list giving the asset and other descriptors for the font. For # example: # fonts: # - family: Schyler # fonts: # - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Italic.ttf # style: italic # - family: Trajan Pro # fonts: # - asset: fonts/TrajanPro.ttf # - asset: fonts/TrajanPro_Bold.ttf # weight: 700 # # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages
5. Demo
import 'dart:async'; import 'dart:io'; import 'package:flutter/foundation.dart'; 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(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override MyAppState createState() => MyAppState(); } class MyAppState extends State<MyApp> { Map<String, Object> _locationResult = {}; late StreamSubscription<Map<String, Object>> _locationListener; final AMapFlutterLocation _locationPlugin = AMapFlutterLocation(); @override void initState() { super.initState(); /// 设置是否已经包含高德隐私政策并弹窗展示显示用户查看,如果未包含或者没有弹窗展示,高德定位SDK将不会工作 /// /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy /// <b>必须保证在调用定位功能之前调用, 建议首次启动App时弹出《隐私政策》并取得用户同意</b> /// /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy /// /// [hasContains] 隐私声明中是否包含高德隐私政策说明 /// /// [hasShow] 隐私权政策是否弹窗展示告知用户 AMapFlutterLocation.updatePrivacyShow(true, true); /// 设置是否已经取得用户同意,如果未取得用户同意,高德定位SDK将不会工作 /// /// 高德SDK合规使用方案请参考官网地址:https://lbs.amap.com/news/sdkhgsy /// /// <b>必须保证在调用定位功能之前调用, 建议首次启动App时弹出《隐私政策》并取得用户同意</b> /// /// [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( "81ff7e4b256exxxx512e1ccb7", "ios ApiKey"); ///iOS 获取native精度类型 if (Platform.isIOS) { requestAccuracyAuthorization(); } ///注册定位结果监听 _locationListener = _locationPlugin .onLocationChanged() .listen((Map<String, Object> result) { setState(() { _locationResult = result; }); }); } @override void dispose() { super.dispose(); ///移除定位监听 _locationListener.cancel(); ///销毁定位 _locationPlugin.destroy(); } ///设置定位参数 void _setLocationOption() { AMapLocationOption locationOption = 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() { _setLocationOption(); _locationPlugin.startLocation(); } ///停止定位 void _stopLocation() { _locationPlugin.stopLocation(); } Container _createButtonContainer() { return Container( alignment: Alignment.center, child: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ RaisedButton( onPressed: _startLocation, child: Text('开始定位'), color: Colors.blue, textColor: Colors.white, ), Container(width: 20.0), RaisedButton( onPressed: _stopLocation, child: Text('停止定位'), color: Colors.blue, textColor: Colors.white, ) ], )); } Widget _resultWidget(key, value) { return Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Container( alignment: Alignment.centerRight, width: 100.0, child: Text('$key :'), ), Container(width: 5.0), Flexible(child: Text('$value', softWrap: true)), ], ); } @override Widget build(BuildContext context) { List<Widget> widgets = []; widgets.add(_createButtonContainer()); _locationResult.forEach((key, value) { widgets.add(_resultWidget(key, value)); }); return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('AMap Location plugin example app'), ), body: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: widgets, ), )); } ///获取iOS native的accuracyAuthorization类型 void requestAccuracyAuthorization() async { AMapAccuracyAuthorization currentAccuracyAuthorization = await _locationPlugin.getSystemAccuracyAuthorization(); if (currentAccuracyAuthorization == AMapAccuracyAuthorization.AMapAccuracyAuthorizationFullAccuracy) { if (kDebugMode) { if (kDebugMode) { 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; } } } }