Flutter谷歌地图路线规划插件google_maps_directions的使用

发布于 1周前 作者 gougou168 来自 Flutter

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_flutterflutter_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

1 回复

更多关于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.xmlInfo.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'),
        ));
      });
    }
  }
}

注意事项

  1. API密钥:在GoogleMapsDirections构造函数中替换'YOUR_API_KEY'为你的实际谷歌地图API密钥。
  2. 权限:确保在Android和iOS项目中正确配置了API密钥,并且应用有权限访问网络。
  3. 错误处理:示例代码中没有包含错误处理逻辑。在实际应用中,应该添加适当的错误处理来应对API请求失败或数据解析错误等情况。

这个示例展示了如何使用google_maps_directions插件来获取路线信息,并在地图上显示起点、终点和路线。你可以根据需要进行进一步的定制和扩展。

回到顶部