What is Geolocation?
Table of contents
- How to Set Up Environments
For Android
For IOS
Handle Location Permission and Services
Get User Latitude and Longitude
1. Setting up the project Environments
First, We must add two packages in you Pubspec.yaml required dependencies:
dependencies:
geolocator: ^10.1.0
geocoding: ^2.1.1
Or using this command, you also add this :
$ flutter pub add geolocator
$ flutter pub add geocoding
Then, we have to add permission for Android and iOS
For Android
- First we have to add the following codes in your gradle.properties file,To do so open the AndroidManifest.xml file (located under android/app/src/main) and add one of the following two lines as direct
children of the <manifest> tag (when you configure both permissions the ACCESS_FINE_LOCATION will be used by the geolocator plugin):
android.useAndroidX=true
android.enableJetifier=true
Then make sure your compileSdkVersion in your android/app/build.gradle file:
android {
compileSdkVersion 31
...
}
Then we have to add ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permission in your android/app/src/main/AndroidManifest.xml file:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Starting from Android 10 you need to add the ACCESS_BACKGROUND_LOCATION permission (next to the ACCESS_COARSE_LOCATION or the ACCESS_FINE_LOCATION permission) if you want to continue receiving updates even when your App is running in the background (note that the geolocator plugin doesn't support receiving and processing location updates while running in the background):
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
For iOS
We need to add following lines in your ios/Runner/Info.plist file for access to your location.
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location when in the background.</string>
If you would like to receive updates when your App is in the background, you'll also need to add the Background Modes capability to your XCode project (Project > Signing and Capabilities > "+ Capability" button) and select Location Updates. Be careful with this, you will need to explain in detail to Apple why your App needs this when submitting your App to the AppStore. If Apple isn't satisfied with the explanation your App will be rejected.
When using the requestTemporaryFullAccuracy({purposeKey: "YourPurposeKey"}) method, a dictionary should be added to the Info.plist file.
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>YourPurposeKey</key>
<string>The example App requires temporary access to the device's precise location.</string>
</dict>
2. Handle location services and permissions
In order to get the user’s location, we must have the user's permission. So we will create a method to check and request the location services and permission.
import 'package:flutter/material.dart'; import 'package:flutter_location_practice/home_page.dart'; import 'package:geocoding/geocoding.dart' as gc; import 'package:geolocator/geolocator.dart' as gl; import 'package:permission_handler/permission_handler.dart' as ph; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); String? _currentIsoCode; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _isAndroidPermissionGranted(); _requestPermissions(); registerNotification(); _getCurrentPosition(); if (Platform.isAndroid) { _checkLocationPermissionAndRestartApp(); } } @override void dispose() { // Remove the observer added in initState WidgetsBinding.instance.removeObserver(this); // Call the parent class's dispose method super.dispose(); } This dispose method is part of the State lifecycle in Flutter. It's used to clean up resources when the State object is removed, such as removing observers. In this case, it removes the observer added in the initState method to listen for app lifecycle changes. Future_forceRequestLocationPermission() async { // Check the location permission status final status = await ph.Permission.location.status; // If the permission is denied, request it forcefully if (status.isDenied) { final status = await ph.Permission.location.request(); // If permission is permanently denied, open app settings if (status.isPermanentlyDenied) { ph.openAppSettings(); } else if (status.isGranted) { // If permission is granted, get the current position await gl.Geolocator.getCurrentPosition( desiredAccuracy: gl.LocationAccuracy.high) .then((gl.Position position) { _getAddressFromLatLng(position); }).catchError((e) { debugPrint(e); }); } } }
This dispose method is part of the State lifecycle in Flutter. It's used to clean up resources when the State object is removed, such as removing observers. In this case, it removes the observer added in the initState method to listen for app lifecycle changes.
Future_forceRequestLocationPermission() async { // Check the location permission status final status = await ph.Permission.location.status; // If the permission is denied, request it forcefully if (status.isDenied) { final status = await ph.Permission.location.request(); // If permission is permanently denied, open app settings if (status.isPermanentlyDenied) { ph.openAppSettings(); } else if (status.isGranted) { // If permission is granted, get the current position await gl.Geolocator.getCurrentPosition( desiredAccuracy: gl.LocationAccuracy.high) .then((gl.Position position) { _getAddressFromLatLng(position); }).catchError((e) { debugPrint(e); }); } } }
This method _forceRequestLocationPermission is designed to handle situations where location permission is denied. It forcefully requests location permission and, if granted, proceeds to get the current device position.
Future_handleLocationPermission() async { bool serviceEnabled; gl.LocationPermission permission; // Get shared preferences final SharedPreferences prefs = await SharedPreferences.getInstance(); try { // Check if location service is enabled serviceEnabled = await gl.Geolocator.isLocationServiceEnabled(); // Log the service status logger.e(serviceEnabled); if (!serviceEnabled) { // If location service is not enabled, request permission and show a message permission = await gl.Geolocator.requestPermission(); locationController.getIsLocationServiceEnabled(false); await Fluttertoast.showToast( msg: "You need to enable location services"); await 3.delay(); logger.e("service not enabled"); return false; } // If location service is enabled, update the controller locationController.getIsLocationServiceEnabled(serviceEnabled); // Check location permission permission = await gl.Geolocator.checkPermission(); logger.d(permission); if (permission == gl.LocationPermission.denied) { // If location permission is denied, forcefully request it final permission1 = await gl.Geolocator.requestPermission(); if (permission1 == gl.LocationPermission.denied) { await _forceRequestLocationPermission(); return true; } } else if (permission == gl.LocationPermission.deniedForever) { // If location permission is permanently denied, forcefully request it await _forceRequestLocationPermission(); return true; } // If all checks pass, set a flag in shared preferences and return true prefs.setBool('locationPermissionGranted', true); return true; } catch (e) { // Handle exceptions and set the flag to false in case of an error prefs.setBool('locationPermissionGranted', false); logger.e("$e"); return false; } }
The _handleLocationPermission method is responsible for checking and handling location permissions. It checks whether location services are enabled and requests permission if necessary. It also handles scenarios where permission is denied or permanently denied, providing appropriate feedback and taking necessary actions.
Future_getCurrentPosition() async { logger.d("get current location"); try { // Check and handle location permission final hasPermission = await _handleLocationPermission(); logger.d(hasPermission); if (!hasPermission) return; logger.f("message"); // Get the current device position final position = await gl.Geolocator.getCurrentPosition( desiredAccuracy: gl.LocationAccuracy.lowest); logger.f("message"); await _getAddressFromLatLng(position); } catch (e) { // Handle exceptions related to getting the current position logger.f("message $e"); } }
The _getCurrentPosition method is responsible for getting the current device position. It first checks and handles location permissions using _handleLocationPermission. If permission is granted, it proceeds to get the current position using Geolocator.
Future_getAddressFromLatLng(gl.Position position) async { try { // Get the address from latitude and longitude final placeMarks = await gc.placemarkFromCoordinates( position.latitude, position.longitude); gc.Placemark place = placeMarks[0]; _currentIsoCode = place.isoCountryCode?.toString() ?? ""; await SharedRefrence().saveIsocode(key: "IsoCode", data: _currentIsoCode); // Update loading status in the controller locationController.getIsLoading(false); } catch (e) { // Handle exceptions related to getting address from coordinates locationController.getIsLoading(false); logger.e(e); } }
The _getAddressFromLatLng method takes a Position object (latitude and longitude) and uses Geocoding to get the address information. It extracts the ISO country code and saves it using SharedRefrence. The loading status is updated in the controller.
@override Widget build(BuildContext context) { return const MaterialApp( title: 'Flutter Location Demo', debugShowCheckedModeBanner: false, home: HomePage( latitude: position.latitude, longitude: position.longitude, position: '$position', ), ); } }
3. Get user’s latitude and longitude
Now it's time to add the geolocation functionality. To query the current location of the device, simply make a call to the getCurrentPosition() method. For example:
import 'package:flutter/material.dart'; class HomePage extends StatelessWidget { final double latitude; final double longitude; final String position; const HomePage({Key? key, required this.latitude, required this.longitude, required this.position}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Flutter Location Demo'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Latitude: $latitude', style: TextStyle(fontSize: 18), ), Text( 'Longitude: $longitude', style: TextStyle(fontSize: 18), ), Text( 'Position: $position', style: TextStyle(fontSize: 18), ), ], ), ), ); } }
0 Comments