Flutter谷歌地图路线规划插件google_maps_directions的使用
Flutter谷歌地图路线规划插件 google_maps_directions
的使用
简介
google_maps_directions
是一个用于获取两个点之间的最短路径、计算距离和时间的 Flutter 插件。它利用了 Google Maps Directions API 来实现这些功能。
特性
- 获取两个点之间的方向。
- 计算两个点之间的距离或时间(对应于最短路径的距离/时间评估)。
- 获取两个点之间的最短路径。
入门指南
基本对象
- AddressPoint:包含纬度和经度的对象。
- DirectionRoute:两个点之间步行、摩托车、火车或汽车的路径。
使用方法
初始化插件
你需要一个有效的 Google Maps Routes API 密钥。
const String googleAPIKey = "YOUR_GOOGLE_API_KEY";
// 你可以通过以下方式初始化插件,这样就不需要在每个方法中传递 API 密钥了。
GoogleMapsDirections.init(googleAPIKey: googleAPIKey);
获取方向
import "package:google_maps_directions/google_maps_directions.dart" as gmd;
...
Directions directions = await gmd.getDirections(
p1.lat,
p1.lng,
p2.lat,
p2.lng,
language: "fr_FR", // 可选参数,设置语言
);
DirectionRoute route = directions.shortestRoute;
在地图上绘制路径
你可以使用 google_maps_flutter
和 flutter_polyline_points
包在地图上绘制路径。
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:google_maps_directions/google_maps_directions.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
...
DirectionRoute route = directions.shortestRoute;
List<LatLng> points = PolylinePoints().decodePolyline(route.overviewPolyline.points)
.map((point) => LatLng(point.latitude, point.longitude))
.toList();
List<Polyline> polylines = [
Polyline(
width: 5,
polylineId: PolylineId("UNIQUE_ROUTE_ID"),
color: Colors.green,
points: points,
),
];
...
@override
Widget build(BuildContext context) {
return Scaffold(
body: GoogleMap(
polylines: Set.of(polylines),
// 其他配置...
),
);
}
获取路径指示
你还可以获取路径中的每个阶段的指示。
import 'package:google_maps_directions/google_maps_directions.dart';
...
DirectionRoute route = directions.shortestRoute;
DirectionLegStep firstRouteLegStep = route.shortestLeg.steps.first;
print(firstRouteLegStep.htmlInstructions); // At the roundabout, take the exit onto route National 1.
print(firstRouteLegStep.maneuver); // roundabout-right.
计算距离
import "package:google_maps_directions/google_maps_directions.dart" as gmd;
DistanceValue distanceBetween = await gmd.distance(
9.2460524,
1.2144565,
6.1271617,
1.2345417,
googleAPIKey: googleAPIKey // 如果插件已经初始化,可以省略此参数
);
int meters = distanceBetween.meters;
String textInKmOrMeters = distanceBetween.text;
计算时间
import "package:google_maps_directions/google_maps_directions.dart" as gmd;
DurationValue durationBetween = await gmd.duration(
9.2460524,
1.2144565,
6.1271617,
1.2345417,
googleAPIKey: googleAPIKey // 如果插件已经初始化,可以省略此参数
);
int seconds = durationBetween.seconds;
String durationInMinutesOrHours = durationBetween.text;
示例代码
以下是一个完整的示例代码,展示了如何使用 google_maps_directions
插件来获取两个点之间的方向并在地图上绘制路径。
import 'package:flutter/material.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:google_maps_directions/google_maps_directions.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Google Maps Distance',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DistanceBetweenTwoPoints(),
);
}
}
class DistanceBetweenTwoPoints extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _DistanceBetweenTwoPoints();
}
}
class _DistanceBetweenTwoPoints extends StatefulWidget {
@override
_DistanceBetweenTwoPointsState createState() => _DistanceBetweenTwoPointsState();
}
class _DistanceBetweenTwoPointsState extends State<_DistanceBetweenTwoPoints> {
static const LatLng center = LatLng(6.1470738, 1.2170085);
Map<MarkerId, Marker> markers = {};
PolylinePoints polylinePoints = PolylinePoints();
GoogleMapController? controller;
LatLng? markerPosition;
MarkerId? selectedMarker;
final CameraPosition _initialCameraPosition = const CameraPosition(
target: center,
zoom: 17.0,
);
final GlobalKey<ScaffoldState> _key = GlobalKey();
AddressPoint? _destination;
Directions? _directions;
String? _googleAPiKey;
AddressPoint? _origin;
List<Polyline>? _polylines;
@override
void dispose() {
super.dispose();
}
@override
void initState() {
super.initState();
load();
}
String get googleAPiKey => _googleAPiKey!;
GoogleMapController get googleMapController => controller!;
bool get loading => controller == null || _googleAPiKey == null;
List<Polyline> get polylines => _polylines ?? [];
DirectionRoute get shortestRoute => _directions!.shortestRoute;
Future<void> load() async {
await _setupApiKeys();
}
void _add(LatLng point) {
final MarkerId markerId = MarkerId(point.toString());
if (_origin == null) {
_origin = AddressPoint(lat: point.latitude, lng: point.longitude);
} else {
_destination = AddressPoint(lat: point.latitude, lng: point.longitude);
}
final Marker marker = Marker(
markerId: markerId,
position: LatLng(point.latitude, point.longitude),
infoWindow: InfoWindow(
title: "${point.latitude},${point.longitude}",
snippet: point.toString(),
),
onTap: () => _onMarkerTapped(markerId),
);
if (_origin != null && _destination != null) {
_setupRoutes(_origin!, _destination!);
}
setState(() {
markers[markerId] = marker;
});
}
void _onMapCreated(GoogleMapController controller) {
this.controller = controller;
}
void _onMarkerTapped(MarkerId markerId) {
final Marker? tappedMarker = markers[markerId];
if (tappedMarker != null) {
setState(() {
final MarkerId? previousMarkerId = selectedMarker;
if (previousMarkerId != null && markers.containsKey(previousMarkerId)) {
final Marker resetOld = markers[previousMarkerId]!
.copyWith(iconParam: BitmapDescriptor.defaultMarker);
markers[previousMarkerId] = resetOld;
}
selectedMarker = markerId;
final Marker newMarker = tappedMarker.copyWith(
iconParam: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueGreen,
),
);
markers[markerId] = newMarker;
markerPosition = null;
});
}
}
Future<void> _setupApiKeys() async {
_googleAPiKey = "YOUR_GOOGLE_API_KEY";
GoogleMapsDirections.init(googleAPIKey: _googleAPiKey ?? "");
setState(() {});
}
Future<void> _setupRoutes(AddressPoint p1, AddressPoint p2) async {
_polylines = [];
try {
Directions directions = await getDirections(
p1.lat,
p1.lng,
p2.lat,
p2.lng,
language: "fr_FR",
);
_directions = directions;
List<PointLatLng> results = polylinePoints.decodePolyline(
directions.routes.first.overviewPolyline.points,
);
LatLng origin = LatLng(p1.lat, p1.lng);
LatLng destination = LatLng(p2.lat, p2.lng);
final MarkerId originMarkerId = MarkerId(origin.toString());
final MarkerId destinationMarkerId = MarkerId(destination.toString());
setState(() {
markers[originMarkerId] = Marker(
markerId: originMarkerId,
position: origin,
infoWindow: InfoWindow(
title: directions.shortestRoute.shortestLeg.startAddress,
snippet: origin.toString(),
),
onTap: () => _onMarkerTapped(originMarkerId),
);
markers[destinationMarkerId] = Marker(
markerId: destinationMarkerId,
position: destination,
infoWindow: InfoWindow(
title: directions.shortestRoute.shortestLeg.endAddress,
snippet: destination.toString(),
),
onTap: () => _onMarkerTapped(destinationMarkerId),
);
});
if (results.isNotEmpty) {
_polylines!.add(
Polyline(
width: 5,
polylineId: PolylineId("${p1.lat}-${p1.lng}_${p2.lat}-${p2.lng}"),
color: Colors.green,
points: results
.map((point) => LatLng(point.latitude, point.longitude))
.toList(),
),
);
}
setState(() {});
} catch (e) {
debugPrint(e.toString());
}
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
key: _key,
body: Stack(
children: [
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: _initialCameraPosition,
markers: Set<Marker>.of(markers.values),
polylines: Set.of(loading ? [] : polylines),
zoomControlsEnabled: false,
onTap: _add,
),
Positioned(
top: 0,
left: 0,
child: (_destination == null ||
_origin == null ||
_directions == null)
? const SizedBox.shrink()
: Container(
padding: const EdgeInsets.all(10),
color: Colors.white,
height: 150,
width: MediaQuery.of(context).size.width,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.08,
child: IconButton(
onPressed: () {
setState(() {
markers.remove(
MarkerId(LatLng(
_destination!.lat,
_destination!.lng,
).toString()),
);
markers.remove(
MarkerId(LatLng(
_origin!.lat,
_origin!.lng,
).toString()),
);
_destination = null;
_origin = null;
_directions = null;
_polylines = [];
});
},
icon: const Icon(Icons.arrow_back),
),
),
SizedBox(width: MediaQuery.of(context).size.width * 0.045),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: TextFormField(
initialValue: shortestRoute.shortestLeg.startAddress,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
),
SizedBox(width: MediaQuery.of(context).size.width * 0.1)
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: TextFormField(
initialValue: shortestRoute.shortestLeg.endAddress,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.12,
child: const IconButton(
onPressed: null,
icon: Icon(
Icons.keyboard_double_arrow_up_sharp,
),
),
),
],
),
],
),
),
),
],
),
floatingActionButton: (_destination == null || _origin == null)
? FloatingActionButton(
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.black,
onPressed: () => googleMapController.animateCamera(
_directions == null
? CameraUpdate.newCameraPosition(_initialCameraPosition)
: CameraUpdate.newLatLngBounds(
LatLngBounds(
southwest: LatLng(
_directions!.routes.first.bounds.southwest.lat,
_directions!.routes.first.bounds.southwest.lng,
),
northeast: LatLng(
_directions!.routes.first.bounds.northeast.lat,
_directions!.routes.first.bounds.northeast.lng,
),
),
100.0,
),
),
child: const Icon(Icons.center_focus_strong),
)
: null,
bottomSheet: (_destination == null || _origin == null)
? null
: BottomSheet(
onClosing: () {},
builder: (context) {
return Container(
padding: const EdgeInsets.all(12),
color: Colors.white,
height: 150,
width: MediaQuery.of(context).size.width,
child: _directions == null
? const Center(child: CircularProgressIndicator())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${shortestRoute.shortestLeg.duration.text} (${shortestRoute.shortestLeg.distance.text})",
style: const TextStyle(fontSize: 24),
),
Text(
"Route la plus rapide selon l'état actuel de la circulation (${shortestRoute.summary}).",
style: const TextStyle(fontSize: 15),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Colors.black45,
elevation: 6,
shape: const StadiumBorder(),
),
onPressed: null,
child: const Row(
children: [
Icon(
Icons.keyboard_double_arrow_right_outlined,
size: 15,
color: Colors.white,
),
Text(
"Apercu",
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
],
),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Colors.green[900],
elevation: 6,
shape: const StadiumBorder(),
),
onPressed: () {},
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.list,
color: Colors.white,
),
Text(
"Étapes",
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
],
),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Colors.black45,
elevation: 6,
shape: const StadiumBorder(),
),
onPressed: null,
child: const Row(
children: [
Icon(
CupertinoIcons.pin,
size: 15,
color: Colors.white,
),
Text(
"Épinglé",
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
],
),
),
],
)
],
),
);
},
),
),
);
}
}
问题与功能请求
如果你有任何问题、建议或功能请求,请在 package issues tracker 上提交。
希望这篇文档对你有所帮助!如果你有任何其他问题或需要进一步的帮助,请随时联系我。
更多关于Flutter谷歌地图路线规划插件google_maps_directions的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter谷歌地图路线规划插件google_maps_directions的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用google_maps_directions
插件来实现谷歌地图路线规划的示例代码。这个示例将展示如何获取路线信息并在地图上显示。
首先,确保你的Flutter项目已经添加了必要的依赖。在pubspec.yaml
文件中添加以下依赖:
dependencies:
flutter:
sdk: flutter
google_maps_flutter: ^2.1.1 # 根据需要调整版本
google_maps_directions: ^2.0.0 # 根据需要调整版本
然后运行flutter pub get
来安装这些依赖。
接下来,你需要在Android和iOS项目中分别配置API密钥以使用谷歌地图服务。这通常涉及在AndroidManifest.xml
和Info.plist
文件中添加API密钥。
主代码示例
以下是一个完整的Flutter应用示例,它展示了如何使用google_maps_directions
插件来获取并显示路线:
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_directions/google_maps_directions.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Google Maps Directions Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MapScreen(),
);
}
}
class MapScreen extends StatefulWidget {
@override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
GoogleMapController _controller;
Set<Marker> _markers = {};
Polyline _polyline;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Google Maps Directions Demo'),
),
body: Stack(
children: <Widget>[
GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 14.0,
),
onMapCreated: (GoogleMapController controller) {
_controller = controller;
_getDirections();
},
markers: _markers,
polylines: _polyline == null ? [] : [_polyline],
),
Positioned(
bottom: 10.0,
right: 10.0,
child: FloatingActionButton(
onPressed: () => _getDirections(),
tooltip: 'Get Directions',
child: Icon(Icons.directions),
),
),
],
),
);
}
Future<void> _getDirections() async {
final GoogleMapsDirections _googleMapsDirections = GoogleMapsDirections(
apiKey: 'YOUR_API_KEY', // 替换为你的谷歌地图API密钥
);
DirectionsResult result = await _googleMapsDirections.getDirections(
origin: Location(lat: 37.7749, lng: -122.4194),
destination: Location(lat: 34.0522, lng: -118.2437),
travelMode: TravelMode.driving,
);
if (result?.routes?.isNotEmpty ?? false) {
final PolylinePoints polylinePoints = PolylinePoints();
final List<LatLng> points = polylinePoints.decodePolyline(
result.routes[0].legs[0].points,
);
setState(() {
_polyline = Polyline(
polylineId: PolylineId('route'),
color: Colors.blue.withOpacity(0.7),
width: 4,
points: points,
);
_markers.clear();
_markers.add(Marker(
markerId: MarkerId('origin'),
position: LatLng(37.7749, -122.4194),
infoWindow: InfoWindow(title: 'Origin', snippet: 'Start Point'),
));
_markers.add(Marker(
markerId: MarkerId('destination'),
position: LatLng(34.0522, -118.2437),
infoWindow: InfoWindow(title: 'Destination', snippet: 'End Point'),
));
});
}
}
}
注意事项
- API密钥:在
GoogleMapsDirections
构造函数中替换'YOUR_API_KEY'
为你的实际谷歌地图API密钥。 - 权限:确保在Android和iOS项目中正确配置了API密钥,并且应用有权限访问网络。
- 错误处理:示例代码中没有包含错误处理逻辑。在实际应用中,应该添加适当的错误处理来应对API请求失败或数据解析错误等情况。
这个示例展示了如何使用google_maps_directions
插件来获取路线信息,并在地图上显示起点、终点和路线。你可以根据需要进行进一步的定制和扩展。