19

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.

Matt Ke
  • 3,599
  • 12
  • 30
  • 49
Aleem
  • 520
  • 1
  • 4
  • 15

11 Answers11

21

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.

Malek Tubaisaht
  • 1,170
  • 14
  • 16
  • 1
    You need to be careful to use it. A free license is not allowing to use it for commercial purpose – dreambit.io dreambitio Dec 24 '20 at 19:03
  • @EldarMiensutov I'm already using it with my application. and it's free as well for both commercial and free usage. – Malek Tubaisaht Dec 24 '20 at 22:45
  • 3
    Based on their website, you do it against their terms of use ‘Free for non-commercial use’. If it works, it does not mean you allowed to use it. So be careful – dreambit.io dreambitio Dec 25 '20 at 23:12
  • @EldarMiensutov my app is free containing ads – Malek Tubaisaht Dec 26 '20 at 14:10
  • A lot of users using VPN, so this method will give false country. Bout license: just use http call to cloudflare https://cloudflare.com/cdn-cgi/trace. Or call your own api backed by clodflare and read cf-ipcountry request header – Ivan Borshchov Dec 18 '22 at 08:35
  • @IvanBorshchov Borshchov not a lot :) there is nothing can be done with VPN cheat – Malek Tubaisaht Dec 19 '22 at 12:08
  • You are right, I used "a lot" just for highlighting concern, depends on audience of course :) Just according to my project near 8% had different country on IP then on phone number verified with OTP. When it is critical to legal/finance, then 8% might turn in thousands of $ at scale. – Ivan Borshchov Dec 20 '22 at 13:28
  • 1
    @IvanBorshchov there is hack at all, even geolocator can be cheated, I worked with bank sector, they couldn't do anything about vpn cheating. so, it's something that there is no tools that could be used to prevent fake data. – Malek Tubaisaht Dec 22 '22 at 18:04
18

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
}
Rahul sharma
  • 1,492
  • 12
  • 26
  • Jsut three small comments. 1. Don't use `new` any more in Dart. 2. The simpler `location` plugin also works, and (as of this writing) that runs on the web as well. 3. There is really no need for high accuracy for the country, you can ask for the lowest and save battery. – Gábor Sep 21 '20 at 10:07
  • Using the GPS to get the device country is overkill, check out "Cenk YAGMUR"'s answer for a better one. Platform.localeName will give something like en_US, which you can just parse for the iso name. – DarkNeuron Oct 14 '20 at 14:45
  • @DarkNeuron it will give you device language not a country. – Rahul sharma Oct 23 '20 at 06:26
  • @frankenstein You don't see the country in "en_US"? – DarkNeuron Oct 23 '20 at 09:37
  • 4
    @DarkNeuron I'm from india and i use English US in my device. I will get en_US from your method and i will get india from my method. – Rahul sharma Oct 23 '20 at 10:16
  • @frankenstein You're right. I suppose it depends on the use case. – DarkNeuron Oct 23 '20 at 12:34
17

try this

import 'dart:io' show Platform;

String localeName = Platform.localeName;
Cenk YAGMUR
  • 3,154
  • 2
  • 26
  • 42
2

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,

Loïc Fonkam
  • 2,284
  • 11
  • 25
  • I haven't used this package yet, but based on description, I think there are two main issues with this approach. 1. If user is not using any SIM, this approach will not work 2. If user has activated international roaming, this approach will still give wrong result. Correct me if I am wrong. Thanks anyway. – Aleem Jan 14 '20 at 20:00
  • Yes, you are right. The user needs to grant permission and have a SIM installed – Loïc Fonkam Jan 15 '20 at 13:08
1

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 }
Clement Joseph
  • 1,235
  • 2
  • 13
  • 17
0

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()),

            ],
          )
        ),
      ),
    );
  }
}
chunhunghan
  • 51,087
  • 5
  • 102
  • 120
0

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.

ravish.hacker
  • 1,189
  • 14
  • 21
0

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;
  }
Shailendra Rajput
  • 2,131
  • 17
  • 26
-2

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);
});
-3

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-

moshfiqur
  • 2,065
  • 3
  • 24
  • 27
-3
var code = WidgetsBinding.instance.window.locale.countryCode;
vitralyoz
  • 570
  • 7
  • 14