Displaying Maps with google_maps_flutter in Flutter

Integrating maps into mobile applications has become a standard feature, offering users location-based services, directions, and geographic data visualization. The google_maps_flutter package is a powerful Flutter plugin that allows developers to embed Google Maps directly into their Flutter applications on both Android and iOS platforms. This article will guide you through the process of displaying maps using google_maps_flutter, covering setup, basic usage, advanced features, and best practices.

What is google_maps_flutter?

The google_maps_flutter package is a Flutter plugin that provides a comprehensive set of APIs to integrate Google Maps into your Flutter app. It allows you to display maps, add markers, draw polygons, polylines, and circles, control the camera, and handle user interactions such as tapping and dragging the map.

Why Use google_maps_flutter?

  • Native Performance: Provides native map performance by utilizing platform-specific map implementations.
  • Comprehensive Feature Set: Supports a wide range of features, including markers, polylines, polygons, and map controls.
  • Customizable: Offers extensive customization options for map appearance and behavior.
  • Cross-Platform: Works seamlessly on both Android and iOS platforms from a single codebase.

Setup and Configuration

Before you start implementing maps in your Flutter app, you need to set up the google_maps_flutter plugin and obtain API keys for both Android and iOS.

Step 1: Add Dependencies

First, add the google_maps_flutter plugin to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.2.6

Run flutter pub get to install the package.

Step 2: Obtain Google Maps API Keys

You need a Google Maps API key for both Android and iOS. If you don’t have one already, follow these steps:

  1. Go to the Google Cloud Console.
  2. Create or select a project.
  3. Enable the Maps SDK for Android and Maps SDK for iOS.
  4. Create API keys for both Android and iOS.
  5. Restrict your API keys to your app’s package name (Android) and bundle identifier (iOS) to prevent unauthorized use.

Step 3: Configure Android

Add the API key to your AndroidManifest.xml file located in android/app/src/main/AndroidManifest.xml:


    <application>
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR_ANDROID_API_KEY"/>
    </application>
</manifest>

Replace YOUR_ANDROID_API_KEY with your actual API key.

Step 4: Configure iOS

Add the API key to your AppDelegate.swift or AppDelegate.m file. If using Swift, add the following to ios/Runner/AppDelegate.swift:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GMSServices.provideAPIKey("YOUR_IOS_API_KEY")
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

If using Objective-C, add the following to ios/Runner/AppDelegate.m:

#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import <GoogleMaps/GoogleMaps.h>

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GMSServices provideAPIKey:@"YOUR_IOS_API_KEY"];
  [GeneratedPluginRegistrant registerWithRegistry:self];
  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

Replace YOUR_IOS_API_KEY with your actual API key.

Additionally, you need to add the following keys to your Info.plist file located in ios/Runner/Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to your location when open to show you nearby places.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to your location to provide you with accurate location-based services.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to your location for optimal performance.</string>

These keys provide explanations to the user for why your app needs access to their location.

Basic Usage

Now that you have configured your project, let’s display a basic map in your Flutter app.

Step 1: Import the Package

Import the google_maps_flutter package into your Dart file:

import 'package:google_maps_flutter/google_maps_flutter.dart';

Step 2: Create a Map Widget

Create a GoogleMap widget in your Flutter app:

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  Completer<GoogleMapController> _controller = Completer();

  static final CameraPosition _kGooglePlex = CameraPosition(
    target: LatLng(37.42796133580664, -122.085749655962),
    zoom: 14.4746,
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Google Maps Demo'),
      ),
      body: GoogleMap(
        mapType: MapType.hybrid,
        initialCameraPosition: _kGooglePlex,
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
        },
      ),
    );
  }
}

In this example:

  • Completer<GoogleMapController> is used to get a reference to the GoogleMapController when the map is created.
  • CameraPosition defines the initial position and zoom level of the map.
  • GoogleMap widget displays the map. The onMapCreated callback is triggered when the map is created, providing you with a GoogleMapController instance.

Adding Markers

Markers are a common feature in map applications. Here’s how to add a marker to your map:

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  Completer<GoogleMapController> _controller = Completer();

  static final CameraPosition _kGooglePlex = CameraPosition(
    target: LatLng(37.42796133580664, -122.085749655962),
    zoom: 14.4746,
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Google Maps Demo'),
      ),
      body: GoogleMap(
        mapType: MapType.hybrid,
        initialCameraPosition: _kGooglePlex,
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
        },
        markers: {
          Marker(
            markerId: MarkerId('marker_1'),
            position: LatLng(37.42796133580664, -122.085749655962),
            infoWindow: InfoWindow(
              title: 'Googleplex',
              snippet: 'Google HQ',
            ),
          ),
        },
      ),
    );
  }
}

In this example:

  • A Marker widget is created with a unique markerId.
  • The position property specifies the latitude and longitude of the marker.
  • The infoWindow property allows you to display a title and snippet when the marker is tapped.

Moving the Camera

Programmatically moving the camera is essential for focusing on specific locations or tracking user movements.

Future<void> _goToTheLake() async {
  final GoogleMapController controller = await _controller.future;
  controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
}

static final CameraPosition _kLake = CameraPosition(
    bearing: 192.8334901395799,
    target: LatLng(37.43296265331129, -122.08832357078792),
    tilt: 59.440717697143555,
    zoom: 19.151926040649414);

This code animates the camera to a new position defined by _kLake. The animateCamera method provides smooth transitions between locations.

Advanced Features

The google_maps_flutter plugin supports a variety of advanced features, including:

  • Polylines: Drawing lines on the map.
  • Polygons: Drawing closed shapes on the map.
  • Circles: Drawing circles on the map.
  • Map Styles: Customizing the appearance of the map.
  • Traffic Data: Displaying real-time traffic information.

Drawing Polylines

polylines: {
  Polyline(
    polylineId: PolylineId('route'),
    points: [
      LatLng(37.42796133580664, -122.085749655962),
      LatLng(37.43296265331129, -122.08832357078792),
    ],
    width: 5,
    color: Colors.blue,
  ),
},

This code draws a blue polyline between two points on the map.

Drawing Polygons

polygons: {
  Polygon(
    polygonId: PolygonId('area'),
    points: [
      LatLng(37.42796133580664, -122.085749655962),
      LatLng(37.43296265331129, -122.08832357078792),
      LatLng(37.435, -122.08),
    ],
    fillColor: Colors.green.withOpacity(0.5),
    strokeColor: Colors.green,
    strokeWidth: 2,
  ),
},

This code draws a green polygon on the map, with a semi-transparent fill color.

Drawing Circles

circles: {
  Circle(
    circleId: CircleId('range'),
    center: LatLng(37.42796133580664, -122.085749655962),
    radius: 100,
    fillColor: Colors.red.withOpacity(0.3),
    strokeColor: Colors.red,
    strokeWidth: 1,
  ),
},

This code draws a red circle on the map with a specified radius.

Customizing Map Styles

You can customize the appearance of the map using JSON styles. Create a JSON file with your custom styles and load it into the GoogleMap widget.

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

Future<String> _loadMapStyle() async {
  return await rootBundle.loadString('assets/map_style.json');
}

@override
void initState() {
  super.initState();
  _loadMapStyle().then((style) {
    _controller.future.then((controller) {
      controller.setMapStyle(style);
    });
  });
}

Ensure your assets/map_style.json is correctly configured in your pubspec.yaml file.

Best Practices

  • API Key Security: Always restrict your API keys to your app’s package name (Android) and bundle identifier (iOS) to prevent unauthorized use.
  • Handle Location Permissions: Ensure you request and handle location permissions properly, providing clear explanations to the user.
  • Optimize Map Performance: Use clustering for a large number of markers to improve performance.
  • Manage Camera Movements: Use animateCamera for smooth transitions and provide a good user experience.
  • Error Handling: Implement error handling for map initialization and API requests.

Conclusion

The google_maps_flutter package is a versatile tool for integrating Google Maps into your Flutter applications. By following this guide, you can set up the plugin, display maps, add markers, draw shapes, customize map styles, and implement best practices for map integration. This powerful plugin enables you to create engaging and location-aware experiences for your users, leveraging the capabilities of Google Maps within your Flutter apps.