带有实时位置跟踪的Flutter谷歌地图--Uber风格
通过本课,您将学习如何在Flutter中使用谷歌地图,并进行一些定制,如设置自定义图像标记和绘制路线方向折线。在地图上添加实时位置更新。
内容表 :
- 初始设置 ⚙️
- 谷歌地图 🗺
- 绘制路线方向 〰
- 在地图上实时更新位置 🔴
- 添加自定义标记/图钉 📍
注意:这篇文章假设您已经在您的项目中使用谷歌地图Flutter包和您自己的谷歌地图API密钥设置了地图。如果没有,请点击此链接,了解如何设置您的Flutter项目与谷歌地图一起工作。其他依赖项包括Flutter Polyline Points包和Flutter Location Plugin。
初始设置 ⚙️
请确保你对你的环境做了相应的准备,以便在IOS和Android上启用位置跟踪,具体方法是按照软件包的README中关于Android清单文件和iOS Info.plist的步骤进行。
一旦设置好,依赖关系看起来像👇
....
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
flutter_polyline_points: ^1.0.0
google_maps_flutter: ^2.1.7
location: ^4.4.0
...
注意:截至本文写作时,上述软件包的版本是可用的–请相应更新。
谷歌地图 🗺
创建一个名为OrderTrackingPage的StatefulWidget及其相应的State类,在这里我导入了所需的包以及一些硬编码的源和目标位置(为了本教程的目的)。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class OrderTrackingPage extends StatefulWidget {
const OrderTrackingPage({Key? key}) : super(key: key);
State<OrderTrackingPage> createState() => OrderTrackingPageState();
}
class OrderTrackingPageState extends State<OrderTrackingPage> {
final Completer<GoogleMapController> _controller = Completer();
static const LatLng sourceLocation = LatLng(37.33500926, -122.03272188);
static const LatLng destination = LatLng(37.33429383, -122.06600055);
Widget build(BuildContext context) {
return Scaffold(
body: ... GoogleMap widget will be here ...,
);
}
}
创建GoogleMap小组件,并将initialCameraPosition设置为源的位置。地图需要放大一些,所以把它设置为13.5。
我们需要一个标记/图钉来了解确切的位置。定义一个标记,并将其位置设置为源位置。对于目的地,添加另一个标记/图钉。
GoogleMap(
initialCameraPosition: const CameraPosition(
target: sourceLocation,
zoom: 13.5,
),
markers: {
const Marker(
markerId: MarkerId("source"),
position: sourceLocation,
),
const Marker(
markerId: MarkerId("destination"),
position: destination,
),
},
onMapCreated: (mapController) {
_controller.complete(mapController);
},
),
绘制路线方向 〰
接下来我想做的是画一条从目的地到源头的线。
创建一个名为polylineCoordinates的空列表。
创建一个PolylinePoints的实例和一个叫做getPolyPoints的异步函数。getRouteBetweenCoordinates方法返回多线点的列表。
需要谷歌API密钥、源和目的地位置。如果这些点不是空的,我们将它们存储到polylineCoordinates。
List<LatLng> polylineCoordinates = [];
void getPolyPoints() async {
PolylinePoints polylinePoints = PolylinePoints();
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
google_api_key, // Your Google Map Key
PointLatLng(sourceLocation.latitude, sourceLocation.longitude),
PointLatLng(destination.latitude, destination.longitude),
);
if (result.points.isNotEmpty) {
result.points.forEach(
(PointLatLng point) => polylineCoordinates.add(
LatLng(point.latitude, point.longitude),
),
);
setState(() {});
}
}
在 initState方法中调用 getPolyPoints
void initState() {
getPolyPoints();
super.initState();
}
回到GoogleMap小组件,定义多段线。
GoogleMap(
...
polylines: {
Polyline(
polylineId: const PolylineId("route"),
points: polylineCoordinates,
color: const Color(0xFF7B61FF),
width: 6,
),
},
),
地图上的实时位置更新 🔴
现在到了最令人兴奋的部分,我们需要设备的位置。
创建一个名为currentLocation的可空变量。
然后用一个叫getCurrentLocation的函数,在里面创建一个Location的实例。一旦我们得到了位置,将当前位置设置为等于该位置。
在位置改变时,更新当前位置。使其在地图上可见,称为setState。
LocationData? currentLocation;
void getCurrentLocation() async {
Location location = Location();
location.getLocation().then(
(location) {
currentLocation = location;
},
);
GoogleMapController googleMapController = await _controller.future;
location.onLocationChanged.listen(
(newLoc) {
currentLocation = newLoc;
googleMapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
zoom: 13.5,
target: LatLng(
newLoc.latitude!,
newLoc.longitude!,
),
),
),
);
setState(() {});
},
);
}
请确保在initState上调用getCurrentLocation。
void initState() {
getPolyPoints();
getCurrentLocation();
super.initState();
}
如果currentLocation是空的,它显示一个加载文本。同时,为currentLocation添加另一个标记/图钉,以及将初始摄像机位置改为当前位置。
body: currentLocation == null
? const Center(child: Text("Loading"))
: GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(
currentLocation!.latitude!, currentLocation!.longitude!),
zoom: 13.5,
),
markers: {
Marker(
markerId: const MarkerId("currentLocation"),
position: LatLng(
currentLocation!.latitude!, currentLocation!.longitude!),
),
const Marker(
markerId: MarkerId("source"),
position: sourceLocation,
),
const Marker(
markerId: MarkerId("destination"),
position: destination,
),
},
onMapCreated: (mapController) {
_controller.complete(mapController);
},
polylines: {
Polyline(
polylineId: const PolylineId("route"),
points: polylineCoordinates,
color: const Color(0xFF7B61FF),
width: 6,
),
},
),
对于iOS ::
进入功能,将鼠标悬停在位置上,选择Freeway drive。我是根据这个Freeway驱动器使用源和目标位置。
对于安卓系统 ::
如果你在Windows上或使用安卓模拟器,点击底部的三个点,并确保你在位置上。
比方说,源位置是谷歌Plex,把sourceLocation改为这个坐标,目的地位置是微软硅谷园区。
把目的地改为这个位置。现在点击 “路线 “标签,搜索微软硅谷和谷歌Plex作为起点。保存路线,设置一个播放速度,点击播放路线。
当前的位置正在移动,这就是我们想要的:
static const LatLng sourceLocation = your chosen location
static const LatLng destination = your chosen location
添加自定义标记/图钉 📍
来源、目的地和当前位置的图标是一样的。让我们为它们使用一个自定义标记/图钉。
BitmapDescriptor sourceIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor destinationIcon = BitmapDescriptor.defaultMarker;
BitmapDescriptor currentLocationIcon = BitmapDescriptor.defaultMarker;
void setCustomMarkerIcon() {
BitmapDescriptor.fromAssetImage(
ImageConfiguration.empty, "assets/Pin_source.png")
.then(
(icon) {
sourceIcon = icon;
},
);
BitmapDescriptor.fromAssetImage(
ImageConfiguration.empty, "assets/Pin_destination.png")
.then(
(icon) {
destinationIcon = icon;
},
);
BitmapDescriptor.fromAssetImage(
ImageConfiguration.empty, "assets/Badge.png")
.then(
(icon) {
currentLocationIcon = icon;
},
);
}
在initState上调用setCustomMarkerIcon
void initState() {
getPolyPoints();
getCurrentLocation();
setCustomMarkerIcon();
super.initState();
}
在标记集的图标上做最后的润色:
GoogleMap(
....
markers: {
Marker(
markerId: const MarkerId("currentLocation"),
icon: currentLocationIcon,
position: LatLng(
currentLocation!.latitude!, currentLocation!.longitude!),
),
Marker(
markerId: const MarkerId("source"),
icon: sourceIcon,
position: sourceLocation,
),
Marker(
markerId: MarkerId("destination"),
icon: destinationIcon,
position: destination,
),
},
),