Reputation: 355
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 : )
Upvotes: 1
Views: 2722
Reputation: 1866
In addition to @MauritsvanBeusekom 's answer people who are using Genymotion emulators should try to turn on the GPS and change the default values of latitude/longitude or even just rotate the navigation compass.
In my case although the GPS was on due to some technical glitch I was unable to retrieve the location with accuracy best and just by altering the latitude and longitude, I solved my issue.
Upvotes: 0
Reputation: 31
geolocator 8.2.1 I do not know but why the API 28 Pie emulator works, but example the "Set Location" button - for me does not work on emulator API version 29 30 31 --> loop D/FlutterGeolocator(26734): Attaching Geolocator to activity D/FlutterGeolocator(26808): Attaching Geolocator to activity. I will work in the API emulator 28 Pie. maybe this will help someone.
Upvotes: 0
Reputation: 5989
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.
Upvotes: 3
Reputation: 355
I have found a solution and a reasonable explanation for why current-position data will not be retrieved by geolocator when operating with
:
The reason is because the android emulator is not a real device and lacks the level of functionality found within real android devices; android emulator cannot accept geolocator's current-position function.
Android emulator does accept geolocator's lastKnownLocation function though, and a location set within the emulator's location settings will be noticed and confirmed by geolocator via its lastKnownLocation function.
I trust that this finding helps everyone working with geolocator relying on the android emulator : )
Dart code example:
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 lastKnownPosition() async {
await locationServicesStatus();
await checkLocationPermissions();
try {
Position? position = await Geolocator.getLastKnownPosition();
print(position);
} catch (e) {
print(e);
}
}
void locationHereIs() async {
await locationServicesStatus();
await checkLocationPermissions();
try {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low)
.timeout(Duration(seconds: 28));
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().lastKnownPosition();
super.initState();
}
@override
Widget build(BuildContext context) {
return Container();
}
}
Console output with lastKnownLocation:
✓ Built build/app/outputs/flutter-apk/app-debug.apk.
Installing build/app/outputs/flutter-apk/app.apk...
Debug service listening on ws://127.0.0.1:52586/aA04hHZ7dIg=/ws
Syncing files to device sdk gphone64 x86 64...
I/flutter (11353): Currently, the emulator's Location Services Status = true.
D/CompatibilityChangeReporter(11353): Compat change id reported: 78294732; UID 10149; state: ENABLED
I/flutter (11353): Current Location Permission Status = LocationPermission.whileInUse.
I/flutter (11353): Latitude: 37.333333333333336, Longitude: -121.89206891099967
Conclusion: there has been a suspicion that the geolocator package is flawed without explanation. The above shows and explains that geolocator works fine with android emulator and should remain a favourite of android developers.
Upvotes: 0
Reputation: 212
Use this code for fix it :
Future<Position> _getGeoLocationPosition() async {
bool serviceEnabled;
LocationPermission permission;
// Test if location services are enabled.
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
await Geolocator.openLocationSettings();
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
print('Location permissions are denied');
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
print('Location permissions are permanently denied, we cannot request permissions.');
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
// When we reach here, permissions are granted and we can
// continue accessing the position of the device.
return await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
}
Result :
Position position = await _getGeoLocationPosition();
Upvotes: 0