Using google map with flutter web

keisuke ishikura
6 min readAug 4, 2020

--

The official package, google_maps_flutter, is not web-enabled as of August 1, 2020.
So I used google_maps, which is a javascript api wrapper.
If you have only used google_maps_flutter, I thought you would be confused by the difference in implementation between google_maps_flutter, and google_maps, so I have summarized how to implement the typical functions in google_maps.

Environment

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, 1.20.0-7.3.pre, on Mac OS X 10.15.5 19F101, locale ja-JP)

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.0)
[✓] VS Code (version 1.47.0)
[✓] Connected device (2 available)

the javascript api import

Import javascript api into index.html. Please specify the api key obtained from GCP in query.

<script src="https://maps.googleapis.com/maps/api/js?key=[api key]"></script>the package import

Import the necessary packages to display the map.

import 'package:google_maps/google_maps.dart' as jsMap;
import 'dart:ui' as ui;
import 'dart:html';

the map itself

Use HtmlElementView to embed the html element into the dart and display the map.

The third line, platformViewRegistry, gives an undefined error in the static analysis, but you can ignore it. It’s pretty weird, but it works fine.

The html element created by HtmlElementView is unique with htmlId, so when redisplaying it, please set the htmlId to be different. If you don’t, the previous display will remain.

Widget _map() {
final String htmlId = "map";
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
final mapOptions = gMap.MapOptions()
..zoom = 15.0
..center = gMap.LatLng(35.7560423, 139.7803552);

final elem = DivElement()..id = htmlId;
final map = gMap.GMap(elem, mapOptions);
return elem;
});
return HtmlElementView(viewType: htmlId);
}

Display an icon on the map

Create an Icon object and add the map object to the Maker object. However, this method does not allow you to specify the rotation of an Icon. (This is possible in flutter mobile and swift)
Please refer to “Turn Icon section” for details on how to specify rotation.

final _icon = gMap.Icon()
..scaledSize = gMap.Size(40, 40)
..url = "https://lh3.googleusercontent.com/ogw/ADGmqu_RzXtbUv4nHU9XjdbNtDNQ5XAIlOh_1jJNci48=s64-c-mo";

gMap.Marker(gMap.MarkerOptions()
..anchorPoint = gMap.Point(0.5, 0.5)
..icon = _icon
..position = gMap.LatLng(35.7560423, 139.7803552)
..map = map
..title = htmlId);

If you want to use the image specified for assets in pubspec.yaml, add “assets/” to the beginning of the path in order to use the icon image.

final _icon = gMap.Icon()
..scaledSize = gMap.Size(40, 40)
..url = "assets/[the path of assets specified in pubspec.yaml]";

Turn Icon

If you’ve ever used the Google Maps JavaScript API, you know that
you need to create a marker icon in the path of svg to change the rotation of the icon.
In the following source, the square is rotated by 45 degrees.

final svgMarker = gMap.GSymbol()
..rotation = 45
..path = 'M 10 10 H 90 V 90 H 10 L 10 10'
..fillColor = 'blue'
..fillOpacity = 0.8
..scale = 1
..strokeColor = 'blue'
..strokeWeight = 14;

gMap.Marker(gMap.MarkerOptions()
..position = map.center
..icon = svgMarker
..map = map);

Hide the controller on the map

The controller on the map, which is displayed by default, can be hidden.

final mapOptions = gMap.MapOptions()
..mapTypeControl = false
..fullscreenControl = false
..streetViewControl = false
..zoomControl = false
..zoom = 15.0
..center = gMap.LatLng(35.7560423, 139.7803552);

Draw a polyLine

I did not use google map directions api, but drew a polyLine to connect two points.
You need to set the path property of the polyLine object to the longitude and the latitude you want to connect.
You just need to set the map object in the polyLine object’s map property.

final polyline = gMap.Polyline(gMap.PolylineOptions()
..path = [
gMap.LatLng(35.7560423, 139.7803552),
gMap.LatLng(35.7713573, 139.7754953)
]
..strokeColor = "#75A9FF"
..strokeOpacity = 1.0
..strokeWeight = 3);
polyline.map = map;

Draw a polygon

Set the vertices of the rectangle you want to display in the PolygonOptions object as a List of LatLng in the paths parameter. Then, specify the map object to display it.

final areaPolygon = gMap.PolygonOptions()
..strokeColor = "#000000"
..fillColor = '#000000'
..fillOpacity = 0.3
..paths = [
gMap.LatLng(35.7560423, 139.7803552),
gMap.LatLng(35.7560424, 139.7803554),
gMap.LatLng(35.7560425, 139.7803555),
gMap.LatLng(35.7560426, 139.7803556),
]
..map = store.map;
gMap.Polygon(areaPolygon);

Handling Camera events

You can get events for the map in Stream.
I used onCenterChanged, onDragstart, and onDragend because I needed events when the center of the map moved, events when the map started dragging, and events when it finished dragging. With the javascript api You can probably use the events . you can trawl through GMap’s property.

map.onCenterChanged.listen((event) {
print(map.center.lat);
print(map.center.lng);
print(map.zoom);
});

map.onDragstart.listen((event) {});

map.onDragend.listen((event) {});

Show the Circle

You need to specify the latitude and the longitude of the center, radius (m) of the circleOptions object and a map object to display it. You can use multiple objects.

final areaCircle = gMap.CircleOptions()
..map = map
..center = gMap.LatLng(35.7560423, 139.7803552)
..radius = 400;
gMap.Circle(areaCircle);

Get your location

The location published in pub.dev was not available on the web.
I googled it and found that you can easily get the location information here.

Use Geolocator to calculate the distance between two points from longitude and latitude

I can’t use the geoolocator published in pub.dev on the web.

import 'dart:math';

class ManualGeolocator {
//Find the distance between two points (m)
//The version you seek from the Hubeni formula
//mode Land Survey System true:world(default) false:japan
//@return double distance(m)
static double distanceBetween(lat1, lon1, lat2, lon2, {mode = true}) {
// Convert longitude and latitude to radians
final radLat1 = lat1 * pi / 180;
final radLon1 = lon1 * pi / 180;
final radLat2 = lat2 * pi / 180;
final radLon2 = lon2 * pi / 180;

// Latitude difference
final radLatDiff = radLat1 - radLat2;

// longitude and latitude difference
final radLonDiff = radLon1 - radLon2;

// Average Latitude
final radLatAve = (radLat1 + radLat2) / 2.0;

// Difference in values by geodetic system
final a = mode ? 6378137.0 : 6377397.155; // Equatorial radius
final b = mode ? 6356752.314140356 : 6356078.963; // Extreme Radius
final e2 = mode ? 0.00669438002301188 : 0.00667436061028297; // primary eccentricity^2
final a1e2 = mode ? 6335439.32708317 : 6334832.10663254; // Meridian radius of curvature at the equator

final sinLat = sin(radLatAve);
final w2 = 1.0 - e2 * (sinLat * sinLat);
final m = a1e2 / (sqrt(w2) * w2);
final n = a / sqrt(w2);
final t1 = m * radLatDiff;
final t2 = n * cos(radLatAve) * radLonDiff;
final dist = sqrt((t1 * t1) + (t2 * t2));

return dist;
}
}

Using Geocoder to get addresses from latitude and longitude

The geocoder available on pub.dev doesn’t work on the web.
So I used Geocoding API.

The return value of Geocoding API is a large json. Here’s a web service that makes it easy to create a model class of dart from json.

Complete example

Widget _map() {
final String htmlId = "map";
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
final mapOptions = gMap.MapOptions()
..zoom = 15.0
..center = gMap.LatLng(35.7560423, 139.7803552);

final elem = DivElement()..id = htmlId;
final map = gMap.GMap(elem, mapOptions);

final _icon = gMap.Icon()
..scaledSize = gMap.Size(40, 40)
..url =
"https://lh3.googleusercontent.com/ogw/ADGmqu_RzXtbUv4nHU9XjdbNtDNQ5XAIlOh_1jJNci48=s64-c-mo";

gMap.Marker(gMap.MarkerOptions()
..anchorPoint = gMap.Point(0.5, 0.5)
..icon = _icon
..position = gMap.LatLng(35.7560423, 139.7803552)
..map = map
..title = htmlId);

gMap.Marker(gMap.MarkerOptions()
..anchorPoint = gMap.Point(0.5, 0.5)
..icon = _icon
..position = gMap.LatLng(35.7713573, 139.7754953)
..map = map
..title = htmlId);

final polyline = gMap.Polyline(gMap.PolylineOptions()
..path = [
gMap.LatLng(35.7560423, 139.7803552),
gMap.LatLng(35.7713573, 139.7754953)
]
..strokeColor = "#75A9FF"
..strokeOpacity = 1.0
..strokeWeight = 3);
polyline.map = map;

final areaCircle = gMap.CircleOptions()
..map = map
..center = gMap.LatLng(35.7560423, 139.7803552)
..radius = 400;
gMap.Circle(areaCircle);

map.onCenterChanged.listen((event) {
print(map.center.lat);
print(map.center.lng);
print(map.zoom);
});

map.onDragstart.listen((event) {});

map.onDragend.listen((event) {});

final svgMarker = gMap.GSymbol()
..rotation = 45
..path = 'M 10 10 H 90 V 90 H 10 L 10 10'
..fillColor = 'blue'
..fillOpacity = 0.8
..scale = 1
..strokeColor = 'blue'
..strokeWeight = 14;

gMap.Marker(gMap.MarkerOptions()
..position = map.center
..icon = svgMarker
..map = map);

return elem;
});
return HtmlElementView(viewType: htmlId);
}

What we couldn’t seem to do

Applying the custom style

I tried GMap’s StyledMapTypeOptions and StyledMapType, but I couldn’t change the style.

Other things to watch out for on the flutter web

importing package for the flutter web and getting stuck in the build for iOS and android

The reason is that dart:html and dart:js, which were imported first, can’t be used on mobile.
It’s better to manage mobile and web sources separately.

ps

Using the methods described in Support Flutter Cross-Platform, you can control what packages you import on the web and mobile.

ps

It seems that the official google map package for the web has been released.

--

--

keisuke ishikura
keisuke ishikura

Written by keisuke ishikura

Mypace Co.,Ltd. CEO/ Mobile Engineer

Responses (2)