官方插件
一个Flutter插件,用于在iOS和Android应用程序中集成Google地图
dependencies:
google_maps_flutter: ^2.3.0
添加Key
<!-- Add the following entry, with your API key -->
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="YOUR-KEY-HERE"/>
显示地图
_googleMap() {
return GoogleMap(
markers: const {},
mapType: MapType.normal,
onMapCreated: (GoogleMapController controller) {},
initialCameraPosition: const CameraPosition(
target: LatLng(22.5551997, 113.0636046),
zoom: 10.5,
),
);
}
字段说明
在使用GoogleMap组件之前,先了解一下字段含义
const GoogleMap({
super.key,
required this.initialCameraPosition,
this.onMapCreated,
this.gestureRecognizers = const <Factory<OneSequenceGestureRecognizer>>{},
this.compassEnabled = true,
this.mapToolbarEnabled = true,
this.cameraTargetBounds = CameraTargetBounds.unbounded,
this.mapType = MapType.normal,
this.minMaxZoomPreference = MinMaxZoomPreference.unbounded,
this.rotateGesturesEnabled = true,
this.scrollGesturesEnabled = true,
this.zoomControlsEnabled = true,
this.zoomGesturesEnabled = true,
this.liteModeEnabled = false,
this.tiltGesturesEnabled = true,
this.myLocationEnabled = false,
this.myLocationButtonEnabled = true,
this.layoutDirection,
/// If no padding is specified default padding will be 0.
this.padding = EdgeInsets.zero,
this.indoorViewEnabled = false,
this.trafficEnabled = false,
this.buildingsEnabled = true,
this.markers = const <Marker>{},
this.polygons = const <Polygon>{},
this.polylines = const <Polyline>{},
this.circles = const <Circle>{},
this.onCameraMoveStarted,
this.tileOverlays = const <TileOverlay>{},
this.onCameraMove,
this.onCameraIdle,
this.onTap,
this.onLongPress,
});
| 字段 | 含义 |
|---|---|
| initialCameraPosition | 初始化地图位置,经纬度/缩放比 |
| onMapCreated | 地图被创建时回调 |
| gestureRecognizers | 声明使用哪些手势 |
| compassEnabled | 地图在旋转的时候是否显示指南针 |
| mapToolbarEnabled | 当点击地图标记时,地图右下角是否显示导航工具栏,默认开启,仅对Android生效 |
| cameraTargetBounds | 设置地图目标边界,默认无界限。设定完边界之后,只能在边界范围内移动地图 |
| mapType | 设置图层类型,标准 / 卫星 |
| minMaxZoomPreference | 设置缩放级别,缩放的最小值和最大值 |
| rotateGesturesEnabled | 是否允许旋转地图 |
| scrollGesturesEnabled | 是否允许移动地图 |
| zoomControlsEnabled | 是否显示地图右下角的缩放按钮,默认显示 |
| zoomGesturesEnabled | 是否允许通过手势缩放地图,默认允许 |
| liteModeEnabled | 精简模式 |
| tiltGesturesEnabled | 是否开启倾斜手势 |
| myLocationEnabled | 我的位置是否在地图上显示,默认不显示 |
| myLocationButtonEnabled | 我的位置定位按钮是否显示,默认显示,如果我的位置在地图上不显示,则同样也不会显示该按钮 |
| layoutDirection | 指定地图的布局方向 |
| padding | 设置内部间距,可以控制地图内部icon间距 |
| indoorViewEnabled | 是否启用地图上的室内视图 |
| trafficEnabled | 是否启用地图交通图层,默认不启用 |
| buildingsEnabled | 是否启用可用的 3D 建筑物 |
| markers | 设置地图标记 |
| polygons | 在地图上绘制多边形区域(是一种封闭形状,可用于在地图上标记区域) |
| polylines | 在地图上绘制线条 |
| circles | 在地图上绘制圆形区域 |
| onCameraMoveStarted | 开始移动地图时回调 |
| tileOverlays | 图块叠加层 |
| onCameraMove | 正在移动地图时回调(地图缩放也会移动地图) |
| onCameraIdle | 当停止移动地图时回调 |
| onTap | 点击地图时回调 |
| onLongPress | 长点击地图时回调 |
开始画圈
画圈标记区域,实现思路
定义一个bool字段,标记是否正在画圈,如果正在画圈
- 隐藏相关组件,通过判断是否正在画圈,隐藏画圈无关的组件
- 禁用地图缩放,通过zoomGesturesEnabled字段禁止地图缩放
- 添加地图蒙层,通过tileOverlays字段实现自定义地图覆盖蒙层
禁止地图移动,实现思路
如何禁止移动地图,并且还能在地图上画圈?经过一番探索后,发现有以下方式可以禁止地图移动
通过设置scrollGesturesEnabled禁用地图移动
通过设置cameraTargetBounds限制地图范围
通过GestureRecognizers对GoogleMap进行包裹
在地图上绘制区域,实现思路
通过GestureRecognizers监听手势,拿到用户在屏幕内移动的坐标,通过坐标转换成经纬度,然后绘制图形
polylines:绘制线条区域
polygons:绘制闭合区域
Offset offset = details.localPosition;
GoogleMapController controller = await mapController.future;
LatLng latLng = await controller.getLatLng(
ScreenCoordinate(x: offset.dx.round(), y: offset.dy.round()));
当拿到经纬度绘制在地图上时,发现绘制的位置偏移了很多,于是猜测根据屏幕位置获取的经纬度不准,为了验证这个问题,则进行代码测试一下:
onClickMap(LatLng latLng) async {
GoogleMapController controller = await mapController.future;
final screenCoordinate = await controller.getScreenCoordinate(latLng);
int x = screenCoordinate.x;
int y = screenCoordinate.y;
LatLng latLng2 = await controller.getLatLng(ScreenCoordinate(x: x, y: y));
print('地圖找房,onClickMap,latLng = $latLng, x = $x, y = $y, latLng2 = $latLng2');
}
日志信息:
地圖找房,onClickMap,latLng = LatLng(22.27059027662734, 114.1790759190917), x = 568, y = 1033, latLng2 = LatLng(22.270566075877795, 114.17907893657684)
根据日志信息可以发现经纬度从小数点第三位开始之后,就不一样了,于是又是一番资料查找,终于发现是因为ScreenCoordinate 要求 (x,y) 参数是实际像素的数量,所以需要将devicePixelRatio考虑在内,修正后的代码:
Offset offset = details.localPosition;
GoogleMapController controller = await mapController.future;
LatLng latLng = await controller.getLatLng(ScreenCoordinate(
x: (offset.dx * pixelRatio).round(),
y: (offset.dy * pixelRatio).round()));
拿到正确的经纬度之后,就可以开始在地图上画圈了~
计算范围
画完圈圈之后,那么又如何拿到圈圈内的标记呢?
/// 判断点是否在多边形内
bool _isPointInPolygon(LatLng point, List<LatLng> polygon) {
num ax = 0;
num ay = 0;
num bx = polygon[polygon.length - 1].latitude - point.latitude;
num by = polygon[polygon.length - 1].longitude - point.longitude;
int depth = 0;
for (int i = 0; i < polygon.length; i++) {
ax = bx;
ay = by;
bx = polygon[i].latitude - point.latitude;
by = polygon[i].longitude - point.longitude;
if (ay < 0 && by < 0) continue;
if (ay > 0 && by > 0) continue;
if (ax < 0 && bx < 0) continue;
num lx = ax - ay * (bx - ax) / (by - ay);
if (lx == 0) return true;
if (lx > 0) depth++;
}
return (depth & 1) == 1;
}
注意:使用google_maps_flutter时,发现一个bug,GoogleMap的markers/polygons可能会出现之前绘制出来的标记无法清除。 在使用这些属性时要注意,使用同一个对象