Flutter - Get user GPS location and use throughout screens globally - flutter

How can I get the user's GPS (current address) on launch and then use that throughout the app where needed? I'd like to be able to use both options of the coordinates and written address being output. I'd like to have it be string data instead of text widget, so that then I can use it in other widgets where appropriate. Here's what I have so far, but I can't figure out how to incorporate this into my app properly and extract and implement that data.
Oh, also how to make it update periodically for when then user changes locations?
Any ideas?
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
class FindMe extends StatefulWidget {
initState() {
FindMe();
}
#override
_FindMeState createState() => _FindMeState();
}
class _FindMeState extends State<FindMe> {
String currentAddress = '';
late Position currentposition;
Future<String> _determinePosition() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
Fluttertoast.showToast(msg: 'Please enable Your Location Service');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
Fluttertoast.showToast(msg: 'Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
Fluttertoast.showToast(msg:'Location permissions are permanently denied, we cannot request permissions.');
}
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.bestForNavigation);
try {
List<Placemark> placemarks = await placemarkFromCoordinates(position.latitude, position.longitude);
Placemark place = placemarks[0];
setState(() {
currentposition = position;
currentAddress = "${place.locality}, ${place.postalCode}, ${place.country}";
});
} catch (e) {
print(e);
}
return currentAddress;
}
#override
Widget build(BuildContext context) {
_determinePosition();
return Text(
currentAddress,
style: const TextStyle(color: Colors.black),
);
}
}

You could use state-management solutions like blocor riverpod to do this.
With Riverpod one solution could look like this:
class LocationService {
Future<String> determinePosition(){...}
}
...
final locationServiceProvider = Provider((ref) => LocationService());
final positionProvider = FutureProvider((ref) => ref.watch(locationServiceProvider).determinePosition);
...
// in a consumer widget
build(BuildContext context, WidgetRef ref){
final position = ref.watch(positionProvider);
// do something with the position
}
You can then access the value in your position provider from different parts of your app.

Related

flutter geolocator package not returning the longitude and latitude values

I am using the flutter geolocator package to get a device's current location. but, it is not returning the location and sends null values instead of longitude and latitude.
I have linked all the codes related to location access for Android and iOS.
Here is my code
loading_screen.dart
import 'package:flutter/material.dart';
import 'package:clima/services/location.dart';
class LoadingScreen extends StatefulWidget {
#override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen> {
#override
void initState() {
super.initState();
print('init state called');
getLocation();
}
double? lat;
double? long;
void getLocation() async {
Location location = Location();
await location.getCurrentLocation();
print(location.longitude);
print(location.latitude);
lat = location.latitude;
long = location.longitude;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('$lat & $long'),
),
);
}
}
location.dart
import 'package:geolocator/geolocator.dart';
class Location {
double? longitude;
double? latitude;
Future<void> getCurrentLocation() async {
try {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.lowest);
longitude = position.longitude;
latitude = position.latitude;
} catch (e) {
print(e);
}
}
}
Here is the console log
Running Gradle task 'assembleDebug'...
√ Built build\app\outputs\flutter-apk\app-debug.apk.
D/FlutterGeolocator( 9601): Attaching Geolocator to activity
D/FlutterGeolocator( 9601): Creating service.
D/FlutterGeolocator( 9601): Binding to location service.
D/FlutterGeolocator( 9601): Geolocator foreground service connected
D/FlutterGeolocator( 9601): Initializing Geolocator services
Debug service listening on ws://127.0.0.1:52220/Dm4z5g8IYT4=/ws
Syncing files to device SM A528B...
I/flutter ( 9601): init state called
I/BufferQueueProducer( 9601): [SurfaceView - com.kanuthakor.clima/com.kanuthakor.clima.MainActivity#e98a93d#0#1(BLAST Consumer)1](id:258100000001,api:1,p:9601,c:9601) queueBuffer: queued for the first time.
D/ViewRootImpl#797d88a[MainActivity]( 9601): Creating frameDrawingCallback nextDrawUseBlastSync=false reportNextDraw=true hasBlurUpdates=false
D/ViewRootImpl#797d88a[MainActivity]( 9601): Creating frameCompleteCallback
I/SurfaceView#e98a93d( 9601): uSP: rtp = Rect(0, 0 - 1080, 2265) rtsw = 1080 rtsh = 2265
I/SurfaceView#e98a93d( 9601): onSSPAndSRT: pl = 0 pt = 0 sx = 1.0 sy = 1.0
I/SurfaceView#e98a93d( 9601): aOrMT: uB = true t = android.view.SurfaceControl$Transaction#10aa451 fN = 1 android.view.SurfaceView.access$500:124 android.view.SurfaceView$SurfaceViewPositionUpdateListener.positionChanged:1728 android.graphics.RenderNode$CompositePositionUpdateListener.positionChanged:319
I/SurfaceView#e98a93d( 9601): aOrMT: vR.mWNT, vR = ViewRootImpl#797d88a[MainActivity]
I/ViewRootImpl#797d88a[MainActivity]( 9601): mWNT: t = android.view.SurfaceControl$Transaction#10aa451 fN = 1 android.view.SurfaceView.applyOrMergeTransaction:1628 android.view.SurfaceView.access$500:124 android.view.SurfaceView$SurfaceViewPositionUpdateListener.positionChanged:1728
I/ViewRootImpl#797d88a[MainActivity]( 9601): mWNT: merge t to BBQ
D/ViewRootImpl#797d88a[MainActivity]( 9601): Received frameDrawingCallback frameNum=1. Creating transactionCompleteCallback=false
I/BufferQueueProducer( 9601): [ViewRootImpl#797d88a[MainActivity]#0(BLAST Consumer)0](id:258100000000,api:1,p:9601,c:9601) queueBuffer: queued for the first time.
D/OpenGLRenderer( 9601): GPIS:: SetUp Pid : 9601 Tid : 9632
D/ViewRootImpl#797d88a[MainActivity]( 9601): Received frameCompleteCallback lastAcquiredFrameNum=1 lastAttemptedDrawFrameNum=1
I/ViewRootImpl#797d88a[MainActivity]( 9601): [DP] pdf(0) 1 android.view.ViewRootImpl.lambda$addFrameCompleteCallbackIfNeeded$3$ViewRootImpl:4987 android.view.ViewRootImpl$$ExternalSyntheticLambda16.run:6 android.os.Handler.handleCallback:938
I/ViewRootImpl#797d88a[MainActivity]( 9601): [DP] rdf()
D/ViewRootImpl#797d88a[MainActivity]( 9601): reportDrawFinished (fn: -1)
I/ViewRootImpl#797d88a[MainActivity]( 9601): MSG_WINDOW_FOCUS_CHANGED 1 1
D/InputMethodManager( 9601): startInputInner - Id : 0
I/InputMethodManager( 9601): startInputInner - mService.startInputOrWindowGainedFocus
D/InputMethodManager( 9601): startInputInner - Id : 0
any help will be highly appreciated, thank you.
The problem is most likely the use of LocationAccuracy.lowest. On Android this translates to the PASSIVE_INTERVAL setting. This means that the location request will not trigger the location services to fetch a location but instead only return location triggered by other clients actively requesting a location update.
You'd most likely want to update the accuracy to LocationAccuracy.low or higher if you want to actively trigger a location update and not rely on other applications to acquire a location for you.
Here is a complete overview of all accuracy options and their Android counterparts:
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
Did you given these permissions?
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
You need to check run time permission and I didn't use your Location class.
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
//import 'package:clima/services/location.dart';
class LoadingScreen extends StatefulWidget {
#override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen> {
#override
void initState() {
super.initState();
print('init state called');
getLocation();
}
double? lat;
double? long;
Future<Position> getLocation() async {
Location location = Location();
bool serviceEnabled;
LocationPermission permission;
print('inside getLoction1');
// 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.
print('Location services are disabled.');
}
print('inside getLoction2');
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
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.
print('Location permissions are denied');
}
print('inside getLoction3');
}
print('inside getLoction4');
if (permission == LocationPermission.deniedForever) {
// Permissions are denied forever, handle appropriately.
print(
'Location permissions are permanently denied, we cannot request permissions.');
}
print('inside getLoction5');
final position = await Geolocator.getCurrentPosition();
print(position.latitude);
print(location.longitude);
print(location.latitude);
print('inside getLoctio6');
lat = location.latitude;
long = location.longitude;
return position;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<Position>(
future: getLocation(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.connectionState == ConnectionState.done) {
print(snapshot.data!.latitude);
return Center(
child: Text(
'${snapshot.data!.latitude}${snapshot.data!.longitude}'),
);
} else {
return SizedBox.shrink();
}
}),
);
}
}
I added an enquiry like yours today, I didn't see yours earlier, but yes, I have found that only null values are being given by geolocator 8.2.1 when position data is requested by Flutter. You might be using a real device, viewing your console response. I have been using the Android Emulator ... I checked, and the location services are enabled, the permission for position data to be noticed is given, and then despite these the position data given is null for long/lat ... I even entered settings to add a location to the emulator, but it made no difference ...
My Code, Dart:
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: 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().locationHereIs();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
If anyone has an idea on what could be improved for position data to be received, please respond to my problem also ... I'm in the same boat as Kanu ... Thank you : )
I have found a solution and a reasonable explanation for why current-position data will not be retrieved by geolocator when operating with
android's emulator
:
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 does not support geolocator's current-position function.
Android emulator does support 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 developers.
I had same issue with geolocator but when I added <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> permission along with ACCESS_COARSE_LOCATION in manifest, and most importantly, I changed the accuracy to LocationAccuracy.high. After this, everything worked perfectly.
Here is my code.
In manifest
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
In dart side.
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);

How to fix Non-nullable instance when trying to get phones location

I'm trying to get the location of my phone with geolocation however the variable that stores location when you a press the button to get location has to be store as null at the beginning as I do not have the location of the phone yet however when I do that I get an error 'Non-nullable instance field 'currentposition' must be initialized' how to I fix this
this is my code
class _HomeState extends State<Home> {
String currentAddress = 'My Address';
Position currentposition;
void initState() {}
Future<Position> _determinePosition() async {
bool serviceEnabled; //check location service is enabled
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
Fluttertoast.showToast(msg: 'Please enable Your Location Service');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
Fluttertoast.showToast(msg: 'Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
Fluttertoast.showToast(
msg:
'Location permissions are permanently denied, we cannot request permissions.');
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
try {
List<Placemark> placemarks =
await placemarkFromCoordinates(position.latitude, position.longitude);
Placemark place = placemarks[0];
setState(() {
currentposition = position;
currentAddress =
"${place.locality}, ${place.postalCode}, ${place.country}";
print(currentposition);
});
} catch (e) {
print(e);
}
throw "";
}
If you declare a variable then you also have to initialize it by passing some value to it. But in any case you want to assign value later then you can define it as late making this change to your variable you can also make it nullable by using ?
late Position currentposition; //late initialize but not null
and nullable with late
late Position? currentposition; //late initialize nullable

Can't get user location after permission granted

I tried get user location when button clicked using flutter location package
Code
FloatingActionButton(
onPressed: () async {
await location
.hasPermission()
.then((PermissionStatus status) async {
if (_permissionGranted == PermissionStatus.denied) {
await location.requestPermission().then(
(PermissionStatus requestStatus) async {
print("PERMISSION TAKEN");
await location
.getLocation()
.then((LocationData userLocation) {
print("LOCATION TAKEN 1");
print(userLocation);
});
},
);
} else {
await location
.getLocation()
.then((LocationData userLocation) {
print("LOCATION TAKEN 2");
print(userLocation);
});
}
});
},
child: Icon(Icons.place, color: Colors.white),
backgroundColor: Colors.green,
),
When user clicked to button requested permission to location and in my code after permission granted work this part of my code
print("PERMISSION TAKEN");
But then not work this part of code
await location
.getLocation()
.then((LocationData userLocation) {
print("LOCATION TAKEN 1");
print(userLocation);
});
you can also get the location by following:---------
pubspec.yaml file under dependencies:----
dependencies:
location: ^3.0.0
Android, add this permission in AndroidManifest.xml :
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
iOS, you have to add this permission in Info.plist :
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
Warning: there is a currently a bug in iOS simulator in which you have to manually select a Location several in order for the Simulator to actually send data. Please keep that in mind when testing in iOS simulator.
main.dart
import 'package:flutter/material.dart';
import 'package:location/location.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter GPS',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: GetLocationPage(),
);
}
}
class GetLocationPage extends StatefulWidget {
#override
_GetLocationPageState createState() => _GetLocationPageState();
}
class _GetLocationPageState extends State<GetLocationPage> {
LocationData _currentLocation;
Location _locationService = new Location();
#override
void initState() {
// TODO: implement initState
super.initState();
_getLocation().then((value) {
setState(() {
_currentLocation = value;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_currentLocation == null
? CircularProgressIndicator()
: Text("Location:" +
_currentLocation.latitude.toString() +
" " +
_currentLocation.longitude.toString()),
],
),
),
);
}
Future<LocationData> _getLocation() async {
LocationData currentLocation;
try {
currentLocation = await _locationService.getLocation();
} catch (e) {
currentLocation = null;
}
return currentLocation;
}
}
ss:---
In order to request location, you should always check manually Location Service status and Permission status. refer this https://pub.dev/packages/location
i get current location as below , try to use like this
Future<LatLng> getUserLocation() async {
LocationData currentLocation;
var location = new Location();
bool _serviceEnabled;
PermissionStatus _permissionGranted;
LocationData _locationData;
_serviceEnabled = await location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await location.requestService();
if (!_serviceEnabled) {
}
}
_permissionGranted = await location.hasPermission();
if (_permissionGranted == PermissionStatus.DENIED) {
_permissionGranted = await location.requestPermission();
if (_permissionGranted != PermissionStatus.GRANTED) {
}
}
// Platform messages may fail, so we use a try/catch PlatformException.
try {
currentLocation = await location.getLocation();
final lat = currentLocation.latitude;
final lng = currentLocation.longitude;
final coordinates = new Coordinates(lat, lng);
var addresses =
await Geocoder.local.findAddressesFromCoordinates(coordinates);
var first = addresses.first;
updateLocation(lat, lng, first.postalCode, first.locality,
first.countryName, first.adminArea, first.addressLine);
final center = LatLng(lat, lng);
return center;
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
showToast("LOCATION PERMISSION DENIED",
gravity: Toast.TOP, duration: Toast.LENGTH_LONG);
}
currentLocation = null;
}
}
And don't forget to add this permission in Info.plist
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription

GPS off message, and continous location update flutter

i made an app which uses the geolocator package to check for GPS and get the location of the user, and i use the provider package to handle the state. The problem is, when the GPS is switched off, a red screen appears that says the latitude was null, i would like to implement a screen to tell the user to switch on the GPS, and update the location accordingly.
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong/latlong.dart';
class MapState with ChangeNotifier {
bool locationServiceActive = true;
MapController _mapController;
MapController get mapController => _mapController;
static var _initialPosition;
var _lastPosition = _initialPosition;
LatLng get initialPosition => _initialPosition;
LatLng get lastPosition => _lastPosition;
MapState(){
checkGPS();
_getUserLocation();
}
checkGPS() async{
bool conn = await Geolocator().isLocationServiceEnabled();
if(conn == false){
locationServiceActive = false;
} else {
locationServiceActive = true;
}
notifyListeners();
}
void _getUserLocation() async{
Position position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
print("/////////////////////////////////////////////////////////////////////////////////position");
print(position);
_initialPosition = LatLng(position.latitude, position.longitude);
notifyListeners();
}
}
update:-
i changed my _getUserLocation function to a stream, which works much better if the user switched on or off the gps (and it uses the last known location if the gps is off)... but it doesn't print the statement in the terminal if the position is null which is weird, only when there is lat and lng!!!
here is the modification i made...
void _getUserLocation() async{
Position position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
var geolocator = Geolocator();
var locationOptions = LocationOptions(accuracy: LocationAccuracy.high, distanceFilter: 10);
StreamSubscription<Position> positionStream = geolocator.getPositionStream(locationOptions).listen(
(Position position) {
print("/////////////////////////////////////////////////////////////////////////// position");
print(position == null ? 'Unknown' : position.latitude.toString() + ', ' + position.longitude.toString());
});
_initialPosition = LatLng(position.latitude, position.longitude);
notifyListeners();
}
If you want to notify the user about the GPS, you can use something like this
#override
void initState() {
super.initState();
initPlatformState();
location.onLocationChanged.listen((result) {
currentLocation = result;
// your code
});
});
}
void initPlatformState() async {
LocationData currentLocation;
try {
currentLocation = await location.getLocation();
error = "";
} 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';
currentLocation = null;
}
setState(() {
currentLocation = currentLocation;
});
}
Here, you are listening to the location stream if enabled. In case it is not then it throws an error. The package being used here is location

Device Country in Flutter

I am trying to get country of device (Android) in Flutter. I used this tutorial, but I think it is the wrong approach for my problem.
Locale myLocale = Localizations.localeOf(context);
print(myLocale.languageCode.toString() + ' ' + myLocale.countryCode.toString());
Based on this, I have couple of questions/issues:
I am always getting en US even though I have set device language to Urdu - Pakistan. So what am I missing?
I want to display certain app items based on the country the device is in, not based on language as people living in Pakistan with language set to English (US) will get items actually intended for USA based users. So, should I use geoLocation and get longitude and latitude and decide based on that data? Or is there any other simpler approach just to get country of user?
Thanking in anticipation.
Add this 2 library in pubspec.yaml
geolocator: ^5.1.1
geocoder: ^0.2.1
Then Add permission for Location access. Check here
At last call this method where you want.
Future<String> getCountryName() async {
Position position = await Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
debugPrint('location: ${position.latitude}');
final coordinates = new Coordinates(position.latitude, position.longitude);
var addresses = await Geocoder.local.findAddressesFromCoordinates(coordinates);
var first = addresses.first;
return first.countryName; // this will return country name
}
if you are looking to get the device country without using "geolocator" so you don't need to get the device permission, you can use the below code:
Future<String> getCountry() async{
Network n = new Network("http://ip-api.com/json");
locationSTR = (await n.getData());
locationx = jsonDecode(locationSTR);
return locationx["country"];
}
The Network class code is below:
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
class Network {
final String url;
Network(this.url);
Future<String> apiRequest(Map jsonMap) async {
HttpClient httpClient = new HttpClient();
HttpClientRequest request = await httpClient.postUrl(Uri.parse(url));
request.headers.set('content-type', 'application/x-www-form-urlencoded');
request.add(utf8.encode(json.encode(jsonMap)));
HttpClientResponse response = await request.close();
// todo - you should check the response.statusCode
String reply = await response.transform(utf8.decoder).join();
httpClient.close();
return reply;
}
Future<String> sendData(Map data) async {
http.Response response = await http.post(url,
headers: {'Content-Type': 'application/json; charset=UTF-8'},
body: jsonEncode(data));
if (response.statusCode == 200)
return (response.body);
else
return 'No Data';
}
Future<String> getData() async {
http.Response response = await http.post(url,
headers: {'Content-Type': 'application/x-www-form-urlencoded'});
if (response.statusCode == 200)
return (response.body);
else
return 'No Data';
}
}
you can get the city and countrycode and internet provider as well.
try this
import 'dart:io' show Platform;
String localeName = Platform.localeName;
Use the flutter_sim_country_code, with this package you can get a user localizations variables and country code directly from the user sim and network provider. For example;
networkCountryIso,
simCountryIso,
these variable always holds the country code,
please use package https://pub.dev/packages/devicelocale
I have tested with real device, it works fine
code snippet
String locale = await Devicelocale.currentLocale;
full code
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:devicelocale/devicelocale.dart';
void main() => runApp(MyApp());
/// Demo getting a device locale
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List _languages = List();
String _locale;
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
List languages;
String currentLocale;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
languages = await Devicelocale.preferredLanguages;
print(languages);
} on PlatformException {
print("Error obtaining preferred languages");
}
try {
currentLocale = await Devicelocale.currentLocale;
print(currentLocale);
} on PlatformException {
print("Error obtaining current locale");
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_languages = languages;
_locale = currentLocale;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
children: <Widget>[
Text("Current locale: "),
Text('$_locale'),
Text("Preferred Languages: "),
Text(_languages.toString()),
],
)
),
),
);
}
}
I had the same problem working on an app with country specific functionality. All these methods mentioned here are good, having gone through them all myself I realised none will cover all possible scenarios.
So, I changed the approach to the problem. Instead of making decision programmatically, which country user is in and going ahead with it. I thought it is better to identify the user's country using one of the methods that suits your app most and then present it to the user. If they wish they can change it, otherwise in most cases auto detection is valid.
For my app I presented users with country when logging in and a flat button at bottom to change it if it is not correct.
This solution requires location service to be enabled.
Add these two dependencies in your pubspec.yaml file
geolocator: ^7.0.3
geocoding: ^2.0.0
Future<String> getCountryCodeName() async {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
List<Placemark> address =
await placemarkFromCoordinates(position.latitude, position.longitude);
Placemark placeMark = address.first;
String country = placeMark.country;
return country; // this will return country }
You have to use a geolocator and geocode plugin. then create this method use below code. In Android Manifest.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Function
Future<String> getCountryName() async {
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
await Geolocator.requestPermission();
}
if (permission == LocationPermission.deniedForever) {
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
}
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
debugPrint('location: ${position.latitude}');
final coordinates = new Coordinates(position.latitude, position.longitude);
var addresses =
await Geocoder.local.findAddressesFromCoordinates(coordinates);
var first = addresses.first;
print(first.countryName);
return first.countryName;
}
Try this. Don't forget to provide necessary permissions in android manifest and ios as well.
import 'package:geolocator/geolocator.dart';
final Geolocator geolocator = Geolocator()..forceAndroidLocationManager;
geolocator
.getCurrentPosition(desiredAccuracy: LocationAccuracy.best)
.then((Position position) {
setState(() {
_currentPosition = position;
});
}).catchError((e) {
print(e);
});
Building on #chunhunghan's answer, you can use the plugin https://pub.dev/packages/devicelocale. To get the country code, you have to use:
Locale l = await Devicelocale.currentAsLocale;
String countryCode = l.countryCode;
For the full code, check: https://pub.dev/packages/devicelocale#-example-tab-
var code = WidgetsBinding.instance.window.locale.countryCode;