Working with Maps and Location Services in Flutter

Flutter’s capabilities extend far beyond basic UI development, offering robust support for integrating maps and location services into your applications. Whether you’re building a navigation app, a local business directory, or any app that benefits from location awareness, Flutter provides the tools you need. In this comprehensive guide, we’ll explore how to work with maps and location services in Flutter, covering everything from setting up the necessary dependencies to displaying interactive maps and handling user location data.

Why Use Maps and Location Services?

  • Enhanced User Experience: Provides location-based features that enhance the user experience.
  • Contextual Information: Delivers relevant information based on the user’s location.
  • Diverse Application: Applicable in a wide range of apps, including navigation, travel, and local business finders.

Prerequisites

Before we start, ensure you have the following:

  • Flutter SDK installed
  • Android Studio or VS Code with Flutter and Dart plugins
  • Basic knowledge of Flutter development

Setting Up Your Flutter Project

Step 1: Create a New Flutter Project

If you don’t already have a project, create a new Flutter project using the following command:

flutter create flutter_maps_location

Navigate into your new project:

cd flutter_maps_location

Step 2: Add Dependencies

Add the necessary dependencies to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.5.0 # Or the latest version
  geolocator: ^10.1.0 # Or the latest version
  cupertino_icons: ^1.0.2

Explanation of dependencies:

  • google_maps_flutter: For displaying Google Maps.
  • geolocator: For accessing device location services.

Run flutter pub get to install the dependencies.

Configuring Native Platforms

Android Setup

You need to obtain an API key for Google Maps and configure your Android project.

Step 1: Get a Google Maps API Key
  • Go to the Google Cloud Console.
  • Create a new project or select an existing one.
  • Enable the Maps SDK for Android.
  • Create an API key.
Step 2: Add the API Key to Your AndroidManifest.xml

Open android/app/src/main/AndroidManifest.xml and 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:name="io.flutter.app.FlutterApplication"
        android:label="flutter_maps_location"
        android:icon="@mipmap/ic_launcher">
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR_API_KEY"/>
        ...
    </application>

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-feature android:name="android.hardware.location.gps" android:required="false" />

</manifest>

Replace YOUR_API_KEY with your actual API key. Also, add the necessary permissions for location access.

Step 3: Configure Permissions

Add the following permissions to your AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false" />

iOS Setup

Step 1: Get a Google Maps API Key

Follow the same steps as in Android Setup to obtain a Google Maps API key.

Step 2: Add the API Key to Your AppDelegate.swift

Open ios/Runner/AppDelegate.swift and add the following:

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.

Step 3: Configure Permissions in Info.plist

Add the following keys to your ios/Runner/Info.plist file:

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to your location when open.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to your location when in the background.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to your location for better performance.</string>

These entries specify the reasons your app needs location permissions.

Displaying a Google Map in Flutter

Step 1: Import the Necessary Packages

In your Flutter code, import the necessary packages:

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

Step 2: Create a Map Widget

Create a StatefulWidget to manage the map:

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); // Example location: Portland

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

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

In this code:

  • We create a GoogleMap widget with an initialCameraPosition.
  • The onMapCreated callback is used to get a reference to the GoogleMapController.

Step 3: Display the Map

Integrate the MapScreen into your main app:

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Maps and Location',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapScreen(),
    );
  }
}

Accessing User Location

Step 1: Import the Geolocator Package

import 'package:geolocator/geolocator.dart';

Step 2: Request Location Permissions

Create a function to request location permissions:

Future<Position> _determinePosition() async {
  bool serviceEnabled;
  LocationPermission permission;

  // Test if location services are enabled.
  serviceEnabled = await Geolocator.isLocationServiceEnabled();
  if (!serviceEnabled) {
    return Future.error('Location services are disabled.');
  }

  permission = await Geolocator.checkPermission();
  if (permission == LocationPermission.denied) {
    permission = await Geolocator.requestPermission();
    if (permission == LocationPermission.denied) {
      return Future.error('Location permissions are denied');
    }
  }

  if (permission == LocationPermission.deniedForever) {
    return Future.error(
        'Location permissions are permanently denied, we cannot request permissions.');
  }
  
  return await Geolocator.getCurrentPosition();
}

Step 3: Get Current Location

Update your MapScreen to use the _determinePosition function:

class _MapScreenState extends State<MapScreen> {
  late GoogleMapController mapController;
  LatLng? _currentPosition;

  @override
  void initState() {
    super.initState();
    _getCurrentLocation();
  }

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

  Future<void> _getCurrentLocation() async {
    try {
      final position = await _determinePosition();
      setState(() {
        _currentPosition = LatLng(position.latitude, position.longitude);
      });
      mapController.animateCamera(CameraUpdate.newLatLngZoom(_currentPosition!, 15));
    } catch (e) {
      print('Error getting location: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Maps and Location in Flutter'),
      ),
      body: _currentPosition == null
          ? const Center(child: CircularProgressIndicator())
          : GoogleMap(
              onMapCreated: _onMapCreated,
              initialCameraPosition: CameraPosition(
                target: _currentPosition!,
                zoom: 15.0,
              ),
              myLocationEnabled: true,
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: _getCurrentLocation,
        child: const Icon(Icons.location_searching),
      ),
    );
  }

  Future<Position> _determinePosition() async {
    // (Previous location permission check and request code here)
  }
}

Explanation:

  • _determinePosition() checks and requests location permissions.
  • _getCurrentLocation() calls _determinePosition() to get the current location and updates the map.
  • myLocationEnabled: true enables the blue dot indicating the user’s location on the map.

Adding Markers

You can add markers to the map to highlight specific locations.

Step 1: Define a Marker

Create a set to hold markers:

final Set<Marker> _markers = {};

Step 2: Add Markers to the Map

Update the _getCurrentLocation function to add a marker:

Future<void> _getCurrentLocation() async {
  try {
    final position = await _determinePosition();
    setState(() {
      _currentPosition = LatLng(position.latitude, position.longitude);
      _markers.add(
        Marker(
          markerId: const MarkerId("current_location"),
          position: _currentPosition!,
          infoWindow: const InfoWindow(title: "Your Location"),
        ),
      );
    });
    mapController.animateCamera(CameraUpdate.newLatLngZoom(_currentPosition!, 15));
  } catch (e) {
    print('Error getting location: $e');
  }
}

Step 3: Update the GoogleMap Widget

Update the GoogleMap widget to display the markers:

GoogleMap(
  onMapCreated: _onMapCreated,
  initialCameraPosition: CameraPosition(
    target: _currentPosition!,
    zoom: 15.0,
  ),
  myLocationEnabled: true,
  markers: _markers,
),

Conclusion

Integrating maps and location services into your Flutter applications opens up a world of possibilities. This comprehensive guide has provided a solid foundation for displaying maps, accessing user location, and adding markers. By leveraging the google_maps_flutter and geolocator packages, you can create rich, location-aware experiences that delight your users.