IOS Simulator Location Access Problem - Flutter - ios

Future<String> getCurrentLocation() async {
late Placemark place;
try {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low);
latitude = position.latitude;
longitude = position.longitude;
print("Geolocator worked");
List<Placemark> placemarks =
await placemarkFromCoordinates(latitude, longitude);
place = placemarks[0];
print("Location to city worked");
print("Place: ${place.locality}");
} catch (e) {
print(e);
}
return place.locality.toString();
}
Here is my code to get current location and the city name of the current lat-log. However, when I started the code, I can't see any permission screen on my simulator. Simulator version is iOS 15.2.
And here is the error code from try-catch block:
flutter: User denied permissions to access the device's location.
However, like I said no permission access screen shows up on my simulator.
Also:
<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>
I already added these to Info.plist.

Add permission_handler package to your pubspec.yaml, and ask for permission before trying to use Geolocator. There are different possibilities, check the following code:
import 'package:permission_handler/permission_handler.dart';
Future<bool> isLocationAvailable() async {
// get current permission status of location
var permissionStatus = await Permission.locationWhenInUse.status;
// if restricted, it can't be used
if (permissionStatus == PermissionStatus.restricted) {
return Future.value(false);
}
// it can be switched off, so it can't be used
if (!await Permission.locationWhenInUse.serviceStatus.isEnabled) {
return Future.value(false);
}
// if user permanently denied access, it can't be used
if (permissionStatus == PermissionStatus.permanentlyDenied) {
return Future.value(false);
}
// get permission and return result
return await Permission.locationWhenInUse.request().isGranted;
}
And call this before you try to get location:
if (await isLocationAvailable()) {
// get location here
}

Related

How to resolve the location permission error for Apps on Huawei device?

Huawei HMS core on my huawei phone do not have defualt permission, I am coding my app using HMS location kit and always got a permission error for Location kit. I followed their development guide to set up the location permission in the Manifest file.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
And followed their code samples:
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
Log.i(TAG, "sdk < 28 Q");
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
String[] strings =
{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
ActivityCompat.requestPermissions(this, strings, 1);
}
} else {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
|| ActivityCompat.checkSelfPermission(this,
"android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) {
String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"};
ActivityCompat.requestPermissions(this, strings, 2);
}
}
Any advice?
The HMS permission must always be allowed. Otherwise, an error will be reported.
Ensure that the location permission has been assigned to HMS Core (APK). To do so, go to Settings > Apps > Apps and find HMS Core. (The menu path may vary depending on the operating system version. If HMS Core is not found, tap the menu icon in the upper right corner of Apps and tap Show system processes.) Then, tap the HMS Core (APK) icon, go to App info > Permissions > Location, and verify that the location permission is assigned to HMS Core. On a device running EMUI 10.0 or later, Location must be set to Always for HMS Core.
Ensure that the Location Info switch in the drop-down notification bar is turned on.
Simulate Location Requires Impersonation Permission. Otherwise, an error permission error will be reported.
Check whether Location Permission is enabled for your app.
Shirley's answer covers the device side of HMS location permission. To cover this type of scenario for all users with lower EMUI versions that HMS Core does not have location permissions by default, you can use the API "settingsClient.checkLocationSettings(…)" to obtain the device location permission. After that, even the location permission for the HMS Core App was disabled, your app will be able to prompt users to enable related permissions with one-click.
Please refer to HMS Location guide
Step 1: Obtain the service API of SettingsClient.
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
Step 2: Call checkLocationSettings() to check the device settings.
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
mLocationRequest = new LocationRequest();
builder.addLocationRequest(mLocationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
//check Location Settings
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
#Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
//Have permissions, send requests
fusedLocationProviderClient
.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
//Interface call successfully processed
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(Exception e) {
//Settings do not meet targeting criteria
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
try {
ResolvableApiException rae = (ResolvableApiException) e;
//Calling startResolutionForResult can pop up a window to prompt the user to open the corresponding permissions
rae.startResolutionForResult(MainActivity.this, 0);
} catch (IntentSender.SendIntentException sie) {
//…
}
break;
}
}
});

Camera permission keeps being denied on iOS

This code is requesting camera permission to initialize camera to use it later, it works fine on Android, but on iOS the permission keeps being denied on its own without showing permission request dialogue box
void _initCamera() async {
if (await PermissionsService().hasCameraPermission()) {
setState(() {
isCameraPermissionGranted = true;
});
print('camera permission granted');
} else {
bool isGranted = await PermissionsService().requestCameraPermission(
onPermissionDenied: () {
AppUtil.showAlertDialog(
context: context,
heading: 'info',
message:
'You must grant this camera access to be able to use this feature.',
firstBtnText: 'Give Permission',
firstFunc: () async {
Navigator.of(context).pop(false);
await _initCamera();
},
secondBtnText: 'Leave',
secondFunc: () async {
print('camera permission denied');
Navigator.of(context).pop();
Navigator.of(context).pop();
},
);
});
setState(() {
isCameraPermissionGranted = isGranted;
});
return;
}
if (isCameraPermissionGranted) {
//initialization code
}
any idea why this behavior happens?
Add this line in info.plist file in Runner folder of iOS this will give camera access permission to iOS app
<key>NSCameraUsageDescription</key>
<string>This app requires access to the camera.</string>

Flutter: Location package Not working on First Time App Install

My current app uses the Location package (link) to obtain the user's current latitude and longitude to be used to find nearby facilities.
This is the code I am using (similar to the example in the documentation)
Map<String, double> _currentLocation;
Map<String, double> _startLocation;
StreamSubscription<Map<String, double>> _locationSubscription;
String error;
bool _permission = false;
Location _location = new Location();
// Platform messages are asynchronous, so we initialize in an async method.
initPlatformState() async {
Map<String, double> location;
try {
_permission = await _location.hasPermission();
location = await _location.getLocation();
error = null;
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
error = 'Permission denied';
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {
error = 'Permission denied - please ask the user to enable it from the app settings';
}
location = null;
}
setState(() {
_startLocation = location;
print("Starting coordinates: ${_startLocation["latitude"]}, ${_startLocation["longitude"]}");
});
}
#override
void initState() {
super.initState();
initPlatformState();
_locationSubscription =
_location.onLocationChanged().listen((Map<String,double> result) {
setState(() {
_currentLocation = result;
print("Current coordinates: ${_currentLocation["latitude"]}, ${_currentLocation["longitude"]}");
});
});
}
The only problem I am facing is that whenever there is a fresh install of a new apk of the app, the app does not find the location after location permission has been granted.
After location has been granted I have set up a print statement to print out the user's location but for some reason it is not printing anything the first time only. After I restart the app then it prints out the location just fine.
First Time Opening After Install
After Restarting the App
Any experts that use the Location package that could help me with this problem?
According to plugin’s source code when you invoke getLocation method it asks ActivityCompat.requestPermissions to get required permission and then process. According to docs from Google:
This method functions asynchronously. It returns right away, and after the user responds to the prompt, the system calls the app's callback method with the results
, but flutter plugin has an issue about location callbacks for Android 6+ and as a workaround it is recommended to aim SDK 21.
So it seems that “native” part of this plugin doesn’t play well with Android 6+. There are two workarounds:
Set SDK to 21 version for your Android project, but I would definitely not recommend doing that.
Create some sort of “hello screen”, which will introduce the app and handle permissions there.
Meanwhile, I am really interested in what is wrong with the plugin cause its implementation seems good, so in case I’ll find how to fix it I’ll get back here.

Xamarin Forms does not wait for GeoLocation approval on iOS

I am have Xamarin Forms cross platform application for iOS, Android and UWP. I use the Xam.Plugin.Geolocator to get the location from each of the devices. My challenge with iOS is on the first launch of the app on a device. My code runs through and detects that IsGeolocationEnabled for the Plugin.Geolocator.Abstractions.IGeolocator object is false before the use is ever presented with the option to allow the application to use the device's location. This causes my app to inform the user that Location Services are not enabled for the application.
Basically I am hitting the line of code below before the use is ever asked about location services:
if (!App.gobj_RealGeoCoordinator.IsGeolocationEnabled)
ls_ErrorMessage = resourcestrings.GetValue("NoLocationServicesMessage");
On the other platforms, UWP at least, it seems that the app is paused while waiting for the user to respond to the request to use location services. Android just seems to automatically allow access to location if an app uses it.
Any idea how I can have the iOS detect if the request to use location services has been answered or not on the first run? Any suggestions would be greatly appreciated.
UPDATE(1):
I have all the correct items in my info.plist as seen below. I do eventually get the request to use the location just after my app has already checked IsGeolocationEnabled and decided the user has not enabled location services for the app.
UPDATE (2):
So I made a little progress using the following code.
try
{
while (!App.gobj_RealGeoCoordinator.IsGeolocationEnabled)
{
await Task.Delay(1000);
}
ViewModelObjects.AppSettings.CanAccessLocation = App.gobj_RealGeoCoordinator.IsGeolocationEnabled;
}
catch (Exception ex)
{
XXXXXXX
}
The challenge is that the plugin appears to provide me no way of knowing in the user has not responded to the location services dialog (i.e. IsGeolocationEnabled == false) versus the user said no to the location services dialog (also IsGeolocationEnabled == false). Any suggestions?
The way this type of permission request occurs on iOS is through an asynchronous dialog prompt, which is only shown if needed (and not until it is needed). Basically, you need to set up a callback from the CLLocation API. I have a helper class that I use for this purpose, which makes it even easier. Just call GetCurrentDeviceLocation() and pass it a callback function. The callback will only be invoked once the user has granted permission to the app, or if they previously granted permission:
public class GeoLocationService
{
readonly CLLocationManager _locationManager;
WeakReference<Action<Position>> _callback;
public GeoLocationService()
{
_locationManager = new CLLocationManager ();
_locationManager.AuthorizationChanged += AuthorizationChanged;
}
void AuthorizationChanged (object sender, CLAuthorizationChangedEventArgs e)
{
Action<Position> callback;
if (_callback == null || !_callback.TryGetTarget (out callback)) {
return;
}
if (IsAuthorized(e.Status)) {
var loc = _locationManager.Location;
var pos = new Position(loc.Coordinate.Latitude, loc.Coordinate.Longitude);
callback (pos);
}
}
static bool IsAuthorized(CLAuthorizationStatus status)
{
return
status == CLAuthorizationStatus.Authorized
|| status == CLAuthorizationStatus.AuthorizedAlways
|| status == CLAuthorizationStatus.AuthorizedWhenInUse;
}
public void GetCurrentDeviceLocation (Action<Position> callback)
{
_callback = new WeakReference<Action<Position>> (callback);
if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
if (_locationManager.Location == null) {
_locationManager.RequestWhenInUseAuthorization ();
return;
}
}
AuthorizationChanged (null, new CLAuthorizationChangedEventArgs (CLAuthorizationStatus.Authorized));
}
}

Determining if geolocation enabled with react native

Building an app with RN I use the following to receive user's location :
navigator.geolocation.getCurrentPosition(
(position) => {
//do stuff with location
},
(error) => {
//error or locaiton not allowed
},
{enableHighAccuracy: true, timeout: 20000, maximumAge: 1000}
);
Which immediately invokes the inquiry for user to accept geolocation permissions. What I'm trying to achieve now is to have an explicit screen before prompting user to click on a button to accept those permissions. In order to get there, I need some way to check if he has accepted those permissions before or not. Caching it in storage would be a bad solution - it might go out of sync if user changes permissions in settings.
How can I check if user has accepted geolocation permissions without triggering the permission alert?
You can use this library https://github.com/yonahforst/react-native-permissions to check if user has accepted location permission.
Like this:
Permissions.getPermissionStatus('location')
.then(response => {
this.setState({ locationPermission: response })
})
In v2.x.x of react-native-permissions (https://github.com/react-native-community/react-native-permissions)
import { Platform } from "react-native";
import { PERMISSIONS, request } from "react-native-permissions";
import * as Geolocation from "#react-native-community/geolocation";
try {
request(
Platform.select({
android: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
ios: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
})
).then(res => {
if (res == "granted") {
Geolocation.getCurrentPosition( // do location staffs);
Geolocation.watchPosition( // do watch location staffs );
} else {
// console.log("Location is not enabled");
}
});
} catch (error) {
console.log("location set error:", error);
}
Check this library, this would help you to check location is enabled or not.
https://www.npmjs.com/package/react-native-device-info#isLocationEnabled
Note : Above will not check permission enabled on not. It will just check location truned on or not

Resources