Flutter 集成高德地图,部分资源来源于网络,技术在不断更新,网上很多方法都过时了,筛选最优解决办法做一次总结。
在高德开放平台创建一个应用 。
我是Ubuntu20.04开发环境,进入目录
scrutiny@scrutiny-CN15S:~/.android$ cd ~/.android/
如果有 debug.keystone 输入以下命令查看调试安全码
keytool -list -v -keystore debug.keystore
如果没有输入以下命令生成 debug.keystore 后再查看
keytool -list -v -keystore "~/.android/debug.keystore" -alias androiddebugkey -storepass android -keypass android
package name 在 android/app/build.gradle下查看,然后完成创建应用。
编辑 android/app/src/main/AndroidManifest.xml 文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.oblivion">
<application
android:name="io.flutter.app.FlutterApplication"
//略···
<activity
//略···
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
//第一步 name 是固定的,value 填高德地图提供的key
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="1391d54211e5425ad433c85187188e3e"/>
</application>
//第二步 在此处添加开放权限
<!--允许程序打开网络套接字-->
<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" />
<!--允许程序读写手机状态和身份-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--允许程序访问CellID或WiFi热点来获取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>
pubspec.yaml 新增配置,然后执行 flutter pub get 获取依赖包。
# https://pub.flutter-io.cn/packages/amap_all_fluttify
amap_all_fluttify: 0.15.1
# https://github.com/Baseflow/flutter-permission-handler
permission_handler: ^5.0.0+hotfix.9
# https://github.com/PonnamKarthik/FlutterToast
fluttertoast: ^4.0.0
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:oblivion/pages/dynamic/map_choice.dart';
class DynamicPage extends StatefulWidget {
@override
_DynamicPageState createState() => _DynamicPageState();
}
class _DynamicPageState extends State<DynamicPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: MapChoicePoint((point) {
debugPrint(point.toString());
})),
);
}
}
import 'package:flutter/material.dart';
import 'package:amap_all_fluttify/amap_all_fluttify.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:fluttertoast/fluttertoast.dart';
/**
* 地图选择点控件
*/
class MapChoicePoint extends StatefulWidget {
/**
* 选择点后回调事件
*/
final Function onChoicePoint;
MapChoicePoint(this.onChoicePoint);
@override
_MapChoicePointState createState() => _MapChoicePointState();
}
class _MapChoicePointState extends State<MapChoicePoint>
with SingleTickerProviderStateMixin {
//----属性----
//地图控制器
AmapController _amapController;
//选择的点
Marker _markerSelect;
//搜索出来之后选择的点
Marker _markerSeached;
//所在城市
String city;
//搜索框文字控制器
TextEditingController _serachController;
//自定义marker点图标图片路径
//Uri _imgUri = Uri.parse('images/position.png');
//----方法----
/**
* 获取权限
*/
Future<bool> _requestPermission() async {
final permissions = await await Permission.location.status;
if (permissions.isUndetermined) {
return true;
} else {
Fluttertoast.showToast(
msg: "需要定位权限",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0
);
return false;
}
}
/**
* 根据搜索条件选出想要的点
*/
Future _openModalBottomSheet() async {
//收起键盘
FocusScope.of(context).requestFocus(FocusNode());
//根据关键字及城市进行搜索
final poiList = await AmapSearch.searchKeyword(
_serachController.text,
city: city,
);
List<Map> points = [];
//便利拼接信息
for (var item in poiList) {
points.add({
'title': await item.title,
'address': await item.adName + await item.address,
'position': await item.latLng,
});
}
//弹出底部对话框并等待选择
final option = await showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return points.length > 0
? ListView.builder(
itemCount: points.length,
itemBuilder: (BuildContext itemContext, int i) {
return ListTile(
title: Text(points[i]['title']),
subtitle: Text(points[i]['address']),
onTap: () {
Navigator.pop(context, points[i]);
},
);
},
)
: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(40),
child: Text('暂无数据'));
});
if (option != null) {
LatLng selectlatlng = option['position'];
//将地图中心点移动到选择的点
_amapController.setCenterCoordinate(selectlatlng);
//删除原来地图上搜索出来的点
if (_markerSeached != null) {
_markerSeached.remove();
}
//将搜索出来的点显示在界面上 --此处不能使用自定义图标的marker,使用会报错,至今也没有解决
_markerSeached = await _amapController.addMarker(MarkerOption(
latLng: selectlatlng,
));
}
}
@override
void initState() {
super.initState();
_serachController = TextEditingController();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
alignment: AlignmentDirectional.topCenter,
children: <Widget>[
AmapView(
// 地图类型 (可选)
mapType: MapType.Standard,
// 是否显示缩放控件 (可选)
showZoomControl: true,
// 是否显示指南针控件 (可选)
showCompass: true,
// 是否显示比例尺控件 (可选)
showScaleControl: true,
// 是否使能缩放手势 (可选)
zoomGesturesEnabled: true,
// 是否使能滚动手势 (可选)
scrollGesturesEnabled: true,
// 是否使能旋转手势 (可选)
rotateGestureEnabled: true,
// 是否使能倾斜手势 (可选)
tiltGestureEnabled: true,
// 缩放级别 (可选)
zoomLevel: 16,
// 中心点坐标 (可选)
// centerCoordinate: LatLng(39, 116),
// 标记 (可选)
markers: <MarkerOption>[],
// 标识点击回调 (可选)
onMarkerClicked: (Marker marker) async {
if (_markerSeached == null) {
return;
}
//获取点击点的位置
var location = await marker.location;
var lon = location.longitude;
var lat = location.latitude;
//获取搜索点的位置
var slocation = await _markerSeached.location;
var slon = slocation.longitude;
var slat = slocation.latitude;
//比较位置
if (lon == slon && lat == slat) {
//移除原来的点
if (_markerSeached != null) {
_markerSeached.remove();
}
if (_markerSelect != null) {
_markerSelect.remove();
}
//画上新的点
_markerSelect = await _amapController.addMarker(MarkerOption(
latLng: location,
//iconUri: _imgUri,
imageConfig: createLocalImageConfiguration(context),
width: 64,
height: 64,
anchorV: 0.7,
anchorU: 0.5));
}
},
// 地图点击回调 (可选)
onMapClicked: (LatLng coord) async {
if (_amapController != null) {
//移除原来的点
if (_markerSelect != null) {
_markerSelect.remove();
}
if (_markerSeached != null) {
_markerSeached.remove();
}
//画上新的点
_markerSelect = await _amapController.addMarker(MarkerOption(
latLng: coord,
//iconUri: _imgUri,
imageConfig: createLocalImageConfiguration(context),
width: 64,
height: 64,
anchorV: 0.7,
anchorU: 0.5));
widget.onChoicePoint(coord);
}
},
onMapMoveStart: (MapMove move) {},
// 地图创建完成回调 (可选)
onMapCreated: (controller) async {
_amapController = controller;
//申请权限
if (await _requestPermission()) {
//获取所在城市
final location = await AmapLocation.fetchLocation();
city = await location.city;
//显示自己的定位
await controller.showMyLocation(MyLocationOption(show: true));
// await initSerach();
}
},
),
Container(
margin: EdgeInsets.all(20),
width: MediaQuery.of(context).size.width,
height: 46,
decoration: BoxDecoration(color: Colors.white),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(
padding: EdgeInsets.all(8),
width: MediaQuery.of(context).size.width - 20 - 80,
child: TextField(
controller: _serachController,
decoration: InputDecoration(border: InputBorder.none),
inputFormatters: <TextInputFormatter>[
LengthLimitingTextInputFormatter(10) //限制长度
],
),
),
IconButton(
icon: Icon(Icons.search), onPressed: _openModalBottomSheet)
],
),
)
],
);
}
}
报错: Error:The number of method references in a .dex file cannot exceed 64K.
解决方法不唯一,我选择的是multidex 配置,在我们配置应用程序的构建过程中,生成多个DEX文件。
android/app/build.gradle 文件最下方 defaultConfig 中添加 multiDexEnabled true。
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.oblivion"
minSdkVersion 16
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
# 追加
multiDexEnabled true
}
android/app/build.gradle 文件最下方 dependencies 中添加如下代码:
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
# 追加
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.android.support:multidex:1.0.3'
}