Google Maps and Geolocation in Flutter

Google Maps and Geolocation in Flutter

Adding Officially Supported Google Maps in a Flutter app. Remove the current marker if not null as there is now a new location it needs to be at. Add marker at latitude and longitude.

Working with a user’s location is a common task that many apps require, yet the steps to implement a well working solution in an app is often confusing and has too much boilerplate required to set up. Most Flutter packages abstract away most or all of the boilerplate required and directly give users the most optimised solution and less hassle to deal with. Often this means some fine grained control is lost through abstraction, but most times it is not needed anyway.

You can look through a tutorial for implementing location in native android to see the immense and often not needed amount of information presented to app developers who simply want the user’s location or want to track it.

Today, we’ll look through implementing getting location, location tracking and geocoding in Flutter for both Android and iOS using two different plugins which give us a varied amount of control over the underlying APIs and then see the implementation of location with maps where we’ll track a user using a marker on a map.

Let’s get started.

Plugin 1: ‘location’

The ‘location’ plugin is the simplest way to get and track a user’s location without the hassle of any boilerplate code.

Setting up is simple enough:

  1. Add plugin to the pubspec.yaml
dependencies:
  location: ^1.4.1
  1. Add a permission to Android Manifest:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  1. Add permissions to iOS Info.plist
<key>NSLocationAlwaysUsageDescription</key>
<string>Needed to access location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Needed to access location</string>

And you’re done.

In Android 6.0+, we need to ask access to location in runtime, which this package handles on its own.

Getting a one-time location

This is image title

It allows us to get two things:

  1. A one-time location of the user
  2. A stream allowing us to listen to the location of the user.

Let’s create a simple app which gets the one-time location of the user like above.

Like we discussed, this package does not need much setup and the code is rather straightforward.

First, simply initialise a Location object.

var location = new Location();

location is not the location of the user itself but a class that helps us get it.

The location is returned as a map with keys like “latitude”, “longitude”, etc.

Map<String, double> userLocation;

And finally, getting the location is as simple as:

userLocation = await location.getLocation();

That’s it.

For the complete code:

class GetLocationPage extends StatefulWidget {
  @override
  _GetLocationPageState createState() => _GetLocationPageState();
}

class _GetLocationPageState extends State<GetLocationPage> {

  var location = new Location();

  Map<String, double> userLocation;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            userLocation == null
                ? CircularProgressIndicator()
                : Text("Location:" +
                    userLocation["latitude"].toString() +
                    " " +
                    userLocation["longitude"].toString()),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: RaisedButton(
                onPressed: () {
                  _getLocation().then((value) {
                    setState(() {
                      userLocation = value;
                    });
                  });
                },
                color: Colors.blue,
                child: Text("Get Location", style: TextStyle(color: Colors.white),),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Future<Map<String, double>> _getLocation() async {
    var currentLocation = <String, double>{};
    try {
      currentLocation = await location.getLocation();
    } catch (e) {
      currentLocation = null;
    }
    return currentLocation;
  }
}

GetLocationPage.dart

Here, we get the latitude and longitude. Other factors available are:

  currentLocation["latitude"];
  currentLocation["longitude"];
  currentLocation["accuracy"];
  currentLocation["altitude"];
  currentLocation["speed"];
  currentLocation["speed_accuracy"]; //Not for iOS

Listening to location updates

Listening to location updates is not that different from getting location, we simply use a stream object provided by the plugin to listen to updates.

location.onLocationChanged().listen((Map<String,double> currentLocation) {
  print(currentLocation["latitude"]);
  print(currentLocation["longitude"]);
  print(currentLocation["accuracy"]);
  print(currentLocation["altitude"]);
  print(currentLocation["speed"]);
  print(currentLocation["speed_accuracy"]); 
});

An example page of this would be:

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

class ListenPage extends StatefulWidget {
  @override
  _ListenPageState createState() => _ListenPageState();
}

class _ListenPageState extends State<ListenPage> {

  Location location = Location();

  Map<String, double> currentLocation;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    location.onLocationChanged().listen((value) {
      setState(() {
        currentLocation = value;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: <Widget>[
          currentLocation == null
              ? CircularProgressIndicator()
              : Text("Location:" + currentLocation["latitude"].toString() + " " + currentLocation["longitude"].toString()),
        ],
      ),
    );
  }
}

ListenPage.dart

Which gives a similar page to the one we made with the first example, but this time, we listen for location.

This plugin was a simple one to use without much setup or control and was straightforward in use and intention. Next, we’ll see another which gives more control over the APIs involved and also allows for geocoding.

Plugin 2: ‘geolocator’

Setup

The setup for this plugin is more or less the same as the location. If you plan to use coarse location as well, add

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

to AndroidManifest.xml .

First, we will do the same two tasks with this plugin, which is more or less the same:

Get location once

Use the Geolocator object instead of Location and instead of a Map<String,double> , we get back a Position object.

Geolocator geolocator = Geolocator();

Position userLocation;

And we do this to take location:

currentLocation = await geolocator.getCurrentPosition(
    desiredAccuracy: LocationAccuracy.best);

Here, we can also set the location accuracy depending on the accuracy we need.

import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'dart:async';

class GeoListenPage extends StatefulWidget {
  @override
  _GeoListenPageState createState() => _GeoListenPageState();
}

class _GeoListenPageState extends State<GeoListenPage> {
  Geolocator geolocator = Geolocator();

  Position userLocation;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _getLocation().then((position) {
      userLocation = position;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            userLocation == null
                ? CircularProgressIndicator()
                : Text("Location:" +
                    userLocation.latitude.toString() +
                    " " +
                    userLocation.longitude.toString()),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: RaisedButton(
                onPressed: () {
                  _getLocation().then((value) {
                    setState(() {
                      userLocation = value;
                    });
                  });
                },
                color: Colors.blue,
                child: Text(
                  "Get Location",
                  style: TextStyle(color: Colors.white),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Future<Position> _getLocation() async {
    var currentLocation;
    try {
      currentLocation = await geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.best);
    } catch (e) {
      currentLocation = null;
    }
    return currentLocation;
  }
}

GeolocatorListenPage.dart

Get Location Updates

Updates are similar to the ‘location’ package but with enhanced options.

geolocator.getPositionStream().listen((position) {
  // Do something here
});

We can set the accuracy, time interval between updates, whether we want to force usage of Android location manager, etc.

geolocator
    .getPositionStream(LocationOptions(
        accuracy: LocationAccuracy.best, timeInterval: 1000))
    .listen((position) {
  // Do something here
});

Working with Geocoding

Geocoding is essentially the process of getting physical map co-ordinates and details for an address.

List<Placemark> placemark = await Geolocator().placemarkFromAddress("Gronausestraat 710, Enschede");

You can get all the details about the place as:

print(placemark[0].country);
print(placemark[0].position);
print(placemark[0].locality);
print(placemark[0].administrativeArea);
print(placemark[0].postalCode);
print(placemark[0].name);
print(placemark[0].subAdministratieArea);
print(placemark[0].isoCountryCode);
print(placemark[0].subLocality);
print(placemark[0].subThoroughfare);
print(placemark[0].thoroughfare);

We can also get the details of a place from the coordinates:

List<Placemark> placemark = await Geolocator().placemarkFromCoordinates(52.2165157, 6.9437819);

Getting the distance between coordinates

This plugin also allows us to calculate the distance between two coordinates as:

double distanceInMeters = await Geolocator().distanceBetween(52.2165157, 6.9437819, 52.3546274, 4.8285838);

Integrating location updates with Google Maps

To integrate Google Maps with location updates, we need to use the google_maps_flutter package and your choice of location plugin.

Let’s just use the ‘location’ package for now.

A precursor to this part is my “Exploring Google Maps in Flutter” article and I highly recommend you use it to set up Maps in your app.

Exploring Google Maps in Flutter

To integrate maps with location tracking, we simply need to use both the plugins in conjunction.

Let’s move a marker as the user changes location.

First, we create our MapController and Location objects to manipulate maps and location. (Assign mapController when map is initialised, as given in the upper article)

GoogleMapController mapController;
Location location = Location();

Next, we add a marker to store the current marker:

Marker marker;

Now, we listen to location updates.

location.onLocationChanged().listen((location) async {
  // Do something here
});

This is how we will move the marker when location is updated:

  1. Remove the current marker if not null as there is now a new location it needs to be at.
  2. Add marker at latitude and longitude.
  3. Move camera to new marker location and set other characteristics like zoom, bearing and tilt. (Details about this given in GMaps article)
location.onLocationChanged().listen((location) async {
  // Step 1
  if(marker != null) {
    mapController.removeMarker(marker);
  }
  // Step 2
  marker = await mapController?.addMarker(MarkerOptions(
    position: LatLng(location["latitude"], location["longitude"]),
  ));
  //Step 3
  mapController?.moveCamera(
    CameraUpdate.newCameraPosition(
      CameraPosition(
        target: LatLng(
          location["latitude"],
          location["longitude"],
        ),
        zoom: 20.0,
      ),
    ),
  );
});

And now the marker will update location when the user moves!

Here’s the complete demo page code:

class GoogleMapsDemo extends StatefulWidget {
  @override
  _GoogleMapsDemoState createState() => _GoogleMapsDemoState();
}

class _GoogleMapsDemoState extends State<GoogleMapsDemo> {
  GoogleMapController mapController;
  Location location = Location();

  Marker marker;

  @override
  void initState() {
    super.initState();
    location.onLocationChanged().listen((location) async {
      if(marker != null) {
        mapController.removeMarker(marker);
      }
      marker = await mapController?.addMarker(MarkerOptions(
        position: LatLng(location["latitude"], location["longitude"]),
      ));
      mapController?.moveCamera(
        CameraUpdate.newCameraPosition(
          CameraPosition(
            target: LatLng(
              location["latitude"],
              location["longitude"],
            ),
            zoom: 20.0,
          ),
        ),
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Container(
            height: MediaQuery.of(context).size.height,
            width: MediaQuery.of(context).size.width,
            child: GoogleMap(
              onMapCreated: (GoogleMapController controller) {
                mapController = controller;
              },
              options: GoogleMapOptions(
                cameraPosition: CameraPosition(
                  target: LatLng(37.4219999, -122.0862462),
                ),
                myLocationEnabled: true,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

GMapsPlusLocation.dart

That’s it for this article! I hope you enjoyed it, and leave a few claps if you did. Follow me for more Flutter articles and comment for any feedback you might have about this article.

flutter programming

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Google's Flutter 1.20 stable announced with new features - Navoki

Google has announced new flutter 1.20 stable with many improvements, and features, enabling flutter for Desktop and Web

What is Flutter and why you should learn it?

Flutter is an open-source UI toolkit for mobile developers, so they can use it to build native-looking Android and iOS applications from the same code base for both platforms. Flutter is also working to make Flutter apps for Web, PWA (progressive Web-App) and Desktop platform (Windows,macOS,Linux).

Complete SQLite CRUD Operations in Flutter

Now a days almost all application have to have some kind of data storage. Application without collaboration with other users will make use of local storage db – SQLite. In this tutorial, we are going to cover all CRUD operations in Flutter with SQLite.

Adobe XD plugin for Flutter with CodePen Tutorial

Recently Adobe XD releases a new version of the plugin that you can use to export designs directly into flutter widgets or screens.

Flutter - How to Add AdMob Real Ads in Flutter App | Flutter AdMob Tutorial

Hello Whats is up Everyone So, Today I am going to show u How to Add Admob Real ads in Flutter apps which are very Easy Implement After watching this video u...