In Flutter development, implementing location-based features can greatly enhance user experience by triggering actions when users enter or exit specific geographical areas. This is commonly achieved using Geofencing. Geofencing allows you to define virtual perimeters around real-world locations and receive notifications when a device enters or exits these boundaries.
What is Geofencing?
Geofencing is a location-based service that defines virtual boundaries around specific geographical areas. When a device enters or exits these defined areas, the system can trigger various actions, such as sending notifications, starting specific tasks, or updating application states.
Why Use Geofencing in Flutter?
- Context-Aware Applications: Provides context-aware functionality based on the user’s location.
- Enhanced User Engagement: Triggers relevant actions and notifications based on location.
- Automated Tasks: Automates tasks when users enter or exit specific areas, such as reminding them to buy groceries when they approach a supermarket.
How to Implement Geofencing in Flutter
To implement geofencing in Flutter, you can use the geofencing plugin, which provides the necessary tools and functionalities.
Step 1: Add the geofencing Package
First, add the geofencing package to your pubspec.yaml file:
dependencies:
flutter:
sdk: flutter
geofencing: ^0.2.1
Then, run flutter pub get to install the package.
Step 2: Configure AndroidManifest.xml
For Android, you need to add the necessary permissions and services to your AndroidManifest.xml file located in android/app/src/main/:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="your_package_name">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name="io.flutter.app.FlutterApplication"
android:label="your_app_name"
android:icon="@mipmap/ic_launcher">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_GOOGLE_MAPS_API_KEY"/>
<service
android:name="com.transistorsoft.flutter.backgroundfetch.BackgroundFetchHeadlessTask"
android:exported="false" />
<service
android:name="com.transistorsoft.flutter.backgroundgeolocation.GeofenceBackgroundService"
android:exported="false" />
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
Ensure you replace your_package_name, your_app_name, and YOUR_GOOGLE_MAPS_API_KEY with your actual package name, app name, and Google Maps API key, respectively.
Step 3: Configure iOS Info.plist
For iOS, you need to add the necessary permissions to your Info.plist file located in ios/Runner/:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<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 geofencing features.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs access to your location in background.</string>
</dict>
</plist>
Step 4: Initialize Geofencing and Define Geofences
In your Flutter code, initialize geofencing and define the geofences. Here’s a sample:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:geofencing/geofencing.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String geofenceState = 'Unknown';
List<GeofenceRegion> geofenceRegions = <GeofenceRegion>[];
@override
void initState() {
super.initState();
initGeofencing();
}
Future<void> initGeofencing() async {
GeofencingManager.initialize().then((_) {
print('Geofencing initialized');
});
GeofencingManager.registerGeofenceRegionStateChange(
id: 'myGeofence',
latitude: 37.7749,
longitude: -122.4194,
radius: 100)
.listen((GeofenceRegionState state) {
setState(() {
geofenceState = state.toString();
});
print('Geofence State: $state');
});
GeofencingManager.startGeofenceService(
pointedCallback: _callbackDispatcher,
failed: _onGeofenceError,
geofenceSendFrequency: LocationSendFrequencies.NEVER);
}
static void _callbackDispatcher() {
WidgetsFlutterBinding.ensureInitialized();
GeofencingManager.registerGeofenceRegionStateChange(
id: 'myGeofence',
latitude: 37.7749,
longitude: -122.4194,
radius: 100)
.listen((GeofenceRegionState state) {
print('Geofence State in Callback: $state');
_onGeofenceStateChanged(state.toString());
});
}
static void _onGeofenceStateChanged(String result) {
print('Geofence callback: $result');
}
void _onGeofenceError(error) {
print('Geofence error: $error');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Geofencing Example'),
),
body: Center(
child: Text('Geofence State: $geofenceState'),
),
),
);
}
}
This example does the following:
- Initializes the
GeofencingManager. - Registers a geofence region at latitude 37.7749 and longitude -122.4194 with a radius of 100 meters.
- Listens for geofence state changes (entry or exit) and updates the UI accordingly.
Step 5: Handle Geofence State Changes
The registerGeofenceRegionStateChange method provides a stream of GeofenceRegionState events that you can listen to. These events indicate when the user enters or exits the geofenced region.
GeofencingManager.registerGeofenceRegionStateChange(
id: 'myGeofence',
latitude: 37.7749,
longitude: -122.4194,
radius: 100)
.listen((GeofenceRegionState state) {
setState(() {
geofenceState = state.toString();
});
print('Geofence State: $state');
if (state == GeofenceRegionState.ENTER) {
// Perform actions when entering the geofence
showNotification('Entered Geofence');
} else if (state == GeofenceRegionState.EXIT) {
// Perform actions when exiting the geofence
showNotification('Exited Geofence');
}
});
Step 6: Implement Notifications (Optional)
To notify the user when they enter or exit the geofence, you can use the flutter_local_notifications package:
dependencies:
flutter_local_notifications: ^9.9.0
Then, implement a function to show local notifications:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> showNotification(String message) async {
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'geofence_channel', 'Geofence Notifications',
channelDescription: 'Notifications triggered by geofence events',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, 'Geofence Event', message, platformChannelSpecifics,
payload: 'item x');
}
Tips and Considerations
- Battery Consumption: Geofencing can consume significant battery life, especially with small radii and frequent updates. Optimize your implementation by using larger radii and less frequent updates where possible.
- Permissions: Ensure you request and handle location permissions appropriately, providing clear explanations to the user about why the permissions are needed.
- Testing: Geofencing can be challenging to test due to its reliance on location data. Use tools like emulators with simulated locations or physical devices to ensure proper functionality.
Conclusion
Implementing geofencing in Flutter allows you to create powerful, location-aware applications that can enhance user engagement and automate tasks. By using the geofencing plugin and following the steps outlined in this guide, you can effectively integrate geofencing into your Flutter projects and trigger actions based on location.