Displaying Maps and Markers Using the google_maps_flutter Package in Flutter

Flutter, Google’s UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase, offers robust support for integrating maps. The google_maps_flutter package is a popular choice for displaying Google Maps in your Flutter apps. This guide covers how to use the google_maps_flutter package to display maps, add markers, and customize the map’s appearance.

Introduction to the google_maps_flutter Package

The google_maps_flutter package is a Flutter plugin that displays Google Maps. It provides a comprehensive set of features to interact with maps, including adding markers, polylines, polygons, and circles, as well as controlling camera movements and handling map events.

Why Use google_maps_flutter?

  • Integration: Seamlessly integrates Google Maps into Flutter apps.
  • Customization: Offers extensive customization options for map appearance.
  • Markers: Simplifies the process of adding and managing map markers.
  • Map Controls: Provides methods to control camera position, zoom level, and more.

Setting Up Your Flutter Project

Before diving into the code, you’ll need to set up your Flutter project and configure it to use the google_maps_flutter package.

Step 1: Add the Dependency

Add the google_maps_flutter package to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.5.0 # Use the latest version

Run flutter pub get in your terminal to install the package.

Step 2: Configure Your API Keys

To use the google_maps_flutter package, you need to obtain an API key from the Google Cloud Platform and configure it in your app. Here’s how to do it:

Android
  1. Open your android/app/src/main/AndroidManifest.xml file.
  2. Add the following <meta-data> tag inside the <application> tag:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="your.package.name">
    <application
        android:label="Your App"
        android:icon="@mipmap/ic_launcher">
        
        <!-- You will need to get a maps_api_key for Google Maps -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR_API_KEY" />

        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <<!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

Replace "YOUR_API_KEY" with your actual API key.

iOS
  1. Open your ios/Runner/AppDelegate.swift file.
  2. Add the following code at the top of the application method:
import UIKit
import Flutter
import GoogleMaps

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

Replace "YOUR_API_KEY" with your actual API key.

Displaying a Basic Map

Now that you’ve set up your project, you can start displaying a basic map in your Flutter app.

Step 1: Create a Map Widget

Create a stateful widget to hold the map and its state:

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> {
  late GoogleMapController mapController;
  
  final LatLng _center = const LatLng(45.521563, -122.677433);

  void _onMapCreated(GoogleMapController controller) {
    mapController = controller;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Google Maps in Flutter'),
      ),
      body: GoogleMap(
        onMapCreated: _onMapCreated,
        initialCameraPosition: CameraPosition(
          target: _center,
          zoom: 11.0,
        ),
      ),
    );
  }
}

In this code:

  • MapScreen is a stateful widget that holds the map.
  • GoogleMapController is used to control the map.
  • _center is the initial latitude and longitude of the map’s center.
  • _onMapCreated is a callback function that’s called when the map is created. It provides a GoogleMapController instance, which you can use to interact with the map.
  • GoogleMap is the widget that displays the map. It takes an initialCameraPosition property to set the starting position of the map.

Step 2: Add Markers to the Map

To add markers to the map, you’ll need to create a Set<Marker> and pass it to the markers property of the GoogleMap widget.

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> {
  late GoogleMapController mapController;
  
  final LatLng _center = const LatLng(45.521563, -122.677433);
  
  final Set<Marker> _markers = {};

  void _onMapCreated(GoogleMapController controller) {
    mapController = controller;
    setState(() {
      _markers.add(
        Marker(
          markerId: MarkerId(_center.toString()),
          position: _center,
          infoWindow: const InfoWindow(
            title: 'My Location',
            snippet: 'This is my current location',
          ),
          icon: BitmapDescriptor.defaultMarker,
        ),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Google Maps in Flutter'),
      ),
      body: GoogleMap(
        onMapCreated: _onMapCreated,
        initialCameraPosition: CameraPosition(
          target: _center,
          zoom: 11.0,
        ),
        markers: _markers,
      ),
    );
  }
}

In this code:

  • _markers is a Set<Marker> that holds the map markers.
  • Inside the _onMapCreated method, a Marker is created and added to the _markers set.
  • The Marker constructor takes several properties, including markerId, position, infoWindow, and icon.
  • The markers property of the GoogleMap widget is set to the _markers set.

Customizing the Map

The google_maps_flutter package allows you to customize the appearance of the map by setting various properties of the GoogleMap widget.

Controlling Camera Movement

You can control the camera movement programmatically using the GoogleMapController. For example, you can animate the camera to a new position:

void _goToNewLocation() {
  final LatLng newLocation = const LatLng(45.521563, -122.0); // New location

  mapController.animateCamera(CameraUpdate.newCameraPosition(
    CameraPosition(
      target: newLocation,
      zoom: 12,
      tilt: 50.0,
      bearing: 45.0,
    ),
  ));
}

Call this method in response to a user action, such as pressing a button:

FloatingActionButton(
  onPressed: _goToNewLocation,
  child: const Icon(Icons.map),
)

Changing Map Type

You can change the map type by setting the mapType property of the GoogleMap widget. Available map types include MapType.normal, MapType.satellite, MapType.hybrid, and MapType.terrain.

GoogleMap(
  onMapCreated: _onMapCreated,
  initialCameraPosition: CameraPosition(
    target: _center,
    zoom: 11.0,
  ),
  mapType: MapType.hybrid, // Set the map type to hybrid
  markers: _markers,
)

Enabling Traffic Data

To display traffic data on the map, set the trafficEnabled property to true:

GoogleMap(
  onMapCreated: _onMapCreated,
  initialCameraPosition: CameraPosition(
    target: _center,
    zoom: 11.0,
  ),
  trafficEnabled: true, // Enable traffic data
  markers: _markers,
)

Customizing Markers

You can customize the appearance of markers by using custom icons and info windows.

Custom Icons

Load a custom image as a marker icon:

import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/services.dart';

Future<BitmapDescriptor> _bitmapDescriptorFromAsset(String assetName, int width) async {
  final ByteData data = await rootBundle.load(assetName);
  final ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width);
  final ui.FrameInfo fi = await codec.getNextFrame();
  final byteData = await fi.image.toByteData(format: ui.ImageByteFormat.png);
  
  if (byteData == null) {
    throw Exception("Failed to convert image to bytes.");
  }
  
  return BitmapDescriptor.fromBytes(byteData.buffer.asUint8List());
}

// Usage:
Future<void> _addCustomMarker() async {
    final BitmapDescriptor customIcon = await _bitmapDescriptorFromAsset('assets/custom_marker.png', 100);

    setState(() {
      _markers.add(
        Marker(
          markerId: MarkerId(_center.toString()),
          position: _center,
          infoWindow: const InfoWindow(
            title: 'My Location',
            snippet: 'Custom marker example',
          ),
          icon: customIcon,
        ),
      );
    });
}

Ensure you have added the asset to your pubspec.yaml:

flutter:
  assets:
    - assets/custom_marker.png

Handling Map Events

The GoogleMap widget provides various callbacks for handling map events, such as tapping on the map or moving the camera.

On Tap Event

The onTap callback is called when the user taps on the map:

GoogleMap(
  onMapCreated: _onMapCreated,
  initialCameraPosition: CameraPosition(
    target: _center,
    zoom: 11.0,
  ),
  markers: _markers,
  onTap: (LatLng latLng) {
    print('Map tapped at: ${latLng.latitude}, ${latLng.longitude}');
  },
)

On Camera Move Started

The onCameraMoveStarted callback is called when the camera starts moving:

GoogleMap(
  onMapCreated: _onMapCreated,
  initialCameraPosition: CameraPosition(
    target: _center,
    zoom: 11.0,
  ),
  markers: _markers,
  onCameraMoveStarted: () {
    print('Camera move started');
  },
)

Advanced Features

The google_maps_flutter package also supports advanced features such as polylines, polygons, and circles.

Polylines

Display polylines on the map:

final Set<Polyline> _polylines = {
    Polyline(
        polylineId: PolylineId('route1'),
        points: [
          LatLng(45.521563, -122.677433),
          LatLng(45.530000, -122.680000),
          LatLng(45.540000, -122.690000),
        ],
        width: 5,
        color: Colors.blue,
    ),
  };

  // In GoogleMap widget
  GoogleMap(
    // ...
    polylines: _polylines,
  )

Polygons

Display polygons on the map:

final Set<Polygon> _polygons = {
    Polygon(
      polygonId: PolygonId('area1'),
      points: [
        LatLng(45.521563, -122.677433),
        LatLng(45.530000, -122.680000),
        LatLng(45.540000, -122.690000),
        LatLng(45.510000, -122.660000),
      ],
      fillColor: Colors.green.withOpacity(0.3),
      strokeColor: Colors.green,
      strokeWidth: 2,
    ),
  };

  // In GoogleMap widget
  GoogleMap(
    // ...
    polygons: _polygons,
  )

Circles

Display circles on the map:

final Set<Circle> _circles = {
    Circle(
      circleId: CircleId('circle1'),
      center: LatLng(45.521563, -122.677433),
      radius: 500,
      fillColor: Colors.red.withOpacity(0.3),
      strokeColor: Colors.red,
      strokeWidth: 2,
    ),
  };

  // In GoogleMap widget
  GoogleMap(
    // ...
    circles: _circles,
  )

Troubleshooting

Here are some common issues and their solutions:

  • Map Not Displaying:
    • Ensure you have correctly configured the API key in your AndroidManifest.xml (Android) or AppDelegate.swift (iOS).
    • Verify that the API key has the necessary permissions enabled in the Google Cloud Platform.
  • Markers Not Appearing:
    • Confirm that the coordinates for the markers are valid.
    • Check if the markers property of the GoogleMap widget is correctly set.
  • GoogleMapController Not Working:
    • Make sure that the onMapCreated callback is properly assigning the GoogleMapController.
    • Verify that you are calling setState when updating the markers.

Conclusion

The google_maps_flutter package is a versatile tool for integrating Google Maps into your Flutter applications. By following this comprehensive guide, you can easily display maps, add markers, customize the map’s appearance, and handle map events. Whether you’re building a travel app, a location-based service, or any other application that requires mapping functionality, google_maps_flutter is a great choice for implementing these features.