1、效果图
2、pub get
#地图定位
amap_flutter_map: ^3.0.0
amap_flutter_location: ^3.0.0
3、上代码
import 'dart:async';
import 'dart:io';
import 'package:amap_flutter_location/amap_flutter_location.dart';
import 'package:amap_flutter_location/amap_location_option.dart';
import 'package:fate_blind_box/theme/theme_color.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:amap_flutter_map/amap_flutter_map.dart';
import 'package:amap_flutter_base/amap_flutter_base.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../utils/loading.dart';
import '../../Home/map/canteen.dart';
class AMapPage extends StatelessWidget {
final String title;
final String iosKey;
final String androidKey;
final LatLng? latLng;
final void Function(AMapController controller)? onMapCreated;
AMapPage(this.title,this.iosKey, this.androidKey, {Key? key, this.latLng, this.onMapCreated}) : super(key: key);
// 添加线段
//lines 结构 [LatLng(26.642386, 106.65195799999998), LatLng(26.641542, 106.65062999999998), LatLng(26.641402, 106.65063499999997), LatLng(26.641411, 106.65086300000002), LatLng(26.641334, 106.65067099999999), LatLng(26.641374, 106.650688), LatLng(26.641567, 106.65078499999998), LatLng(26.641656, 106.651207), LatLng(26.641708, 106.651408), LatLng(26.641413, 106.65066100000001), LatLng(26.641484, 106.65012100000001)]
//定义
@override
Widget build(BuildContext context) {
List<Marker> markers = [
Marker(position: const LatLng(39.909215, 116.41005),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon1.png"),
infoWindow: const InfoWindow(
title: '东城区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.9305, 116.36003),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon2.png"),
infoWindow: const InfoWindow(
title: '西城区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.9484, 116.48548),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon3.png"),
infoWindow: const InfoWindow(
title: '朝阳区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.8585, 116.28625),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon4.png"),
infoWindow: const InfoWindow(
title: '丰台区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.90564, 116.2229),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon5.png"),
infoWindow: const InfoWindow(
title: '石景山区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.95931, 116.29812),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon1.png"),
infoWindow: const InfoWindow(
title: '海淀区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.94043, 116.10137),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon2.png"),
infoWindow: const InfoWindow(
title: '门头沟区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.74786, 116.14257),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon3.png"),
infoWindow: const InfoWindow(
title: '房山区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(39.90564, 116.2229),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon4.png"),
infoWindow: const InfoWindow(
title: '石景山区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(116.65716, 39.90966),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon5.png"),
infoWindow: const InfoWindow(
title: '通州区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(116.65417, 40.1302),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon1.png"),
infoWindow: const InfoWindow(
title: '顺义区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(116.2312, 40.22072),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon3.png"),
infoWindow: const InfoWindow(
title: '昌平区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(116.34149, 39.72668),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon4.png"),
infoWindow: const InfoWindow(
title: '大兴区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(116.63168, 40.31602),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon5.png"),
infoWindow: const InfoWindow(
title: '怀柔区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(117.12133, 40.14056),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon1.png"),
infoWindow: const InfoWindow(
title: '平谷区',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(116.84295, 40.37618),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon2.png"),
infoWindow: const InfoWindow(
title: '密云县',
snippet: '',
),
onTap: (marker){
},
),
Marker(position: const LatLng(115.97494, 40.45672),
icon:BitmapDescriptor.fromIconPath("images/ic_mapIcon3.png"),
infoWindow: const InfoWindow(
title: '延庆县',
snippet: '',
),
onTap: (marker){
},
),
];
final size = MediaQuery.of(context).size;
CameraPosition kInitialPosition = CameraPosition(
target: latLng ?? const LatLng(39.909187, 116.397451),
zoom: 10.0,
);
return Scaffold(
appBar: title.isNotEmpty ? AppBar(
title: Text(
title,
style: const TextStyle(
color: Colors.black, fontSize: 18, fontWeight: FontWeight.bold),
),
// title:title,
elevation: 0,
backgroundColor: Colors.white,
) :null,
backgroundColor: ThemeColor.sendMsgColor,
body: AMapWidget(
initialCameraPosition: kInitialPosition,
buildingsEnabled: false,
onMapCreated: onCreated,
privacyStatement: const AMapPrivacyStatement(hasShow: true, hasAgree: true, hasContains: true),
apiKey: AMapApiKey(
iosKey: iosKey,
androidKey: androidKey,
),
markers:Set<Marker>.of(markers),
// Set<marker>.of(markers.values),
onTap: (marker){
},
onPoiTouched: (marker){
print('object');
},
),
);
}
/// 高德地图
static Future<bool> gotoGaoDeMap(keywords,longitude, latitude) async {
var url = '${Platform.isAndroid ? 'android' : 'ios'}amap://navi?sourceApplication=amap&lat=$latitude&lon=$longitude&dev=0&style=2';
bool canLaunchUrl = await canLaunch(url);
if (!canLaunchUrl) {
toast('未检测到高德地图~');
return false;
}
await launch(url);
return true;
}
/// 腾讯地图
static Future<bool> gotoTencentMap(longitude, latitude) async {
var url = 'qqmap://map/routeplan?type=drive&fromcoord=CurrentLocation&tocoord=$latitude,$longitude&referer=IXHBZ-QIZE4-ZQ6UP-DJYEO-HC2K2-EZBXJ';
bool canLaunchUrl = await canLaunch(url);
if (!canLaunchUrl) {
toast('未检测到腾讯地图~');
// ToastUtil.show('未检测到腾讯地图~');
return false;
}
await launch(url);
return canLaunchUrl;
}
void onCreated(AMapController controller) {
AMapApprovalNumber.setApprovalNumber(controller);
if (onMapCreated != null) onMapCreated!(controller);
}
}
/// 获取审图号
/// 这里设计的很奇怪,当地图创建后才知道这个号码,但是这个号码不一定要显示在地图之上,却一定要显示在app之内,主要是和上架后的合规有关
class AMapApprovalNumber {
static String? mapContentApprovalNumber;
static String? satelliteImageApprovalNumber;
static Function(String? mapContentApprovalNumber, String? satelliteImageApprovalNumber)? _listener;
static void addListener(Function(String? mapContentApprovalNumber, String? satelliteImageApprovalNumber) run) {
_listener = run;
}
static void setApprovalNumber(AMapController? mapController) async {
//普通地图审图号
mapContentApprovalNumber = await mapController?.getMapContentApprovalNumber();
//卫星地图审图号
satelliteImageApprovalNumber = await mapController?.getSatelliteImageApprovalNumber();
if (kDebugMode) {
print('地图审图号(普通地图): $mapContentApprovalNumber');
print('地图审图号(卫星地图): $satelliteImageApprovalNumber');
}
if (_listener != null) _listener!(mapContentApprovalNumber, satelliteImageApprovalNumber);
}
}
///需要在程序启动时向用户展示隐私政策等
///高德地图的定位插件,可以进行连续定位,返回当前位置的经纬度以及转译过的位置信息
mixin AMapLocationStateMixin<WIDGET extends StatefulWidget> on State<WIDGET> {
String get title;
String get iosKey;
String get androidKey;
/// 是否拥有定位权限
bool get hasLocationPermission => _hasLocationPermission;
///获取到的定位信息
Map<String, Object> get locationResult => _locationResult ?? {};
///整理过的数据
LocationInfo get locationInfo => LocationInfo(locationResult);
///开始定位
void startLocation() {
///开始定位之前设置定位参数
_setLocationOption();
_locationPlugin.startLocation();
}
///停止定位
void stopLocation() {
_locationPlugin.stopLocation();
}
Map<String, Object>? _locationResult;
StreamSubscription<Map<String, Object>>? _locationListener;
final AMapFlutterLocation _locationPlugin = AMapFlutterLocation();
// final List<LatLng> _traackPath = [];
late AMapController _mapController;
@override
void initState() {
super.initState();
setPath();
AMapFlutterLocation.updatePrivacyShow(true, true);
AMapFlutterLocation.updatePrivacyAgree(true);
/// 动态申请定位权限
_requestLocationPermission();
AMapFlutterLocation.setApiKey(androidKey, iosKey);
///iOS 获取native精度类型
if (Platform.isIOS) {
_requestAccuracyAuthorization();
}
///注册定位结果监听
_locationListener = _locationPlugin.onLocationChanged().listen((Map<String, Object> result) {
setState(() {
_locationResult = result;
});
});
}
// AMapController _mapController;
Map mapMakerListMap = <dynamic, Marker>{};
Map mapPolylineListMap = <dynamic, Polyline>{};
final List<LatLng> _trackPath = [];
late final List trackList;
late final List<LatLng> pointList;
late final LatLng center;
late final double zoom;
// 设置轨迹路径
setPath() {
if (trackList.isNotEmpty) {
for (var item in trackList) {
if (item is Map<String, dynamic>) {
LatLng? latLng = LatLng.fromJson(item);
if (latLng != null) {
_trackPath.add(latLng);
}
}
if (item is List && item.length == 2) {
LatLng latLng = LatLng(item[0], item[1]);
_trackPath.add(latLng);
}
}
}
mapClean();
}
mapClean() async {
mapMakerListMap.clear();
mapPolylineListMap.clear();
setState(() => null);
}
@override
void dispose() {
super.dispose();
///移除定位监听
if (null != _locationListener) {
_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);
}
///获取iOS native的accuracyAuthorization类型
Future<AMapAccuracyAuthorization> _requestAccuracyAuthorization() async {
AMapAccuracyAuthorization currentAccuracyAuthorization = await _locationPlugin.getSystemAccuracyAuthorization();
if (kDebugMode) {
if (currentAccuracyAuthorization == AMapAccuracyAuthorization.AMapAccuracyAuthorizationFullAccuracy) {
print("精确定位类型");
} else if (currentAccuracyAuthorization == AMapAccuracyAuthorization.AMapAccuracyAuthorizationReducedAccuracy) {
print("模糊定位类型");
} else {
print("未知定位类型");
}
}
return currentAccuracyAuthorization;
}
bool _hasLocationPermission = false;
/// 申请定位权限
Future<void> _requestLocationPermission() async {
//获取当前的权限
var status = await Permission.location.status;
if (status == PermissionStatus.granted) {
//已经授权
_hasLocationPermission = true;
} else {
//未授权则发起一次申请
status = await Permission.location.request();
if (status == PermissionStatus.granted) {
_hasLocationPermission = true;
} else {
_hasLocationPermission = false;
}
}
if (kDebugMode) {
if (_hasLocationPermission) {
print("定位权限申请通过");
} else {
print("定位权限申请不通过");
}
}
}
}
class LocationInfo {
//TODO:应当再此类对信息做转换,明确数据类型
String? locTime;
String? province;
String? callbackTime;
String? district;
double? speed;
double? latitude;
double? longitude;
String? country;
String? city;
String? cityCode;
String? street;
String? streetNumber;
String? address;
String? description;
double? bearing;
double? accuracy;
String? adCode;
double? altitude;
int? locationType;
LocationInfo(Map<String, Object> locationResult) {
locTime = locationResult["locTime"] as String;
province = locationResult["province"] as String;
callbackTime = locationResult["callbackTime"] as String;
district = locationResult["district"] as String;
speed = locationResult["speed"] as double;
latitude = double.parse(locationResult["latitude"] as String);
longitude = double.parse(locationResult["longitude"] as String);
country = locationResult["country"] as String;
city = locationResult["city"] as String;
print(city);
cityCode = locationResult["cityCode"] as String;
street = locationResult["street"] as String;
streetNumber = locationResult["streetNumber"] as String;
address = locationResult["address"] as String;
description = locationResult["description"] as String;
bearing = locationResult["bearing"] as double;
accuracy = locationResult["accuracy"] as double;
adCode = locationResult["adCode"] as String;
altitude = locationResult["altitude"] as double;
locationType = locationResult["locationType"] as int;
}
}