Skip to content

Instantly share code, notes, and snippets.

@sbis04

sbis04/blog.md Secret

Created May 30, 2020 09:16
Show Gist options
  • Save sbis04/db54ee4942c80bc10958a37c39325347 to your computer and use it in GitHub Desktop.
Save sbis04/db54ee4942c80bc10958a37c39325347 to your computer and use it in GitHub Desktop.

Creating a Route Calculator using Google Maps in Flutter

Google Maps is an essential part of your app if you want to conveniently convey details of places, directions, routes to the end-user. There is an Official Google Maps plugin available for Flutter.

In this article, I will show you how to integrate Google Maps in Flutter to find a route between two places and calculate its distance.

The topics that I am going to cover in this article are:

  • Create & setup a new Google Cloud Platform (GCP) project
  • Add Google Maps SDK (for both Android & iOS) and Directions API to the GCP project
  • Create an API Key on GCP
  • Setup Flutter project to use Google Maps
  • Add Google Maps widget
  • Use Geocoding to translate coordinates into a place address and vice versa
  • Use Polylines for drawing route between two places
  • Calculate distance of that route

There's a lot to learn from this article. So, kindly bear with me and follow along.

Creating a GCP project

You will need a GCP project for getting access to the Google Maps API and for generating the API key.

NOTE: If you have never used GCP before, you will have to setup a Billing account. You can follow the official guide here.

Follow the steps below for creating a new GCP project:

  1. Go to GCP Console.

  2. Go to Project Selection dialog-box and click New Project.

  3. Enter a Project Name and click on Create.

This will create a new GCP Project.

Enabling APIs

In order to use Google Maps in your app, you will need to enable Maps SDK for both the platforms.

  1. Go to APIs & Services from the left menu and select Library.

  2. Now, search for Maps and enable Maps SDK for both the platforms.

  3. You will also need the Directions API while drawing the routes. So, enable that also.

Generating API key

You will need an API key for integrating Google Maps to your app.

  1. Go to APIs & Services from the left menu and select Credentials.

  2. Click CREATE CREDENTIALS and select API key.

This will generate an API key for the project, you will need this in the next step.

Setup Flutter project

  • Create a new Flutter project.

    flutter create flutter_maps
  • Open the project using your favorite IDE. For opening with VS Code:

    code flutter_maps
  • Add the google_maps_flutter plugin to the pubspec.yaml file:

    google_maps_flutter: ^0.5.28+1

Android setup

  • Navigate to the file android/app/src/main/AndroidManifest.xml and add the following code snippet inside the application tag:

    <!-- Add your Google Maps API Key here -->
    <meta-data android:name="com.google.android.geo.API_KEY"
                   android:value="YOUR KEY HERE"/>
  • Also, you will need location access in the app. So, add the following permission in the same file inside the manifest tag:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

iOS setup

  • Navigate to the file ios/Runner/AppDelegate.swift and replace the whole code with the following:

    import UIKit
    import Flutter
    import GoogleMaps
    
    @UIApplicationMain
    @objc class AppDelegate: FlutterAppDelegate {
      override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        //Add your Google Maps API Key here
        GMSServices.provideAPIKey("YOUR KEY HERE")
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
      }
    }
  • Also, add the following to ios/Runner/Info.plist file:

    <key>io.flutter.embedded_views_preview</key>
    <string>YES</string>
  • For getting location permission, add the following to the same file:

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>This app needs access to location when open.</string>

This completes the setup for both the platforms in Flutter.

Integrating Google Maps Widget

Now, you are ready to add Google Maps Widget to your Flutter app.

The starting code that you can use is as follows:

https://gist.github.com/22969957a5ba8bacbe847b514b8624c3

I have defined the Container height and width to be the size of the screen so that the Google Maps widget takes up the entire space of the screen.

Also, I am using a Stack to keep Google Maps widget in the background and add other necessary widgets on top of it.

Now, replace the TODO in the above code snippet with the Google Maps widget.

https://gist.github.com/b2a991dc5d3e4f977565140d668ae7dc

Let's take a look at the parameters that I have defined in the Google Maps widget:

  • initialCameraPosition: This is a required parameter that is used for loading the Map view on initial startup.
  • myLocationEnabled: For showing your current location on Map with a blue dot.
  • myLocationButtonEnabled: Button used for bringing the user location to the center of the camera view.
  • mapType: For specifying the type of map to display (normal, satellite, hybrid & terrain).
  • zoomGesturesEnabled: Whether the map view should respond to zoom gestures.
  • zoomControlsEnabled: Whether to show zoom controls (only applicable for Android platform).
  • onMapCreated: Callback for when the map is ready to use.

I have set the myLocationButtonEnabled and zoomControlsEnabled to false because I am going to show you how to define a custom button with the same functionality but with better control.

The mapController would be used for controlling the camera position of the Map view.

The app will look like this now:

For displaying the zoom buttons and the current location button you can add them as a child to the Stack widget and position them accordingly.

The code for design of a button is given below:

https://gist.github.com/399a5b21175238789ebc11a83cc335c3

The remaining buttons have a similar design.

You can take a look at the code snippet for the design and positioning of the zoom buttons here and current location button here.

Perform zoom on Map view

You can use the mapController to perform a simple Zoom In and Zoom Out action.

https://gist.github.com/9723f9e672698f38e4914ed5e8b6f263

Move to a new position

For moving to a new position you can use the following code snippet:

https://gist.github.com/ee429c0cae322924a351583b21c5c49e

Here, I have specified only target & zoom property of the CameraPosition widget. But there are two more properties, bearing & tilt that you may use.

In the target property, you have to pass the latitude and longitude of the position you want to move.

Here, we need the Current Location button to move the camera view to the user's present location. Let's see how to achieve that.

After adding the buttons, the app will look like this:

Fetching current location

There is a nice plugin for Flutter, known as geolocator that can help you to fetch user's current location easily.

Add it to your pubspec.yaml file:

https://gist.github.com/6c9230912fd152a1418c2190a7c1e219

  1. Initialize Geolocator and define a variable.

    final Geolocator _geolocator = Geolocator();
    
    // For storing the current position
    Position _currentPosition;
  2. Get the current location of the user.

    // Method for retrieving the current location
    _getCurrentLocation() async {
      await _geolocator
          .getCurrentPosition(desiredAccuracy: LocationAccuracy.high)
          .then((Position position) async {
        setState(() {
          // Store the position in the variable
          _currentPosition = position;
    
          print('CURRENT POS: $_currentPosition');
    
          // For moving the camera to current location
          mapController.animateCamera(
            CameraUpdate.newCameraPosition(
              CameraPosition(
                target: LatLng(position.latitude, position.longitude),
                zoom: 18.0,
              ),
            ),
          );
        });
      }).catchError((e) {
        print(e);
      });
    }
  3. Add this method to the initState in order to fetch the current location of the user as soon as the app launches, and also move the camera to the detected location.

    @override
    void initState() {
      super.initState();
      _getCurrentLocation();
    }
  4. Also pass the latitude & longitude to the onTap method of the custom button for fetching user's current location.

After completing these, the camera will automatically move to the detected location as the app launches.

Geocoding

As you have the initial app setup and working, now you can move on to adding two places and detecting their route on Map. For this, you will need the latitude and longitude of the two places. But, for any user knowing the geographic coordinates of a place is a hard task. So, here comes Geocoding.

Geocoding is a technique by which the address of a place can be converted to their coordinates (latitude & longitude) and vice versa.

The Flutter package geolocator also supports Geocoding. For getting started with geocoding, you will need to take the addresses as user inputs using TextField.

You can find the code for the design of the TextField here.

If you want to show the address of the starting location, then you have to define a TextEditingController for the starting address and update the text as soon as the address of the user's present location is retrieved.

https://gist.github.com/513bcb959c23b83181da5e1dd93bb133

You can retrieve the address of the starting location by using the following method:

https://gist.github.com/87b855a5d459dd69ee2c67faea8fa8f0

You can use a similar method for retrieving the coordinates from the starting address and the destination address. You will require these coordinates in order to draw a route on the Map and for placing Markers.

https://gist.github.com/76dbe3ef7e25a4cecb5cdcf96d8f3209

Placing Markers

You can use the coordinates retrieved in the previous step for placing Markers on the Map.

  • First of all, define a variable for storing the Markers:

    Set<Marker> markers = {};
  • Create the Markers:

    // Start Location Marker
    Marker startMarker = Marker(
      markerId: MarkerId('$startCoordinates'),
      position: LatLng(
        startCoordinates.latitude,
        startCoordinates.longitude,
      ),
      infoWindow: InfoWindow(
        title: 'Start',
        snippet: _startAddress,
      ),
      icon: BitmapDescriptor.defaultMarker,
    );
    
    // Destination Location Marker
    Marker destinationMarker = Marker(
      markerId: MarkerId('$destinationCoordinates'),
      position: LatLng(
        destinationCoordinates.latitude,
        destinationCoordinates.longitude,
      ),
      infoWindow: InfoWindow(
        title: 'Destination',
        snippet: _destinationAddress,
      ),
      icon: BitmapDescriptor.defaultMarker,
    );
  • Add the Markers:

    // Add the markers to the list
    markers.add(startMarker);
    markers.add(destinationMarker);
  • Display the Markers on the Map:

    // Add the markers property to the widget
    GoogleMap(
      markers: markers != null ? Set<Marker>.from(markers) : null,
      // ...
    ),

If you run the app now, it will look like this:

You will notice that only one of the Markers is visible, although you had placed two Markers, one at the starting point and the other at the destination point.

So, what's happening here?

Actually, both the Markers are added to the Map, but only one of them is visible because the other one is outside the Map View. If you zoom out a bit, you will notice the other one too.

You can use the following code snippet for reorienting the Map View to accommodate both the Markers.

https://gist.github.com/ec9402f37dec341dd8401e6fdce3ca26

Drawing Route

Polyline is used for drawing routes on the Google Maps. A Polyline is a list of points, where line segments are drawn between consecutive points.

There is a Flutter package available for drawing polylines, known as flutter_polyline_points. It uses the Directions API that you had enabled in the beginning.

  • Add the package to your pubspec.yaml file.

    flutter_polyline_points: ^0.2.1
  • Define some variables.

    // Object for PolylinePoints
    PolylinePoints polylinePoints;
    
    // List of coordinates to join
    List<LatLng> polylineCoordinates = [];
    
    // Map storing polylines created by connecting
    // two points
    Map<PolylineId, Polyline> polylines = {};
  • Define a method for creating the Polylines. You have to pass the starting and destination positions.

    // Create the polylines for showing the route between two places
    
    _createPolylines(Position start, Position destination) async {
      // Initializing PolylinePoints
      polylinePoints = PolylinePoints();
    
      // Generating the list of coordinates to be used for
      // drawing the polylines
      PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
        Secrets.API_KEY, // Google Maps API Key 
        PointLatLng(start.latitude, start.longitude),
        PointLatLng(destination.latitude, destination.longitude),
        travelMode: TravelMode.transit,
      );
    
      // Adding the coordinates to the list
      if (result.points.isNotEmpty) {
        result.points.forEach((PointLatLng point) {
          polylineCoordinates.add(LatLng(point.latitude, point.longitude));
        });
      }
    
      // Defining an ID
      PolylineId id = PolylineId('poly');
    
      // Initializing Polyline
      Polyline polyline = Polyline(
        polylineId: id,
        color: Colors.red,
        points: polylineCoordinates,
        width: 3,
      );
    
      // Adding the polyline to the map
      polylines[id] = polyline;
    }
  • Display polyline on the Map.

    // Add the polylines property to the widget
    GoogleMap(
      polylines: Set<Polyline>.of(polylines.values),
      // ...
    ),

After drawing the route, the app will look like this:

Calculating Distance

Now, you have arrived at the last phrase of this project. With the markers properly positioned and the route drawn, you might want to calculate the distance between the two locations.

There is a method that comes with the geolocator package using which you can calculate the distance between two places just by providing their coordinates.

https://gist.github.com/fcd45637b9d0f3f41c9d8517897bda82

Yes, this method does not calculate the distance of the actual route, rather it uses the Haversine formula to calculate the distance based upon the supplied GPS coordinates.

More information about this here.

So, what you can do is break the entire route into several small parts and calculate their distances, then sum them up. Remember, you have already calculated the coordinates while drawing the polyline. You can use these coordinates to calculate the total distance of the route.

The formula that I have used for calculating the distance between two geographic coordinates is as follows:

https://gist.github.com/26e6faa8d703fcefc704fad4d2eba492

Now, you can use this formula to calculate the distances between two coordinates and sum them up to find the total distance.

https://gist.github.com/7253762591e4431c65943a08fa192c70

Show the calculated distance on the UI:

https://gist.github.com/f60989f63452df7e1e0c2310638bf6fe

The final app will look like this:

Conclusion

There's a lot more you can do using Google Maps and some of its supported APIs. I hope this article will help you to get started with Google Maps in Flutter.

  • The project is available on GitHub here.

  • You can find the Official Documentation for Google Maps Platform here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment