How to Use Google Maps in Flutter: A Step-by-Step Guide
Integrating Google Maps into a Flutter application allows users to view and interact with a map interface. In this article, we’ll cover every step, from setting up your project and obtaining an API key to displaying the map, adding markers, and customizing the map appearance.
Prerequisites
- Flutter SDK
- A Google Cloud Platform account (for obtaining an API key)
- Basic knowledge of Dart and Flutter development
Step 1: Create a New Flutter Project
If you haven’t already created a Flutter project, open your terminal and run:
flutter create google_maps_example
cd google_maps_example
Step 2: Add Google Maps Flutter Plugin
To integrate Google Maps, we’ll use the google_maps_flutter
package. Open pubspec.yaml
and add the following dependency:
dependencies:
flutter:
sdk: flutter
google_maps_flutter: ^2.2.0 # Make sure to check for the latest version on pub.dev
Run the following command to install the dependency:
flutter pub get
Step 3: Set Up Google Maps API Key
- Go to the Google Cloud Console.
- Create a new project or select an existing one.
- Navigate to APIs & Services > Credentials.
- Click on Create Credentials > API Key. Copy the generated API key.
- In APIs & Services > Library, enable the following APIs:
- Maps SDK for Android
- Maps SDK for iOS
Configuring the API Key for Android and iOS
For Android:
- Open the
android/app/src/main/AndroidManifest.xml
file and add the following inside the<application>
tag:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_API_KEY" />
Replace YOUR_API_KEY
with your actual API key.
For iOS:
- Open
ios/Runner/AppDelegate.swift
(for Swift) orAppDelegate.m
(for Objective-C) and add the following inside theapplication
method:
import GoogleMaps // Add this line at the top
GMSServices.provideAPIKey("YOUR_API_KEY")
Step 4: Add Google Maps to Flutter Application
Now, let’s add the Google Maps widget to our app. Create a new Dart file called google_map_screen.dart
and add the following code.
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class GoogleMapScreen extends StatefulWidget {
@override
_GoogleMapScreenState createState() => _GoogleMapScreenState();
}
class _GoogleMapScreenState extends State<GoogleMapScreen> {
late GoogleMapController mapController;
// Set the initial camera position
final CameraPosition initialPosition = CameraPosition(
target: LatLng(37.7749, -122.4194), // Coordinates for San Francisco
zoom: 10.0,
);
// Marker setup
final Set<Marker> markers = {};
// Called when the map is created
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
// Example marker
markers.add(
Marker(
markerId: MarkerId('sfMarker'),
position: LatLng(37.7749, -122.4194),
infoWindow: InfoWindow(
title: 'San Francisco',
snippet: 'A beautiful city',
),
),
);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Google Maps in Flutter'),
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: initialPosition,
markers: markers,
mapType: MapType.normal,
myLocationEnabled: true, // Enables user location
zoomControlsEnabled: true,
),
);
}
}
Step 5: Display the Map Screen
In main.dart
, update the home
property to display GoogleMapScreen
:
import 'package:flutter/material.dart';
import 'google_map_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Google Maps Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: GoogleMapScreen(),
);
}
}
Step 6: Customizing Markers
Adding a custom marker icon can improve the user experience. Save your marker icon in the assets
folder, and update pubspec.yaml
to include it:
flutter:
assets:
- assets/custom_marker.png
Load and use the custom marker in _onMapCreated
:
BitmapDescriptor customIcon;
@override
void initState() {
super.initState();
_loadCustomMarker();
}
void _loadCustomMarker() async {
customIcon = await BitmapDescriptor.fromAssetImage(
ImageConfiguration(size: Size(48, 48)),
'assets/custom_marker.png',
);
}
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
markers.add(
Marker(
markerId: MarkerId('customMarker'),
position: LatLng(37.7749, -122.4194),
icon: customIcon, // Use custom icon
infoWindow: InfoWindow(
title: 'Custom Location',
snippet: 'Using a custom marker icon',
),
),
);
setState(() {});
}
Step 7: Adding User Location
To show the user’s current location, ensure that both myLocationEnabled
and myLocationButtonEnabled
are set to true
:
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: initialPosition,
markers: markers,
myLocationEnabled: true,
myLocationButtonEnabled: true,
)
Permissions (Android)
For Android, add the following permissions in android/app/src/main/AndroidManifest.xml
:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
For Flutter, consider using the permission_handler
package to request location permissions.
Step 8: Updating Camera Position
To navigate the map camera to a different location, use the animateCamera
method:
void _moveToPosition(LatLng position) {
mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(target: position, zoom: 14),
),
);
}
Step 9: Implementing Routing with Google Maps
To add routing, we’ll integrate the Google Directions API. This API calculates routes and provides detailed turn-by-turn directions.
Step 9.1: Enable the Directions API
- Go to Google Cloud Console.
- Under APIs & Services > Library, enable the Directions API for your project.
Step 9.2: Request Directions
To get directions between two points, you’ll make an HTTP request to the Directions API. We’ll use the http
package to make this request.
Add http
to pubspec.yaml
:
dependencies:
http: ^0.13.3
Run:
flutter pub get
Step 9.3: Write the Code for Route Calculation
In google_map_screen.dart
, import http
and define a function that fetches route information:
import 'package:http/http.dart' as http;
import 'dart:convert';
// Function to get directions from Google Directions API
Future<void> _getRoute(LatLng origin, LatLng destination) async {
final String apiKey = 'YOUR_API_KEY';
final String url = 'https://maps.googleapis.com/maps/api/directions/json?origin=${origin.latitude},${origin.longitude}&destination=${destination.latitude},${destination.longitude}&key=$apiKey';
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final Map<String, dynamic> data = json.decode(response.body);
if (data['routes'].isNotEmpty) {
// Extract polyline
final String encodedPolyline = data['routes'][0]['overview_polyline']['points'];
_decodePolyline(encodedPolyline);
}
} else {
print('Failed to fetch route: ${response.reasonPhrase}');
}
}
The overview_polyline
gives the route’s encoded polyline, which represents the path between the origin and destination.
Step 9.4: Decode and Display the Polyline on the Map
The polyline must be decoded to use it on the map. Add this function to decode it:
List<LatLng> _decodePolyline(String polyline) {
List<LatLng> points = [];
int index = 0, len = polyline.length;
int lat = 0, lng = 0;
while (index < len) {
int b, shift = 0, result = 0;
do {
b = polyline.codeUnitAt(index++) - 63;
result |= (b & 0x1F) << shift;
shift += 5;
} while (b >= 0x20);
int dlat = (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
lat += dlat;
shift = 0;
result = 0;
do {
b = polyline.codeUnitAt(index++) - 63;
result |= (b & 0x1F) << shift;
shift += 5;
} while (b >= 0x20);
int dlng = (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
lng += dlng;
points.add(LatLng(lat / 1E5, lng / 1E5));
}
return points;
}
void _showRoute(List<LatLng> points) {
final Polyline routePolyline = Polyline(
polylineId: PolylineId('route'),
color: Colors.blue,
width: 5,
points: points,
);
setState(() {
_polylines.add(routePolyline);
});
}
After decoding, call _showRoute
to display the route as a polyline on the map. Call _getRoute
when you want to calculate and display the route.
Add the _polylines
set in your class:
final Set<Polyline> _polylines = {};
And update GoogleMap
widget to use _polylines
:
GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: initialPosition,
markers: markers,
polylines: _polylines,
myLocationEnabled: true,
zoomControlsEnabled: true,
)
Step 10: Adding Real-Time Tracking
Tracking shows the user’s real-time location on the map and can automatically update their position.
Step 10.1: Use the geolocator
Package
Add the geolocator
package for accessing location services:
dependencies:
geolocator: ^9.0.2
Run:
flutter pub get
Step 10.2: Configure Permissions
Configure Android and iOS permissions in android/app/src/main/AndroidManifest.xml
and Info.plist
for iOS. Refer to the geolocator documentation for details.
Step 10.3: Track User Location
Add the following code to listen to location changes and update the map’s camera:
import 'package:geolocator/geolocator.dart';
void _startTracking() async {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
await Geolocator.openLocationSettings();
return;
}
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return;
}
}
Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 10,
),
).listen((Position position) {
LatLng newPosition = LatLng(position.latitude, position.longitude);
mapController.animateCamera(
CameraUpdate.newLatLng(newPosition),
);
setState(() {
markers.add(
Marker(
markerId: MarkerId('userLocation'),
position: newPosition,
infoWindow: InfoWindow(title: 'You are here'),
),
);
});
});
}
Call _startTracking()
in initState
to start tracking as soon as the screen loads.
Full Code for google_map_screen.dart
Here’s the complete code for google_map_screen.dart
:
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class GoogleMapScreen extends StatefulWidget {
@override
_GoogleMapScreenState createState() => _GoogleMapScreenState();
}class _GoogleMapScreenState extends State<GoogleMapScreen> {
late GoogleMapController mapController;
final Set<Marker> markers = {};
final Set<Polyline> _polylines = {};
final CameraPosition initialPosition = CameraPosition(
target: LatLng(37.7749, -122.4194),
zoom: 10.0,
); @override
void initState() {
super.initState();
_startTracking();
} void _onMapCreated(GoogleMapController controller) {
mapController = controller;
} Future<void> _getRoute(LatLng origin, LatLng destination) async {
final String apiKey = 'YOUR_API_KEY';
final String url = 'https://maps.googleapis.com/maps/api/directions/json?origin=${origin.latitude},${origin.longitude}&destination=${destination.latitude},${destination.longitude}&key=$apiKey'; final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final data = json.decode(response.body);
if (data['routes'].isNotEmpty) {
final encodedPolyline = data['routes'][0]['overview_polyline']['points'];
final points = _decodePolyline(encodedPolyline);
_showRoute(points);
}
}
} List<LatLng> _decodePolyline(String polyline) { /*...*/ } void _showRoute(List<LatLng> points) {
final Polyline routePolyline = Polyline(
polylineId: PolylineId('route'),
color: Colors.blue,
width: 5,
points: points,
);
setState(() {
_polylines.add(routePolyline);
});
} void _startTracking() { /*...*/ } @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Routing & Tracking')),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: initialPosition,
markers: markers,
polylines: _polylines,
myLocationEnabled: true,
zoomControlsEnabled: true,
),
);
}
}