Location-based services have become a ubiquitous feature in modern mobile applications. Flutter, with its rich set of packages and cross-platform capabilities, makes it relatively straightforward to access a device’s location. The geolocator
package is one of the most popular choices for achieving this. In this comprehensive guide, we’ll walk you through the process of using the geolocator
package to access a device’s location in a Flutter app, ensuring accuracy, handling permissions, and providing best practices.
What is the Geolocator Package?
The geolocator
package is a Flutter plugin that provides easy access to location services on both Android and iOS platforms. It abstracts the complexities of the native location APIs, offering a simple, unified interface for Flutter developers.
Why Use the Geolocator Package?
- Cross-Platform Compatibility: Works seamlessly on both Android and iOS.
- Simple API: Easy-to-use methods for fetching location data.
- Accuracy Control: Allows configuring the desired accuracy level.
- Permission Handling: Provides utilities for requesting and checking location permissions.
- Background Location Updates: Supports background location updates (with appropriate configuration).
Step-by-Step Guide to Using Geolocator
Step 1: Add the Geolocator Package
First, you need to add the geolocator
package to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
geolocator: ^10.1.1
After adding the dependency, run flutter pub get
in your terminal to fetch the package.
Step 2: Configure Permissions
Before you can access the device’s location, you need to configure the necessary permissions for both Android and iOS.
Android Configuration
- Add Permissions to
AndroidManifest.xml
:Open
android/app/src/main/AndroidManifest.xml
and add the following permissions inside the<manifest>
tag:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- Required only when requesting background location access on Android 11+ --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="30"/> <!-- Required only when targeting Android 12+ --> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31"/> <!-- Required for foreground services on Android 9+ --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- Handle Background Location (Optional):
If your app needs to access the location in the background, you also need to declare a service in the
AndroidManifest.xml
:<application ...> <service android:name="com.baseflow.geolocator.GeolocatorService" android:foregroundServiceType="location" android:enabled="true" android:exported="false"/> <meta-data android:name="flutterEmbedding" android:value="2" /> </application>
iOS Configuration
- Add Permissions to
Info.plist
:Open
ios/Runner/Info.plist
and add the following keys:<key>NSLocationWhenInUseUsageDescription</key> <string>This app needs access to your location when open to provide location-based services.</string> <key>NSLocationAlwaysUsageDescription</key> <string>This app needs access to your location even when in the background to provide enhanced location-based services.</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>This app needs access to your location both when in use and in the background.</string>
Note:
NSLocationAlwaysUsageDescription
andNSLocationAlwaysAndWhenInUseUsageDescription
are required only if you need background location access.
Step 3: Import the Geolocator Package
In your Flutter Dart file, import the geolocator
package:
import 'package:geolocator/geolocator.dart';
Step 4: Implement Location Logic
Now, let’s implement the core logic for checking permissions and fetching the location.
Checking Location Permissions
First, check if the user has granted location permissions. If not, request them:
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class LocationService {
Future<bool> handleLocationPermission(BuildContext context) async {
bool serviceEnabled;
LocationPermission permission;
// Check if location services are enabled
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location services are disabled. Please enable the services')));
return false;
}
// Check location permission
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Location permissions are denied')));
return false;
}
}
if (permission == LocationPermission.deniedForever) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location permissions are permanently denied, we cannot request permissions.')));
return false;
}
// Permissions are granted
return true;
}
}
Fetching Current Location
Once permissions are granted, you can fetch the current location:
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class LocationService {
Future<Position> getCurrentLocation(BuildContext context) async {
final hasPermission = await handleLocationPermission(context);
if (!hasPermission) {
return Future.error('Location permissions not granted');
}
try {
return await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error getting location: ${e.toString()}')),
);
return Future.error(e);
}
}
Future<bool> handleLocationPermission(BuildContext context) async {
// Check if location services are enabled
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location services are disabled. Please enable the services')));
return false;
}
// Check location permission
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Location permissions are denied')));
return false;
}
}
if (permission == LocationPermission.deniedForever) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text(
'Location permissions are permanently denied, we cannot request permissions.')));
return false;
}
// Permissions are granted
return true;
}
}
Example Usage in a Widget
Here’s how you might use this service within a Flutter widget:
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
class LocationWidget extends StatefulWidget {
@override
_LocationWidgetState createState() => _LocationWidgetState();
}
class _LocationWidgetState extends State<LocationWidget> {
String _locationText = 'Fetching location...';
@override
void initState() {
super.initState();
_getLocation();
}
Future<void> _getLocation() async {
final locationService = LocationService();
try {
Position position = await locationService.getCurrentLocation(context);
setState(() {
_locationText = 'Latitude: ${position.latitude}, Longitude: ${position.longitude}';
});
} catch (e) {
setState(() {
_locationText = 'Error: ${e.toString()}';
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Location Example')),
body: Center(
child: Text(_locationText),
),
);
}
}
Best Practices for Using Geolocator
- Handle Permissions Gracefully:
Always check and request location permissions before attempting to fetch the location. Provide informative messages to the user about why the location permission is needed.
- Configure Accuracy:
Use the
desiredAccuracy
parameter of thegetCurrentPosition
method to balance accuracy with power consumption. High accuracy consumes more battery.Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high); // or balanced, low, lowest, etc.
- Handle Errors:
Wrap your location fetching logic in a
try-catch
block to handle potential exceptions (e.g., when location services are disabled or unavailable). - Use Streams for Continuous Updates:
For apps that require continuous location updates, use the
Geolocator.getPositionStream()
method to listen to location changes over time. Don’t forget to cancel the stream subscription when it’s no longer needed to avoid memory leaks.StreamSubscription<Position> positionStream = Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 100 // Minimum distance (meters) a device must move horizontally before an update event is generated; )).listen((Position position) { print('New position: ${position.latitude}, ${position.longitude}'); }); // Cancel subscription when not needed @override void dispose() { positionStream.cancel(); super.dispose(); }
- Consider Background Location Updates:
If your app needs location updates even when it’s in the background, configure the appropriate permissions and use
Geolocator.getPositionStream()
with background location settings. Be mindful of user privacy and battery consumption when using background location.
Advanced Features
Distance Calculation
The geolocator
package provides utilities to calculate the distance between two geographical coordinates. Use the Geolocator.distanceBetween()
method:
double distanceInMeters = Geolocator.distanceBetween(
latitude1, longitude1, latitude2, longitude2);
Bearing Calculation
You can also calculate the bearing (direction) between two geographical coordinates using Geolocator.bearingBetween()
:
double bearingInDegrees = Geolocator.bearingBetween(
latitude1, longitude1, latitude2, longitude2);
Conclusion
Accessing a device’s location in Flutter using the geolocator
package is straightforward, thanks to its simple and cross-platform API. By following the steps outlined in this guide and adhering to best practices, you can integrate location-based services into your Flutter applications efficiently and responsibly. Always prioritize user privacy, handle permissions gracefully, and optimize location settings for the best balance between accuracy and power consumption.