A flutter plugin for displaying google maps on iOS and Android
Please note: API changes are likely as we continue to develop this plugin.
Maps SDK for Android
Maps SDK for iOS
Credentials
, choose Create Credential
. The way you register your API key on iOS vs Android is different. Make sure to read the next sections carefully.
ios/Runner/Info.plist
. Example: <key>NSLocationWhenInUseUsageDescription</key>
<string>Using location to display on a map</string>
import 'package:map_view/map_view.dart';
void main() {
MapView.setApiKey("<your_api_key>");
runApp(new MyApp());
}
Note: If your iOS and Android API key are different, be sure to use your iOS API key here.
//Create an instance variable for the mapView
var _mapView = new MapView();
//Add a method to call to show the map.
void showMap() {
_mapView.show(new MapOptions(showUserLocation: true));
}
```
ClientParametersRequest failed, 7 attempts remaining (0 vs 12). Error Domain=com.google.HTTPStatus Code=400 "(null)" UserInfo={data=<>}
Your Bundle ID does not match what is registered in the Google API Console. When you create an restricted API key in the Google API console it asks you to specify your iOS bundle ID. Make sure that your iOS Bundle Identifier matches the one you registered in the console.
Using the wrong key. If you made a separate key for iOS and Android, make sure you are using the iOS key in the MapView.setApiKey() call.
You will be making multiple edits to your AndroidManifest.xml
file. In your Flutter project, you can
find this file location under android/app/src/main
In your AndroidManifest.xml
, add the following uses-permission above the <application> tag.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
In your AndroidManifest.xml, add the following lines inside of the application
tag. Be sure to replace your_api_key
with the one you generated.
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="your_api_key"/>
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
Add the MapActivity to your AndroidManifest.xml
<activity android:name="com.apptreesoftware.mapview.MapActivity" android:theme="@style/Theme.AppCompat.Light.DarkActionBar"/>
In your android/build.gradle
file. Under buildScript
dependencies
add:
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-4'
Run your application on an Android device or simulator. Confirm that when you display the map you see map detail. If you only see a beige screen it's possible that your API key is incorrect.
This plugin does not currently support displaying a Google Map within the Flutter widget hierarchy. A common workaround for this is to show a static image using the Google Static Maps API. Included in this Plugin is the StaticMapProvider class which will allow you to easily generate a static map. The Static Maps API also requires an API Key and you must enable the API within the Google API Console.
Maps Static API
var provider = new StaticMapProvider('your_api_key');
var uri = staticMapProvider.getImageUriFromMap(mapView,
width: 900, height: 400);
You can refer to the example project if you run into any issues with these steps.
![]() |
![]() |
mapView.show(
new MapOptions(
mapViewType: MapViewType.normal,
showUserLocation: true,
initialCameraPosition: new CameraPosition(
new Location(45.5235258, -122.6732493), 14.0),
title: "Recently Visited"),
toolbarActions: [new ToolbarAction("Close", 1)]);
mapView.onMapReady.listen((_) {
print("Map ready");
});
mapView.setMarkers(<Marker>[
new Marker("1", "Work", 45.523970, -122.663081, color: Colors.blue),
new Marker("2", "Nossa Familia Coffee", 45.528788, -122.684633),
]);
mapView.addMarker(new Marker("3", "10 Barrel", 45.5259467, -122.687747,
color: Colors.purple));
First add your assets to a folder in your project directory. The name of the folder could be any but "images" or "assets" are the more common. It should look like this.
- project_name
|-android
|-images
|-flower_vase.png
|-ios
|-lib
# Rest of project folders and files
Then add asset to the pubspec.yaml under flutter tag.
flutter:
# Code already existent
# Added asset.
assets:
- images/flower_vase.png
Finally use the asset name as icon for your marker. If the width or height is not set or is equals to 0, the image original value of said attribute will be used.
new Marker(
"1",
"Something fragile!",
45.52480841512737,
-122.66201455146073,
color: Colors.blue,
draggable: true, //Allows the user to move the marker.
markerIcon: new MarkerIcon(
"images/flower_vase.png", //Asset to be used as icon
width: 112.0, //New width for the asset
height: 75.0, // New height for the asset
),
);
First set the draggable attribute of a marker to true.
Marker marker=new Marker(
"1",
"Something fragile!",
45.52480841512737,
-122.66201455146073,
draggable: true, //Allows the user to move the marker.
);
Now add listeners for the events.
// This listener fires when the marker is long pressed and could be moved.
mapView.onAnnotationDragStart.listen((markerMap) {
var marker = markerMap.keys.first;
var location = markerMap[marker]; // The original location of the marker before moving it. Use it if needed.
print("Annotation ${marker.id} dragging started");
});
// This listener fires when the user releases the marker.
mapView.onAnnotationDragEnd.listen((markerMap) {
var marker = markerMap.keys.first;
var location = markerMap[marker]; // The actual position of the marker after finishing the dragging.
print("Annotation ${marker.id} dragging ended");
});
// This listener fires every time the marker changes position.
mapView.onAnnotationDrag.listen((markerMap) {
var marker = markerMap.keys.first;
var location = markerMap[marker]; // The updated position of the marker.
print("Annotation ${marker.id} moved to ${location.latitude} , ${location
.longitude}");
});
mapView.addPolyline(new Polyline(
"12",
<Location>[
new Location(45.519698, -122.674932),
new Location(45.516687, -122.667014),
],
width: 15.0));
mapView.setPolylines(<Polyline>[
new Polyline(
"11",
<Location>[
new Location(45.523970, -122.663081),
new Location(45.528788, -122.684633),
new Location(45.528864, -122.667195),
],
jointType: FigureJointType.round,
width: 15.0,
color: Colors.orangeAccent,
),
new Polyline(
"12",
<Location>[
new Location(45.519698, -122.674932),
new Location(45.516687, -122.667014),
],
width: 15.0,
),
]);
mapView.addPolygon(new Polygon(
"111",
<Location>[
new Location(45.5231233, -122.6733130),
new Location(45.5233225, -122.6732969),
new Location(45.5232398, -122.6733506),
new Location(45.5231233, -122.6733130),
],
jointType: FigureJointType.round,
strokeWidth: 5.0,
strokeColor: Colors.red,
fillColor: Color.fromARGB(75, 255, 0, 0),
));
mapView.setPolygons(<Polygon>[
new Polygon(
"111",
<Location>[
new Location(42.9274334, -72.2811234),
new Location(42.9258230, -72.2808444),
new Location(42.9261294, -72.2779906),
new Location(42.9275120, -72.2779155),
],
//you can add a hole inside the polygon
holes: <Hole>[
new Hole(
<Location>[
new Location(42.9270721, -72.2797287),
new Location(42.9266400, -72.2796750),
new Location(42.9267186, -72.2790956),
new Location(42.9270014, -72.2790956),
],
),
],
jointType: FigureJointType.round,
strokeWidth: 5.0,
strokeColor: Colors.red,
fillColor: Color.fromARGB(75, 255, 0, 0)),
new Polygon(
"111",
<Location>[
new Location(45.5231233, -122.6733130),
new Location(45.5233225, -122.6732969),
new Location(45.5232398, -122.6733506),
new Location(45.5231233, -122.6733130),
],
jointType: FigureJointType.round,
strokeWidth: 5.0,
strokeColor: Colors.red,
fillColor: Color.fromARGB(75, 255, 0, 0)),
]);
//Remove all markers
mapView.clearA
mapView.zoomToFit(padding: 100);
mapView.onLocationUpdated
.listen((location) => print("Location updated $location"));
//Marker
mapView.onTouchAnnotation.listen((annotation) => print("annotation ${annotation.id} tapped"));
//Polyline
mapView.onTouchPolyline.listen((polyline) => print("polyline ${polyline.id} tapped"));
//Polygon
mapView.onTouchPolygon.listen((polygon) => print("polygon ${polygon.id} tapped"));
mapView.onMapTapped
.listen((location) => print("Touched location $location"));
mapView.onCameraChanged.listen((cameraPosition) =>
this.setState(() => this.cameraPosition = cameraPosition));
mapView.onToolbarAction.listen((id) {
if (id == 1) {
_handleDismiss();
}
});
double zoomLevel = await mapView.zoomLevel;
Location centerLocation = await mapView.centerLocation;
List<Marker> visibleAnnotations = await mapView.visibleAnnotations;
package:map_view/map_view.dart
example/lib/main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:map_view/figure_joint_type.dart';
import 'package:map_view/map_view.dart';
import 'package:map_view/polygon.dart';
import 'package:map_view/polyline.dart';
///This API Key will be used for both the interactive maps as well as the static maps.
///Make sure that you have enabled the following APIs in the Google API Console (https://console.developers.google.com/apis)
/// - Static Maps API
/// - Android Maps API
/// - iOS Maps API
const API_KEY = "<your-api-key>";
void main() {
MapView.setApiKey(API_KEY);
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
MapView mapView = new MapView();
CameraPosition cameraPosition;
var compositeSubscription = new CompositeSubscription();
var staticMapProvider = new StaticMapProvider(API_KEY);
Uri staticMapUri;
//Marker bubble
List<Marker> _markers = <Marker>[
new Marker(
"1",
"Something fragile!",
45.52480841512737,
-122.66201455146073,
color: Colors.blue,
draggable: true, //Allows the user to move the marker.
markerIcon: new MarkerIcon(
"images/flower_vase.png",
width: 112.0,
height: 75.0,
),
),
];
//Line
List<Polyline> _lines = <Polyline>[
new Polyline(
"11",
<Location>[
new Location(45.52309483308097, -122.67339684069155),
new Location(45.52298442915803, -122.66339991241693),
],
width: 15.0,
color: Colors.blue),
];
//Drawing
List<Polygon> _polygons = <Polygon>[
new Polygon(
"111",
<Location>[
new Location(45.5231233, -122.6733130),
new Location(45.5231195, -122.6706147),
new Location(45.5231120, -122.6677823),
new Location(45.5230894, -122.6670957),
new Location(45.5230518, -122.6660979),
new Location(45.5230518, -122.6655185),
new Location(45.5232849, -122.6652074),
new Location(45.5230443, -122.6649070),
new Location(45.5230443, -122.6644135),
new Location(45.5230518, -122.6639414),
new Location(45.5231195, -122.6638663),
new Location(45.5231947, -122.6638770),
new Location(45.5233074, -122.6639950),
new Location(45.5232698, -122.6643813),
new Location(45.5235480, -122.6644349),
new Location(45.5244349, -122.6645529),
new Location(45.5245928, -122.6639628),
new Location(45.5248108, -122.6632762),
new Location(45.5249385, -122.6626861),
new Location(45.5249310, -122.6622677),
new Location(45.5250212, -122.6621926),
new Location(45.5251490, -122.6621711),
new Location(45.5251791, -122.6623106),
new Location(45.5252242, -122.6625681),
new Location(45.5251791, -122.6632118),
new Location(45.5249010, -122.6640165),
new Location(45.5247431, -122.6646388),
new Location(45.5249611, -122.6646602),
new Location(45.5253820, -122.6642525),
new Location(45.5260811, -122.6642525),
new Location(45.5260435, -122.6637161),
new Location(45.5261713, -122.6635551),
new Location(45.5263066, -122.6634800),
new Location(45.5265471, -122.6635873),
new Location(45.5269003, -122.6639628),
new Location(45.5270356, -122.6642632),
new Location(45.5271484, -122.6646602),
new Location(45.5274866, -122.6649177),
new Location(45.5271258, -122.6651645),
new Location(45.5269605, -122.6653790),
new Location(45.5267049, -122.6654434),
new Location(45.5262990, -122.6657224),
new Location(45.5261337, -122.6666021),
new Location(45.5256677, -122.6678467),
new Location(45.5245777, -122.6687801),
new Location(45.5236908, -122.6690161),
new Location(45.5233751, -122.6692307),
new Location(45.5233826, -122.6714945),
new Location(45.5233337, -122.6729804),
new Location(45.5233225, -122.6732969),
new Location(45.5232398, -122.6733506),
new Location(45.5231233, -122.6733130),
],
jointType: FigureJointType.bevel,
strokeWidth: 5.0,
strokeColor: Colors.red,
fillColor: Color.fromARGB(75, 255, 0, 0)),
];
@override
initState() {
super.initState();
cameraPosition = new CameraPosition(Locations.portland, 2.0);
staticMapUri = staticMapProvider.getStaticUri(Locations.portland, 12,
width: 900, height: 400, mapType: StaticMapViewType.roadmap);
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('Map View Example'),
),
body: new Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Container(
height: 250.0,
child: new Stack(
children: <Widget>[
new Center(
child: new Container(
child: new Text(
"You are supposed to see a map here.\n\nAPI Key is not valid.\n\n"
"To view maps in the example application set the "
"API_KEY variable in example/lib/main.dart. "
"\n\nIf you have set an API Key but you still see this text "
"make sure you have enabled all of the correct APIs "
"in the Google API Console. See README for more detail.",
textAlign: TextAlign.center,
),
padding: const EdgeInsets.all(20.0),
)),
new InkWell(
child: new Center(
child: new Image.network(staticMapUri.toString()),
),
onTap: showMap,
)
],
),
),
new Container(
padding: new EdgeInsets.only(top: 10.0),
child: new Text(
"Tap the map to interact",
style: new TextStyle(fontWeight: FontWeight.bold),
),
),
new Container(
padding: new EdgeInsets.only(top: 25.0),
child:
new Text("Camera Position: \n\nLat: ${cameraPosition.center
.latitude}\n\nLng:${cameraPosition.center
.longitude}\n\nZoom: ${cameraPosition.zoom}"),
),
],
)),
);
}
showMap() {
mapView.show(
new MapOptions(
mapViewType: MapViewType.normal,
showUserLocation: true,
showMyLocationButton: true,
showCompassButton: true,
initialCameraPosition: new CameraPosition(
new Location(45.526607443935724, -122.66731660813093), 15.0),
hideToolbar: false,
title: "Recently Visited"),
toolbarActions: [new ToolbarAction("Close", 1)]);
StreamSubscription sub = mapView.onMapReady.listen((_) {
mapView.setMarkers(_markers);
mapView.setPolylines(_lines);
mapView.setPolygons(_polygons);
});
compositeSubscription.add(sub);
sub = mapView.onLocationUpdated.listen((location) {
print("Location updated $location");
});
compositeSubscription.add(sub);
sub = mapView.onTouchAnnotation
.listen((annotation) => print("annotation ${annotation.id} tapped"));
compositeSubscription.add(sub);
sub = mapView.onTouchPolyline
.listen((polyline) => print("polyline ${polyline.id} tapped"));
compositeSubscription.add(sub);
sub = mapView.onTouchPolygon
.listen((polygon) => print("polygon ${polygon.id} tapped"));
compositeSubscription.add(sub);
sub = mapView.onMapTapped
.listen((location) => print("Touched location $location"));
compositeSubscription.add(sub);
sub = mapView.onCameraChanged.listen((cameraPosition) =>
this.setState(() => this.cameraPosition = cameraPosition));
compositeSubscription.add(sub);
sub = mapView.onAnnotationDragStart.listen((markerMap) {
var marker = markerMap.keys.first;
print("Annotation ${marker.id} dragging started");
});
sub = mapView.onAnnotationDragEnd.listen((markerMap) {
var marker = markerMap.keys.first;
print("Annotation ${marker.id} dragging ended");
});
sub = mapView.onAnnotationDrag.listen((markerMap) {
var marker = markerMap.keys.first;
var location = markerMap[marker];
print("Annotation ${marker.id} moved to ${location.latitude} , ${location
.longitude}");
});
compositeSubscription.add(sub);
sub = mapView.onToolbarAction.listen((id) {
print("Toolbar button id = $id");
if (id == 1) {
_handleDismiss();
}
});
compositeSubscription.add(sub);
sub = mapView.onInfoWindowTapped.listen((marker) {
print("Info Window Tapped for ${marker.title}");
});
compositeSubscription.add(sub);
}
_handleDismiss() async {
double zoomLevel = await mapView.zoomLevel;
Location centerLocation = await mapView.centerLocation;
List<Marker> visibleAnnotations = await mapView.visibleAnnotations;
List<Polyline> visibleLines = await mapView.visiblePolyLines;
List<Polygon> visiblePolygons = await mapView.visiblePolygons;
print("Zoom Level: $zoomLevel");
print("Center: $centerLocation");
print("Visible Annotation Count: ${visibleAnnotations.length}");
print("Visible Polylines Count: ${visibleLines.length}");
print("Visible Polygons Count: ${visiblePolygons.length}");
var uri = await staticMapProvider.getImageUriFromMap(mapView,
width: 900, height: 400);
setState(() => staticMapUri = uri);
mapView.dismiss();
compositeSubscription.cancel();
}
}
class CompositeSubscription {
Set<StreamSubscription> _subscriptions = new Set();
void cancel() {
for (var n in this._subscriptions) {
n.cancel();
}
this._subscriptions = new Set();
}
void add(StreamSubscription subscription) {
this._subscriptions.add(subscription);
}
void addAll(Iterable<StreamSubscription> subs) {
_subscriptions.addAll(subs);
}
bool remove(StreamSubscription subscription) {
return this._subscriptions.remove(subscription);
}
bool contains(StreamSubscription subscription) {
return this._subscriptions.contains(subscription);
}
List<StreamSubscription> toList() {
return this._subscriptions.toList();
}
}
Add this to your package's pubspec.yaml file:
dependencies:
map_view: ^0.0.14
You can install packages from the command line:
with Flutter:
$ flutter packages get
Alternatively, your editor might support flutter packages get
.
Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:map_view/map_view.dart';
Version | Uploaded | Documentation | Archive |
---|---|---|---|
0.0.14 | Jul 20, 2018 |
|
|
0.0.13 | Jul 18, 2018 |
|
|
0.0.12 | Apr 17, 2018 |
|
|
0.0.11 | Apr 16, 2018 |
|
|
0.0.10 | Dec 30, 2017 |
|
|
0.0.9 | Dec 30, 2017 |
|
|
0.0.8 | Dec 29, 2017 |
|
|
0.0.7 | Dec 29, 2017 |
|
|
0.0.6 | Dec 27, 2017 |
|
|
0.0.5 | Nov 14, 2017 |
|
|
Popularity:
Describes how popular the package is relative to other packages.
[more]
|
97
|
Health:
Code health derived from static analysis.
[more]
|
100
|
Maintenance:
Reflects how tidy and up-to-date the package is.
[more]
|
81
|
Overall:
Weighted score of the above.
[more]
|
95
|
We analyzed this package on Feb 14, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:
Detected platforms: Flutter
References Flutter, and has no conflicting libraries.
Format lib/map_options.dart
.
Run flutter format
to format lib/map_options.dart
.
Format lib/map_view_type.dart
.
Run flutter format
to format lib/map_view_type.dart
.
Format lib/polygon.dart
.
Run flutter format
to format lib/polygon.dart
.
Format lib/polyline.dart
.
Run flutter format
to format lib/polyline.dart
.
Package is pre-v0.1 release. (-10 points)
While nothing is inherently wrong with versions of 0.0.*
, it might mean that the author is still experimenting with the general direction of the API.
The package description is too short. (-9 points)
Add more detail to the description
field of pubspec.yaml
. Use 60 to 180 characters to describe the package, what it does, and its target use case.
Package | Constraint | Resolved | Available |
---|---|---|---|
Direct dependencies | |||
Dart SDK | >=2.0.0-dev.28.0 <3.0.0 | ||
flutter | 0.0.0 | ||
uri | ^0.11.1 | 0.11.3+1 | |
Transitive dependencies | |||
collection | 1.14.11 | ||
matcher | 0.12.4 | ||
meta | 1.1.6 | 1.1.7 | |
path | 1.6.2 | ||
quiver | 2.0.1 | ||
sky_engine | 0.0.99 | ||
stack_trace | 1.9.3 | ||
typed_data | 1.1.6 | ||
utf | 0.9.0+5 | ||
vector_math | 2.0.8 |