Drawing Route Lines on Google Maps Between Two Locations in Flutter

Roman Jaquez
Flutter Community
Published in
5 min readNov 30, 2019

(Hint: It’s just a bunch of connected lines)

Note: This post assumes you already have the maps set up in your project using the Google Maps Flutter Package, as well as have your own Google Maps API key and enable the Google Directions API for the routelines to show. If not, follow this link on how to set up your Flutter project to work with Google Maps. Github project for this tutorial here.

When I came to realize how easy it was to implement route lines on a Google Map (thanks to the Flutter Polyline Points Package!) I decided to create a quick post about this to share my experiences with this package and my process of implementing it.

Settting it up

After setting up Google Maps on my Flutter Application, I made sure my dependencies were correctly specified on my pubspec.yaml, as follows:

...
dependencies:
flutter:
sdk: flutter
google_maps_flutter: ^0.5.11+1
flutter_polyline_points: ^0.1.0
...(rest of the file omitted for brevity)...

I made sure my required imports were properly in place, plus some other constant variables I’d be using throughout my code (for this sample, I’m hardcoding the locations for demo purposes) and set up my StatefulWidget and State, which wraps my Google Map implementation; i’m calling MapPage and MapPageState respectively.

import ‘package:flutter_polyline_points/flutter_polyline_points.dart’;
import ‘package:google_maps_flutter/google_maps_flutter.dart’;
import ‘dart:async’;
import ‘package:flutter/material.dart’;
const double CAMERA_ZOOM = 13;
const double CAMERA_TILT = 0;
const double CAMERA_BEARING = 30;
const LatLng SOURCE_LOCATION = LatLng(42.7477863, -71.1699932);
const LatLng DEST_LOCATION = LatLng(42.6871386, -71.2143403);
class MapPage extends StatefulWidget { @override
State<StatefulWidget> createState() => MapPageState();
}
class MapPageState extends State<MapPage> {
...(rest of the code will be here)...
}

In my MapState, I set up some properties upfront, such as the GoogleMapController, as well as collections and sets to hold my markers, polylines, polyline points and its coordinates.

Completer<GoogleMapController> _controller = Completer();// this set will hold my markers
Set<Marker> _markers = {};
// this will hold the generated polylines
Set<Polyline> _polylines = {};
// this will hold each polyline coordinate as Lat and Lng pairs
List<LatLng> polylineCoordinates = [];
// this is the key object - the PolylinePoints
// which generates every polyline between start and finish
PolylinePoints polylinePoints = PolylinePoints();
String googleAPIKey = “<YOUR_API_KEY>”;// for my custom icons
BitmapDescriptor sourceIcon;
BitmapDescriptor destinationIcon;

In essence, a Polyline is nothing more than a bunch of lines connected together by points. Each point is a pair of latitude and longitude coordinates. Each line represents a pair of points; all lines connected together then represent the entire route, from beginning to end.

Let’s proceed with the code.

To give this tutorial a touch of uniqueness, I customized the map pins (for how to customize the map pins, follow this link), so therefore I set my source and destination icons inside the initState method.

@override
void initState() {
super.initState();
setSourceAndDestinationIcons();
}
void setSourceAndDestinationIcons() async {
sourceIcon = await BitmapDescriptor.fromAssetImage(
ImageConfiguration(devicePixelRatio: 2.5),
‘assets/driving_pin.png’);
destinationIcon = await BitmapDescriptor.fromAssetImage(
ImageConfiguration(devicePixelRatio: 2.5),
‘assets/destination_map_marker.png’);
}

I proceed to override my build method as usual and make the Google Map occupy the full widget:

@override
Widget build(BuildContext context) {
CameraPosition initialLocation = CameraPosition(
zoom: CAMERA_ZOOM,
bearing: CAMERA_BEARING,
tilt: CAMERA_TILT,
target: SOURCE_LOCATION
);
return GoogleMap(
myLocationEnabled: true,
compassEnabled: true,
tiltGesturesEnabled: false,
markers: _markers,
polylines: _polylines,
mapType: MapType.normal,
initialCameraPosition: initialLocation,
onMapCreated: onMapCreated
);
}

As you noticed, the Google Map already takes as input a Set of Markers and Polylines, which I’m leveraging to programatically add markers and polylines.
I handle the onMapCreated event in a separate method named the same way, whose signature must take as a parameter the GoogleMapController associated with this map. Inside this method, I defined two other methods for setting the pins on the map, and then, lay down the polylines on the map.

... (rest of the code omitted for brevity)...void onMapCreated(GoogleMapController controller) {
_controller.complete(controller);
setMapPins();
setPolylines();

}

I set my map pins using the custom BitmapDescriptor objects I created above for customization purposes, and add them to the set of markers defined above:

...(rest of the code omitted for brevity)...void setMapPins() {   setState(() {
// source pin
_markers.add(Marker(
markerId: MarkerId(‘sourcePin’),
position: SOURCE_LOCATION,
icon: sourceIcon
));
// destination pin
_markers.add(Marker(
markerId: MarkerId(‘destPin’),
position: DEST_LOCATION,
icon: destinationIcon
));
});
}

And finally, set the polylines on the map, provided the source and destination as a pair of latitude and longitude values and the Google Maps API Key to the getRouteBetweenCoordinates method, which conveniently returns a list of PointLatLng objects, representing each point along the path between source and destination where polylines can be drawn in between. This is an asynchronous call so make sure to decorate your method as async and perform an await on this call, as well as adding the polylines within the setState() method so as to reflect on the map upon being added to the polylines list set.

...(rest of the code omitted for brevity)...setPolylines() async {   List<PointLatLng> result = await
polylinePoints?.getRouteBetweenCoordinates(
googleAPIKey,
SOURCE_LOCATION.latitude,
SOURCE_LOCATION.longitude,
DEST_LOCATION.latitude,
DEST_LOCATION.longitude);
if(result.isNotEmpty){ // loop through all PointLatLng points and convert them
// to a list of LatLng, required by the Polyline
result.forEach((PointLatLng point){
polylineCoordinates.add(
LatLng(point.latitude, point.longitude));

});
}
setState(() {
// create a Polyline instance
// with an id, an RGB color and the list of LatLng pairs
Polyline polyline = Polyline(
polylineId: PolylineId(“poly”),
color: Color.fromARGB(255, 40, 122, 198),
points: polylineCoordinates
);

// add the constructed polyline as a set of points
// to the polyline set, which will eventually
// end up showing up on the map
_polylines.add(polyline);
});
}

And you should be all set!
Here’s the Github Repo with a sample project with the above code.

Stay tuned for an upcoming tutorial on how to track the position of a pin along a route line in real time — Uber-style — as the user moves.

Happy Fluttering!

--

--

Roman Jaquez
Flutter Community

Flutter GDE / GDG Lawrence Lead Organizer / Follow me on Twitter @drcoderz — Subscribe to my YouTube Channel https://tinyurl.com/romanjustcodes