This is my first StackOverflow question and I am learning Dart and Flutter as a newbie.
I have been introduced to the geolocator package and I have been using the latest version which is version 8.2.1. I am also working with Android Studio and its Android Emulator.
The challenge has been to gain latitude and longitude values, and these would come from position data acquired by geolocator.
After some tests, the results have been that the location services are enabled, permission for the location data to be noticed is granted, but despite this, the position data is not being retrieved by geolocator.
I spent some time in the emulator's settings and managed to set a location in San Jose, California, which I hoped that geolocator would then find and work with, but doing so made no difference.
The console's response which seems important is "Future Not Completed":
✓ Built build/app/outputs/flutter-apk/app-debug.apk. Installing build/app/outputs/flutter-apk/app.apk... D/FlutterGeolocator( 6027): Attaching Geolocator to activity D/FlutterGeolocator( 6027): Creating service. D/FlutterGeolocator( 6027): Binding to location service. D/FlutterGeolocator( 6027): Geolocator foreground service connected D/FlutterGeolocator( 6027): Initializing Geolocator services Debug service listening on ws://127.0.0.1:64162/f6U62iu6OXc=/ws Syncing files to device sdk gphone64 x86 64... I/flutter ( 6027): Currently, the emulator's Location Services Status = true. D/CompatibilityChangeReporter( 6027): Compat change id reported: 78294732; UID 10149; state: ENABLED I/flutter ( 6027): Current Location Permission Status = LocationPermission.whileInUse. I/flutter ( 6027): TimeoutException after 0:00:05.000000: Future not completed
My Code:
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
void main() {
runApp(ScreenView());
}
class ScreenView extends StatefulWidget {
double? latitude;
double? longitude;
ScreenView({this.latitude, this.longitude});
void locationHereIs() async {
await locationServicesStatus();
await checkLocationPermissions();
try {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low)
.timeout(Duration(seconds: 5));
print(position);
} catch (e) {
print(e);
}
}
Future<void> checkLocationPermissions() async {
LocationPermission permission = await Geolocator.requestPermission();
print('Current Location Permission Status = $permission.');
}
void checkLocationSettings() async {
await Geolocator.openLocationSettings();
}
Future<void> locationServicesStatus() async {
bool isLocationServiceEnabled = await Geolocator.isLocationServiceEnabled();
print(
'Currently, the emulator\'s Location Services Status = $isLocationServiceEnabled.');
}
@override
State<ScreenView> createState() => _ScreenViewState();
}
class _ScreenViewState extends State<ScreenView> {
@override
void initState() {
ScreenView().locationHereIs();
super.initState();
}
@override
Widget build(BuildContext context) {
return Container();
}
}
If you can help me understand what's going wrong so that I can fix things and get results, that'd be perfect. In your response, please assume that I have used complileSdkVersion 32, miniSdkVersion 23 and targetSdkVersion 32.
With thanks : )
The problem here is related to the specified desired accuracy. In case of mathems32 it is set to LocationAccuracy.low
. On Android this translates to the PRIORITY_LOW_POWER setting which means Android will only use the cellular network to triangulate the device's location. This will work on real devices (as long as they have a cellular connection) but doesn't on the emulator unfortunately.
There is no direct relationship to the getLastKnownPosition
. The getLastKnowPosition
maps to the getLastLocation
on the Android FusedLocationProviderClient
API which according to the documentation:
Returns the most recent historical location currently available. Will return null if no historical location is available. The historical location may be of an arbitrary age, so clients should check how old the location is to see if it suits their purposes.
The getCurrentPosition
will always try to return the actual location (or null
if the actual location cannot be determined), while the getLastKnownPosition
will (very quickly) return the last known position which is cached by the device (or null
if no location is cached). The later might be very old and not representative at all, therefore you should always check its timestamp and determine if the position is recent enough to use for your needs.
If you'd like to test the location on the emulator set the desired accuracy to LocationAccuracy.high
or higher. Below is a table showing the translation between the accuracies used by the Geolocator and how they map to the Android priorities:
Geolocator | Android |
---|---|
LocationAccuracy.lowest | PRIORITY_PASSIVE |
LocationAccuracy.low | PRIORITY_LOW_POWER |
LocationAccuracy.medium | PRIORITY_BALANCED_POWER_ACCURACY |
LocationAccuracy.high | PRIORITY_HIGH_ACCURACY |
LocationAccuracy.best | PRIORITY_HIGH_ACCURACY |
LocationAccuracy.bestForNavigation | PRIORITY_HIGH_ACCURACY |
UPDATE (see also issue 1082 on GitHub):
The conclusion that the Android emulator doesn't support the getCurrentPosition
method is incorrect. I have run the code you attached, chancing the desiredAccuracy
too LocationAccuracy.high
, on the Android emulator (Android Pixel 5 - API 30) and it correctly reports the configured location. Here is the output:
W/GooglePlayServicesUtil( 2212): Google Play Store is missing.
I/flutter ( 2212): Currently, the emulator's Location Services Status = true.
I/flutter ( 2212): Current Location Permission Status = LocationPermission.whileInUse.
W/GooglePlayServicesUtil( 2212): Google Play Store is missing.
I/flutter ( 2212): Latitude: 52.56731333333333, Longitude: 5.641091666666666
Here is how I used it:
I did notice however that sometimes the Android emulator doesn't want to play nice and I'd have to set the location a few times before the emulator picks it up. I have experienced this only with a fresh emulator, once I have the initial location it keeps on working just fine. What also might help is to setup a route in the Android emulator settings and repeat the playback.
Also make sure the android/app/src/main/AndroidManifest.xml
contains the correct permissions (ACCESS_COARSE_LOCATION
and ACCESS_FINE_LOCATION
) as shown in this sample snippet:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.issue_1082">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:label="issue_1082"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
P.S. for what it is worth, I am the author and maintainer of the Geolocator package.