<strong>Google Map With Live Location Tracking In Flutter</strong>

Introduction

In this tutorial, you will learn how to integrate Google Maps into a Flutter app with live location tracking capabilities. We will cover customizations like setting up custom image markers and drawing route direction polylines. Additionally, you’ll discover how to add real-time location updates to the map, making your app more interactive and user-friendly.

Set Up Google Maps API:

To get started, you’ll need to set up the Google Maps API. Follow these steps:

  1. Go to the Google Cloud Console at (https://console.cloud.google.com/) and create a new project.
  2. Enable the Maps SDK for Android and Maps SDK for iOS within your project settings.
  3. Generate an API KEY for your project. This API KEY will be used to access Google Maps services from your Flutter app.

Add API key in Android and iOS:

In Android’s (android/app/src/main/AndroidManifest.xml)file, include the following:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
        <meta-data android:name="com.google.android.geo.API_KEY"
            android:value="API KEY"/>
    </application>
</manifest>

In iOS(ios/Runner/AppDelegate.m)file, include the following:

override func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
  GMSServices.provideAPIKey("API KEY")
  GeneratedPluginRegistrant.register(with: self)
  return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

Adding Dependencies:

In pubspec.yaml file, add the required packages:

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.3.1
  flutter_polyline_points: ^1.0.0
  geolocator: ^9.0.2

Run the following command to install dependencies

$ flutter pub get

Permission and Platform Setup:

Android(android/app/src/main/AndroidManifest.xml)

For Android, add the necessary location permission in AndroidManifest.xml:

<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”/>
<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”/>

If you want to access the user’s location in the background as well, add this permissions:

<uses-permission android:name=”android.permission.ACCESS_BACKGROUND_LOCATION”/>

iOS (ios/Runner/Info.plist) 

For iOS, include the following location permissions in Info.plist:

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to location when open and in the background.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location when in the background.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to current location.</string>

Integrating Google Map:

Now, let’s add the Google Maps widget to the Flutter widget:

import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

//Declare Variable

final Completer<GoogleMapController> _controller = Completer();
LatLng? startLocation;

@override
Widget build(BuildContext context) {
  return Scaffold(
    Body: GoogleMap(zoomGesturesEnabled: true,
                    initialCameraPosition: CameraPosition(
                             target: startLocation ?? LatLng(0, 0),
                             zoom: 16,
                    ),
                    markers: {
                       if (startLocation != null)
                       Marker(
                           markerId: const MarkerId("startLocation"),
                           position: startLocation ?? LatLng(0, 0),
                          ),
                    },
                    mapType: MapType.normal,
                    onMapCreated: (mapController) {
                          _controller.complete(mapController);
                    },
             ),
    );
}

Get current location:

Next, let’s get the user’s current location and update the map accordingly:

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

Future<Position> _checkPermission() async {
  LocationPermission permission;
  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.’);
  }
  return await Geolocator.getCurrentPosition();
}


void _getCurrentLocation() async {
  Position position = await _checkPermission();
  setState(() {
    startLocation = LatLng(position.latitude, position.longitude);
    updateMap();
  });
}
void updateMap() async {
  GoogleMapController googleMapController = await _controller.future;
  var tmpZoom = await googleMapController.getZoomLevel();
  googleMapController.animateCamera( CameraUpdate.newCameraPosition(
      CameraPosition(target: startLocation ?? LatLng(0, 0),
        zoom: tmpZoom,),
    ),);
}

Real-time Location Updates: 

Let’s set up real-time location updates using the geolocator package:

//Declare Variable

static const LocationSettings locationSettings = LocationSettings(accuracy: LocationAccuracy.high, distanceFilter: 50);
StreamSubscription<Position>? currentPostionStream;
bool isRouting = false;
List<LatLng> arrPointer = [];

void _getCurrentLocation() async {
  Position position = await _checkPermission();
  setState(() {
    arrPointer = [];
    startLocation = LatLng(position.latitude, position.longitude);
    arrPointer.add(LatLng(position.latitude, position.longitude));
    updateMap();
  });
}

void startRoutingLocation() async {
      currentPostionStream = Geolocator.getPositionStream(locationSettings: locationSettings)
      .listen((Position? position) async {
    print('loc updated - Listener ${position!.latitude},${position.longitude}');

    setState(() {
      arrPointer.add(LatLng(position.latitude, position.longitude));
    });
  });
}

void stopRoutingLocation() {
  if(currentPostionStream != null) {
    currentPostionStream?.cancel();
    arrPointer.clear();
  }
}

Drawing Polyline: 

Now, let’s draw the polylines to visualize the route on the map:

markers: {
        if (startLocation != null)
          Marker(
            markerId: const MarkerId("startLocation"),
            position: startLocation ?? LatLng(0, 0),
          ),
          if (arrPointer.length > 1)
          Marker(
            markerId: const MarkerId("endLocation"),
            position: arrPointer.last,
          )
 },
polylines: {
           Polyline(
            polylineId:  const PolylineId("route"),
            points: isRouting ? arrPointer :
               [],
            color: Colors.black,
            width: 4,
          ),
 },

Start and Stop Tracking Button:

Finally, we’ll add buttons to start and stop the live tracking feature:

//Declare Variable

bool isRouting = false;
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: startLocation == null
        ? const Center(child: CircularProgressIndicator())
        : Stack(
          children: [
GoogleMap(
      zoomGesturesEnabled: true,
      initialCameraPosition: CameraPosition(
          target: startLocation ?? LatLng(0, 0), zoom: 16,
      ),
      markers: {
        if (startLocation != null)
          Marker(
            markerId: const MarkerId(“startLocation”),
            position: startLocation ?? LatLng(0, 0),
          ),
          if (arrPointer.length > 1)
          Marker(
            markerId: const MarkerId(“endLocation”),
            position: arrPointer.last,
          )
      },
      polylines: {
          Polyline(
            polylineId:  const PolylineId(“route”),
            points: isRouting ? arrPointer :
              [],
            color: Colors.black,
            width: 4,
          ),
      },
      mapType: MapType.normal,
      onMapCreated: (mapController) {
          _controller.complete(mapController);
      },
    ),
    Padding(padding: EdgeInsets.only(top: MediaQuery.of(context).size.height – 100, left: 50,right: 50),
              child: SizedBox(height: 50, child:
                  Row(children: [
                    Expanded(child: ElevatedButton(onPressed: (){
                      _getCurrentLocation();
                      startRoutingLocation();
                      setState(() {
                        isRouting = true;
                      });
                    }, child: const Text(“Start”))),
                    const SizedBox(width: 20,),
                    Expanded(child: ElevatedButton(onPressed: (){
                      stopRoutingLocation();
                      _getCurrentLocation();
                      setState(() {
                        isRouting = false;
                      });
                    }, child: const Text(“Stop”))),
                  ],),
              ),
            ),

          ]),
  );
}

Add Custom Marker:

To add a custom marker to the Google Map, follow these steps:

1). Declare the BitmapDescriptor variable in your widget’s state to hold the custom marker icon:

BitmapDescriptor markerIcon = BitmapDescriptor.defaultMarker;

2). In the initState() method, call the addCustomIcon() method to load the custom marker icon:

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

void addCustomIcon() {
  BitmapDescriptor.fromAssetImage(
      const ImageConfiguration(size: Size(60, 60)), ‘assets/location_delivery.png’)
      .then((icon) {
    setState(() {
      markerIcon = icon;
    });
  });
}

3). Use the markerIcon in the Marker widget’s icon property to apply the custom marker icon:

markers: {
    if (startLocation != null)
        Marker(
            markerId: const MarkerId(“startLocation”),
            position: startLocation ?? LatLng(0, 0),
        ),
        if (arrPointer.length > 1)
        Marker(
            markerId: const MarkerId(“endLocation”),
            position: arrPointer.last,

icon: markerIcon, )
},

Note: Make sure to replace ‘assets/location_delivery.png’ with the correct path to your custom marker icon.

Additional information

For access to the full source code, please check the Live tracking Demo

You may also like

Leave a Reply