-
-
Notifications
You must be signed in to change notification settings - Fork 661
Breaking changes in 7.0.0
Version 7.0.0 of the geolocator contains four important (and breaking) changes. This page will discuss these changes in detail.
For users that migrated to null-safety and there shouldn't be any big surprises. A few take aways:
- The
getLastKnownPosition
returns aFuture<Position?>
. This is because if the last known position isn't available Android and iOS will both return anull
value; - The
timeLimit
parameter of thegetCurrentPosition
method accepts anull
value, meaning the method will not timeout while waiting for a position to be resolved; - The
intervalDuration
andtimeLimit
parameters of thegetPositionStream
method accept anull
value, which means respectively that the geolocator will not take any interval filter into account and will not timeout when waiting for a position to be resolved.
All other return values and parameters are guaranteed not to be null and should be treated as such.
Before version 7.0.0 the geolocator used (or misused) the Android shouldShowRequestPermissionRationale
to help determine if a user indicated not to ask permissions again. Pre API 30 the user could do so by checking the "Don't ask me again" checkbox, from API 30 and higher Android automatically infers this the second time a user denies permissions. The shouldShowRequestPermissionRationale
method can be used to determine if, after the user initially denied permissions, the App should show an explanatory UI to the user explaining why the App needs location permissions (more details can be found here).
Unfortunately the shouldShowRequestPermissionRationale
method returns false
when the App request permission for the very first time but also once the user has indicated permissions should not be asked again. And only returns true
once the user explicitly denied permissions when requesting permission but didn't indicate the App should not ask again (remember on API 30 Android infers this automatically the second time the user denied permissions).
So the logic that the geolocator applied was as follows:
- When the user denies permission the geolocator checks the result of the
shouldShowRequestPermissionRationale
and acts as follows:- If
true
is returned than the geolocator will returnLocationPermission.denied
, meaning permission is denied but the App can ask again; - If
false
is returned than the geolocator will returnLocationPermission.deniedForever
, meaning permission is denied and the App should not (and can not) ask again. If the App does request permissions another time it will immediately returnLocationPermission.deniedForever
;
- If
- The above result is also stored as a variable in the shared preferences. This way when checking for permissions (using the
checkPermission
method) the geolocator is able to determine if the permission had been denied forever in earlier runs of the application:- If the last time the App requested permission the permission was denied forever but the
shouldShowRequestPermissionRationale
returnstrue
, the geolocator returnsLocationPermission.denied
and the App is allowed to request permissions again; - If the last time the App requested permission the permission was denied forever and the
shouldShowRequestPermissionRationale
returnsfalse
, the geolocator returnsLocationPermission.deniedForever
and the developer knows the App cannot request permissions and should redirect the user to the settings;
- If the last time the App requested permission the permission was denied forever but the
Unfortunately with the introduction of the "Ask every time" permission in Android API 30, storing the permission results in a bug. To describe the situation in which the bug occurs assume that the App requested permissions twice and both times they were denied. The geolocator now stores that permissions are denied forever in the shared preferences. Next the user reconsiders and decides that for once the App can have access to the location services of the device and changes permissions in the App settings to "Ask every time". Android will reset all earlier permissions and assumes permissions have never been requested before. So when the App starts and permissions are checked, Android will return that permissions are denied as Android always denies permissions implicitly until the user explicitly grants permissions. This is where the bug shows up, since the geolocator detects the value stored earlier in the shared preferences and determines that since Android reports permissions are denied and the value in the shared preferences indicates that last time permissions were denied forever, permissions should still be denied forever (even though the user indicated in the Android settings that permissions can be requested again).
After careful consideration we decided that the geolocator should not be storing the permission result in the shared preferences anymore. This means that the checkPermission
method will no longer be able to detect if permissions are denied forever. From version 7.0.0 and onwards the checkPermission
will return one of the following permissions (note that this effects all API levels):
-
LocationPermission.denied
: indicates that permissions to access the location services have been denied; -
LocationPermission.whenInUse
: indicates that permissions are granted while the App is in use (this result is returned when the user selects "Ask every time" or "Only when in use"); -
LocationPermission.always
: indicates that permissions are granted even if the App is running in the background (note that the geolocator doesn't support running in the background but the permissions do).
When requesting permissions we can still detect if permissions have been simply denied or if they have been denied forever. This means that the requestPermission
method will still report if permissions are denied forever. There fore we suggest users of the geolocator plugin to use the following flow:
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Location services are not enabled don't continue
// accessing the position and request users of the
// App to enable the location services.
return;
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return;
}
if (permission == LocationPermission.denied) {
// Permissions are denied, next time you could try
// requesting permissions again (this is also where
// Android's shouldShowRequestPermissionRationale
// returned true. According to Android guidelines
// your App should show an explanatory UI now.
return;
}
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
_position = await Geolocator.getCurrentPosition();
Since version 6.1.0 the int timeInterval
parameter of the Geolocator.getPositionStream
method has been deprecated on favour of the Duration intervalDuration
parameter. In version 7.0.0 the timeInterval
parameter will be completely removed.
With release 7.0.0 we removed the deprecated global methods (deprecated in 6.1.0) from the code base. A complete overview of the method to be removed and its replacement method is listed below.
Removed method | Replacement method |
---|---|
checkPermission() | Geolocator.checkPermission() |
requestPermission() | Geolocator.requestPermission() |
isLocationServiceEnabled() | Geolocator.isLocationServiceEnabled() |
getLastKnownPosition({ bool forceAndroidLocationManager = false }) | Geolocator.getLastKnownPosition({ bool forceAndroidLocationManager = false }) |
getCurrentPosition({ LocationAccuracy desiredAccuracy = LocationAccuracy.best, bool forceAndroidLocationManager = false, Duration timeLimit }) | Geolocator.getCurrentPosition({ LocationAccuracy desiredAccuracy = LocationAccuracy.best, bool forceAndroidLocationManager = false, Duration? timeLimit }) |
getPositionStream({ LocationAccuracy desiredAccuracy = LocationAccuracy.best, int distanceFilter = 0, bool forceAndroidLocationManager = false, int timeInterval = 0, Duration timeLimit }) | Geolocator.getPositionStream({ LocationAccuracy desiredAccuracy = LocationAccuracy.best, int distanceFilter = 0, bool forceAndroidLocationManager = false, Duration? intervalDuration, Duration? timeLimit }) |
openAppSettings() | Geolocator.openAppSettings() |
openLocationSettings() | Geolocator.openLocationSettings() |
distanceBetween(double startLatitude, double startLongitude, double endLatitude, double endLongitude) | Geolocator.distanceBetween(double startLatitude, double startLongitude, double endLatitude, double endLongitude) |
bearingBetween(double startLatitude, double startLongitude, double endLatitude, double endLongitude) | Geolocator.bearingBetween(double startLatitude, double startLongitude, double endLatitude, double endLongitude) |