关键词:
前言:想完成美团一样的自定义地图组件吗?想要软件和美团一样丝滑吗?想要工资和美团一样高吗?现在是白天,你想做白日梦吗?大家好,我是阿T。又过了一周,这次主要是使用高德地图SDK实现了自定义的地图,为啥要做这个呢,因为网上关于Flutter的地图处理文章实在是太少了,更不要说是自定义地图了,然后官网文档又太简略,害,说多了都是泪,于是我付出了少陪女朋友5天的时间,把这篇文章写了出来!!
源码在文章最后,一步一步按照文章来运行肯定可以💪
先上效果图:
还有其他很多小功能就不展示了,大家可以自己运行看看~
阅读本文的注意点:
1.测试代码需要使用真机,模拟器无法加载地图(可以定位,但是无法加载地图,可能与版本有关)
2.本文使用的插件:
permission_handler: ^8.1.4 #权限管理
amap_flutter_map: 2.0.2 #高德地图
正文:
1.在高德的开发者平台申请key:
第一步:注册开发者账号
高德开发者地址:https://console.amap.com/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qVPEBatj-1634367342850)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/064bfdfa0e09449ca5c3c6db2f55ebe8~tplv-k3u1fbpfcp-watermark.image?)]
第二步:创建新的应用,并申请key
第三步:
关于如何获取SHA1,以及处理高德定位使用中的报错在这篇文章里(下一篇文章,还在努力码字😭,给个赞吧)
第四步获取到key:
2.处理使用高德地图的权限
第一步:添加定位所需要的权限
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--访问网络-->
<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" />
**第二步:**添加自己刚刚申请的key,添加开发详细定位
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="自己的key" />
<service android:name="com.amap.api.location.APSService"/>
第三步:配置一些key.jks
如何生成key.jks在这一篇文章里
生成完后创建一个key.properties
storePassword=你自己的密码
keyPassword=你自己的密码
keyAlias=key
storeFile=存放的位置(D:\\\\flutter_gaode_keystore\\\\key.jks)
然后在app文件夹下的build.gradle中使用:
先找到key.properties
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
然后在signingConfigs下引用:
signingConfigs
release
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
第四步:导入地图
仍在app文件夹下的build.gradle中
dependencies
implementation('com.amap.api:location:5.2.0')
implementation 'com.amap.api:3dmap:7.6.0'
implementation 'com.amap.api:search:5.0.0'
sourceSets
//添加地图SDK引入路径
main
jniLibs.srcDirs = ['libs']
并在buildTypes下添加一下两行:
buildTypes
release
signingConfig signingConfigs.release
minifyEnabled false //删除无用代码
shrinkResources false //删除无用资源
这样就配置好了~
3.加载高德地图
接下来的部分为代码实现部分,只展示核心代码🐷
第一步:添加申请到的key
class ConstConfig
///配置申请的apikey,在此处配置之后,可以在初始化[AMapWidget]时,通过`apiKey`属性设置
///
///注意:使用[AMapWidget]的`apiKey`属性设置的key的优先级高于通过Native配置key的优先级,
///使用[AMapWidget]的`apiKey`属性配置后Native配置的key将失效,请根据实际情况选择使用
static const AMapApiKey amapApiKeys = AMapApiKey(
androidKey: '你自己申请的key',
iosKey: '没有申请ios的话先填android的key,这样也可以在安卓手机测试');
第二步:定义一下地图类型
//地图类型
MapType _mapType;
final Map<String, MapType> _radioValueMap =
'普通地图': MapType.normal,
'卫星地图': MapType.satellite,
'导航地图': MapType.navi,
'公交地图': MapType.bus,
'黑夜模式': MapType.night,
;
初始化地图:
@override
void initState()
super.initState();
//默认为普通地图
_mapType = MapType.normal;
创建地图:
//创建地图
final AMapWidget map = AMapWidget(
apiKey: ConstConfig.amapApiKeys,
//地图类型属性
mapType: _mapType ?? MapType.normal,
);
第三步:显示地图
AMapRadioGroup(
groupLabel: '地图样式',
groupValue: _mapType,
radioValueMap: _radioValueMap,
onChanged: (value) =>
//改变当前地图样式为选中的样式
setState(()
_mapType = value;
)
,
)
第四步:默认地址
//默认显示在北京天安门
static final CameraPosition _kInitialPosition = const CameraPosition(
target: LatLng(39.909187, 116.397451),
zoom: 10.0,
);
4.自定义地图
第一步:加载自定义地图的样式
//用于记录是否为自定义地图
bool _mapCreated = false;
//加载自定义地图样式
void _loadCustomData() async
if (null == _customStyleOptions)
_customStyleOptions = CustomStyleOptions(false);
ByteData styleByteData = await rootBundle.load('assets/style.data');
_customStyleOptions.styleData = styleByteData.buffer.asUint8List();
ByteData styleExtraByteData =
await rootBundle.load('assets/style_extra.data');
_customStyleOptions.styleExtraData =
styleExtraByteData.buffer.asUint8List();
//如果需要加载完成后直接展示自定义地图,可以通过setState修改CustomStyleOptions的enable为true
setState(()
_customStyleOptions.enabled = true;
);
第二步:定义地图
final AMapWidget map = AMapWidget(
apiKey: ConstConfig.amapApiKeys,
onMapCreated: onMapCreated,
customStyleOptions: _customStyleOptions,
);
void onMapCreated(AMapController controller)
if (null != controller)
_mapCreated = true;
第三步:点击切换为自定义地图
AMapSwitchButton(
label: Text(
'自定义地图',
style: TextStyle(color: Colors.white),
),
defaultValue: _customStyleOptions.enabled,
onSwitchChanged: (value) =>
if (_mapCreated)
setState(()
_customStyleOptions.enabled = value;
)
,
)
5.让外卖小哥飞起来
第一步:获取自定义的图片
有三种方式:
第一种:
///通过BitmapDescriptor.fromAssetImage的方式获取图片
Future<void> _createMarkerImageFromAsset(BuildContext context) async
if (_markerIcon == null)
final ImageConfiguration imageConfiguration =
createLocalImageConfiguration(context);
BitmapDescriptor.fromAssetImage(imageConfiguration, 'assets/start.png')
.then(_updateBitmap);
第二种:
///通过BitmapDescriptor.fromBytes的方式获取图片
Future<void> _createMarkerImageFromBytes(BuildContext context) async
final Completer<BitmapDescriptor> bitmapIcon =
Completer<BitmapDescriptor>();
final ImageConfiguration config = createLocalImageConfiguration(context);
const AssetImage('assets/end.png')
.resolve(config)
.addListener(ImageStreamListener((ImageInfo image, bool sync) async
final ByteData bytes =
await image.image.toByteData(format: ImageByteFormat.png);
final BitmapDescriptor bitmap =
BitmapDescriptor.fromBytes(bytes.buffer.asUint8List());
bitmapIcon.complete(bitmap);
));
bitmapIcon.future.then((value) => _updateBitmap(value));
第三种(最简单的一种):
//最简单的方式
if (null == _markerIcon)
_markerIcon = BitmapDescriptor.fromIconPath('assets/location_marker.png');
第二步:根据x,y轴计算图片要显示的位置
void _changeAnchor()
final Marker marker = _markers[selectedMarkerId];
if (marker == null)
return;
final Offset currentAnchor = marker.anchor;
double dx = 0;
double dy = 0;
if (currentAnchor.dx < 1)
dx = currentAnchor.dx + 0.1;
else
dx = 0;
if (currentAnchor.dy < 1)
dy = currentAnchor.dy + 0.1;
else
dy = 0;
final Offset newAnchor = Offset(dx, dy);
setState(()
_markers[selectedMarkerId] = marker.copyWith(
anchorParam: newAnchor,
);
);
这样就可以将外卖小哥展示到地图上了,还有一些其他的小功能
修改图片的位置:
void _changePosition()
final Marker marker = _markers[selectedMarkerId];
final LatLng current = marker.position;
final Offset offset = Offset(
mapCenter.latitude - current.latitude,
mapCenter.longitude - current.longitude,
);
setState(()
_markers[selectedMarkerId] = marker.copyWith(
positionParam: LatLng(
mapCenter.latitude + offset.dy,
mapCenter.longitude + offset.dx,
),
);
);
Future<void> _changeAlpha() async
final Marker marker = _markers[selectedMarkerId];
final double current = marker.alpha;
setState(()
_markers[selectedMarkerId] = marker.copyWith(
alphaParam: current < 0.1 ? 1.0 : current * 0.75,
);
);
修改图片的角度:
Future<void> _changeRotation() async
final Marker marker = _markers[selectedMarkerId];
final double current = marker.rotation;
setState(()
_markers[selectedMarkerId] = marker.copyWith(
rotationParam: current == 330.0 ? 0.0 : current + 30.0,
);
);
6.添加多个图标在地图上
1.定义marker
static final LatLng mapCenter = const LatLng(39.909187, 116.397451);
//需要先设置一个空的map赋值给AMapWidget的markers,否则后续无法添加marker
final Map<String, Marker> _initMarkerMap = <String, Marker>;
2.增加marker
添加一个:
void _addMarker()
final _markerPosition =
LatLng(_currentLatLng.latitude, _currentLatLng.longitude + 2 / 1000);
final Marker marker = Marker(
position: _markerPosition,
//使用默认hue的方式设置Marker的图标
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange),
);
//调用setState触发AMapWidget的更新,从而完成marker的添加
setState(()
_currentLatLng = _markerPosition;
//将新的marker添加到map里
_markers[marker.id] = marker;
);
添加多个:
for(int i=0; i< 10; i++)
LatLng position = LatLng(
mapCenter.latitude + sin(i * pi / 12.0) / 20.0,
mapCenter.longitude + cos(i * pi / 12.0) / 20.0);
Marker marker = Marker(position: position);
_initMarkerMap[marker.id] = marker;
7.辅助工具toast
封装了一个toast,通过OverlayEntry添加到屏幕上
显示它的核心代码:
if (_overlayEntry == null)
//OverlayEntry负责构建布局
//通过OverlayEntry将构建的布局插入到整个布局的最上层
_overlayEntry = OverlayEntry(
builder: (BuildContext context) => Positioned(
//top值,可以改变这个值来改变toast在屏幕中的位置
top: buildToastPosition(context),
child: Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 40.0),
child: AnimatedOpacity(
opacity: _showing ? 1.0 : 0.0, //目标透明度
duration: _showing
? Duration(milliseconds: 100)
: Duration(milliseconds: 400),
child: _buildToastWidget(),
),
)),
));
//插入到整个布局的最上层
overlayState.insert(_overlayEntry);
else
//重新绘制UI,类似setState
_overlayEntry.markNeedsBuild();
绘制核心代码:
//toast绘制
static _buildToastWidget()
return Center(
child: Card(
color: _bgColor,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: _pdHorizontal, vertical: _pdVertical),
child: Text(
_msg,
style: TextStyle(
fontSize: _textSize,
color: _textColor,
),
),
),
),
);
设置toast的位置:
// 设置toast位置
static buildToastPosition(context)
var backResult;
if (_toastPosition == ToastPostion.top)
backResult = MediaQuery.of(context).size.height * 1 / 4;
else if (_toastPosition == ToastPostion.center)
backResult = MediaQuery.of(context).size.height * 2 / 5;
else
backResult = MediaQuery.of(context).size.height * 3 / 4;
return backResult;
这样基本功能就讲解完成啦,还有其他功能源代码里都有写到,比如路况显示,限制地图大小等等
本文结束了,看到这里的兄弟们给点点赞吧,女朋友已经让我睡家门口了😭
(太长时间没理她怎么哄…)评论区告诉小弟😭
如何在 Flutter 中自定义谷歌地图标记图标
】如何在Flutter中自定义谷歌地图标记图标【英文标题】:HowtocustomizegooglemapsmarkericoninFlutter【发布时间】:2019-10-2901:34:37【问题描述】:我在我的颤振应用程序中使用google_maps_flutter来使用谷歌地图我有自定义标记图标,我用Bitmap... 查看详情
请为外卖服务稍加点宽容
...论何时,他们都要接受任务,穿梭在大街小巷,将接手的外卖送到顾客手中,他们就是外卖小哥。外卖小哥每天的工作虽然单调, 查看详情
Flutter 多自定义图标未显示在地图视图上
】Flutter多自定义图标未显示在地图视图上【英文标题】:Fluttermulticustomiconsarenotshowedonthemapview【发布时间】:2021-12-1804:14:05【问题描述】:我试图在地图视图上显示多个自定义图标,并尝试处理图标大小(iOS案例)。我编写的代... 查看详情
程序员加班崩溃,过路外卖小哥主动帮忙改代码,网友直呼太暖了!(代码片段)
...群????????内容整编自:微博,网络暖心!青岛外卖小哥帮崩溃程序员写代码近日,一段青岛外卖小哥帮程序员写代码的视频走红网络。据了解,当日男子和朋友来酒吧看欧洲杯球赛,途中多次离开接电话... 查看详情
java反射java代理模式
...他类,让其他类来代理自己的类对象然后做事;比喻:订外卖,送餐小哥手里的外卖就是我的对象,是我让他送的。静态代理就是我告诉送餐小哥外卖是我的(假设外卖小哥认识我),然后他直接就奔着我来了;动态代理就是我不... 查看详情
外卖小哥,才是这个社会最大的托底
...,一个月13万+》《人生,第一次车祸。。。》1外卖小哥考上上海交大这两天看到,关于两个外卖小哥的新闻,挺有感触和大家聊一下。第一个是我们今天文章的头图,也就说下面的这个小伙,他叫高帅... 查看详情
外卖小哥,才是这个社会最大的托底
...,一个月13万+》《人生,第一次车祸。。。》1外卖小哥考上上海交大这两天看到,关于两个外卖小哥的新闻,挺有感触和大家聊一下。第一个是我们今天文章的头图,也就说下面的这个小伙,他叫高帅... 查看详情
如何在 Flutter 'Web' 中显示 OSM 地图?
】如何在Flutter\\\'Web\\\'中显示OSM地图?【英文标题】:HowcanIdisplayOSMmapsinFlutter\'Web\'?如何在Flutter\'Web\'中显示OSM地图?【发布时间】:2020-07-2002:48:04【问题描述】:我正在尝试使用FlutterWeb创建一个自定义地图,该地图能够显示COVI... 查看详情
小区代送外卖和快递的微信小程序-专业定制开发
1.背景:现在很多外卖员在小区门口进不去,或者要在小区门口耽误不少时间,导致外卖员一天的送单量上不去,所以有必要开发一个小程序为外卖小哥服务,让他们在花点小钱在平台下单后,这边安排员... 查看详情
饿了么,美团,百度外卖哪个工资待遇好
...实也不怎么好),但是架不住投诉(一个投诉扣500块),为了让客户吃点热饭,提前30分钟送到,客户不买帐,直接投诉,降等级。尤其是美团外卖业务员,不为配送员说话,老是为商家说话,挣两个钱还没有罚的多。骑车看手... 查看详情
同步i/o和异步i/o
...步I/O包括:阻塞,非阻塞,多路复用阻塞模型:给你送的外卖到了,给你打电话,你不去取,外卖小哥一直在那等你,直到你来,形成阻塞,当然应该给外卖小哥点赞,哈哈哈哈!!非阻塞模型:取外卖的主人非常饿,秘书不停... 查看详情
聋哑外卖小哥无声送餐:月送600份,准时率100%
...祥来到了厨房边上的冰柜旁。 他取出了一个打包好的外卖袋,看了看手机,又继续在冰柜中找。确认没有另一个袋子后,他折回取餐口,看着一个穿着西装打着蝴蝶结的服务员,欲言又止。服务员在不停地打出新的订单,头... 查看详情
从大厂失业半个月,实在顶不住了,原以为程序员当外卖小哥是笑话,没想到发生在我身上!...(代码片段)
...社区(ID:devabc)网上经常有程序员失业去送外卖、跑滴滴的“笑话”,但笑话也有成真的一天,一位前腾讯程序员最近发帖感叹:失业第15天,实在顶不住了 查看详情
创建自定义地图叠加层(折线)[Mapbox]
...:2020-09-0716:06:51【问题描述】:我有一个使用来自Mapbox的flutter_map和图块的Flutter项目。我还有一个非常大的坐标列表来在地图上创建折线。坐标是全球地理边界。它工作正常,但在显示折线时,由于折线数据量大,地图有点反应... 查看详情
程序员加班看不上球赛崩溃,外卖小哥伸出援手:我帮你改代码
...|程序人生(ID:coder_life)近日,一段青岛外卖小哥帮奔溃程序员写代码的视频意外走红。据了解,一程序员与朋友来酒吧看欧洲杯球赛,途中多次出去接听电话,最后不得已拎着电脑包出去了。通过视... 查看详情
青岛程序员加班看不上球赛崩溃,外卖小哥伸出援手:我帮你改代码
...|程序人生(ID:coder_life)近日,一段青岛外卖小哥帮奔溃程序员写代码的视频意外走红。据了解,一程序员与朋友来酒吧看欧洲杯球赛,途中多次出去接听电话,最后不得已拎着电脑包出去了。通过视... 查看详情
青岛程序员加班看不上球赛崩溃,外卖小哥伸出援手:我帮你改代码
...|程序人生(ID:coder_life)近日,一段青岛外卖小哥帮奔溃程序员写代码的视频意外走红。1据了解,一程序员与朋友来酒吧看欧洲杯球赛,途中多次出去接听电话,最后不得已拎着电脑包出去了。通过... 查看详情
Flutter - Google Maps,同时有多个不同的自定义标记图像?
】Flutter-GoogleMaps,同时有多个不同的自定义标记图像?【英文标题】:Flutter-GoogleMaps,MorethanonedifferentcustommarkerImageatthesametime?【发布时间】:2020-07-1407:20:30【问题描述】:我正在使用Flutter和GoogleMapsAPI。我设法在地图打开时显示了... 查看详情