I am using the flutter Map and I want to get the device location using the geolocator package but it is not working.
This works perfectly well on Windows(desktop) and Chrome/Edge web. However, it won't work on Android Emulator.
I already followed the instructions on this thread but to no avail. Specifically, I have configured a position using the emulator settings and have clicked the "Set Location" button.
I also have setup a route in the Android emulator settings and chose the repeat the playback.
For my android manifest, I have tried using just the ACCESS_COARSE_LOCATION
and just the ACCESS_FINE_LOCATION
but still to no avail. I also tried using the two at once but it still didn't work.
Geolocator version:
PS: I followed the instructions on the package's official page. PSS: When I run the app on the emulator, I didn't even get the pop-up asking for permission to access the location.
Below are my codes:
compileSdkVersion 33
Gradle.properties
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
Android Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.spot_the_bird">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:label="spot_the_bird"
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>
Location Cubit:
import 'dart:developer';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:geolocator/geolocator.dart';
part 'location_state.dart';
class LocationCubit extends Cubit<LocationState> {
LocationCubit() : super(const LocationInitial());
Future<void> getLocation() async {
LocationPermission locationPermission = await Geolocator.checkPermission();
if (locationPermission != LocationPermission.denied ||
locationPermission != LocationPermission.deniedForever) {
emit(const LocationLoading());
try {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
log("$position");
emit(LocationLoaded(
longitude: position.longitude, latitude: position.latitude));
} catch (error) {
emit(const LocationError(
message: 'Error occured while fetching location'));
}
} else {
Geolocator.requestPermission();
}
}
}
Location State:
part of 'location_cubit.dart';
abstract class LocationState extends Equatable {
const LocationState();
@override
List<Object?> get props => [];
}
class LocationInitial extends LocationState {
const LocationInitial();
}
class LocationLoading extends LocationState {
const LocationLoading();
}
class LocationLoaded extends LocationState {
const LocationLoaded({
required this.longitude,
required this.latitude,
});
final double longitude;
final double latitude;
@override
List<Object?> get props => [latitude, longitude];
}
class LocationError extends LocationState {
final String message;
const LocationError({
required this.message,
});
}
map Screen
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:spot_the_bird/bloc/location_cubit.dart';
class MapScreen extends StatelessWidget {
MapScreen({super.key});
final MapController _mapController = MapController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: BlocListener<LocationCubit, LocationState>(
listener: (prevState, currState) {
if (currState is LocationError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(currState.message),
duration: const Duration(seconds: 3),
backgroundColor: Theme.of(context).errorColor,
),
);
}
if (currState is LocationLoaded) {
_mapController.onReady.then((value) => _mapController.move(
LatLng(currState.latitude, currState.longitude), 15));
}
},
child: FlutterMap(
mapController: _mapController,
options: MapOptions(
center: LatLng(4.8472226, 6.974604),
zoom: 15,
minZoom: 4,
),
layers: [
TileLayerOptions(
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
subdomains: ['a', 'b', 'c'],
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<LocationCubit>().getLocation();
},
child: const Icon(Icons.navigation_outlined),
),
);
}
}
Dependencies in pubspec.yaml
flutter:
sdk: flutter
flutter_map: ^2.2.0
latlong2: ^0.8.1
geolocator: ^9.0.2
flutter_bloc: ^8.1.1
equatable: ^2.0.5
bloc: ^8.1.0
cupertino_icons: ^1.0.2
The issue was with my location.cubit. Weirdly, When I checked for the location.denied
and location.deniedPermanantly
separately it worked.
Below is my new location.cubit code:
Future<void> getLocation() async {
// bool serviceEnabled;
LocationPermission permission;
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
Get.snackbar('', 'Location Permission Denied');
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
} else {
emit(const LocationLoading());
try {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
log("$position");
emit(LocationLoaded(
longitude: position.longitude, latitude: position.latitude));
} catch (error) {
emit(const LocationError(
message: 'Error occured while fetching location'));
}
}
}
PS: By the way, credit to @ibrahim Eltayfe, adding the <uses-permission android:name="android.permission.INTERNET"/>
made me see the issue in debug console. Thanks man.