Device Country in Flutter - 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;

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

Flutter - Get user GPS location and use throughout screens globally

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.

Flutter Geolocator Location doesn't obtain for real phones

I used geolocator flutter package for my little app and it works perfectly with emulator, but when I try to use in my real smart phone, it doesnt work. Do you have any idea?
void getLocationData() async {
WeatherModel weatherModel = WeatherModel();
var weatherData = await weatherModel.getLocationWeather();
//print(weatherData);
var zipCode = await weatherModel.getLocationZipCode();
zipCode = zipCode.toString().substring(0, 2);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LocationScreen(
locationWeather: weatherData,
zipCode: zipCode,
);
}));
}
Future<dynamic> getLocationWeather() async {
Location location = Location();
await location.getCurrentLocation();
NetworkHelper networkHelper = NetworkHelper(
'$openWeatherMapURL?lat=${location.latitude}&lon=${location.longitude}&appid=$apiKey&units=metric');
var weatherData = await networkHelper.getData();
return weatherData;
}
Angela Yu's student? her projects need one update for optimizing and fixing little errors.
I got problems with Geolocator, it was not being used correctly, and this site helped me a lot. Then I made my own Location class with Provider and... life becomes good again.
Try use geolocator. I seems you are not using geolocator package
final Geolocator geolocator = Geolocator()..forceAndroidLocationManager;
// return true if location service is enable, false if not
bool locationServiceEnable = await geolocator.isLocationServiceEnabled();
// position instace
Position position;
// if location service enable get current position
if (locationServiceEnable) {
// await current position
position = await geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.best);
// if location service is not enable get last known position
} else {
// await last known position
position = await geolocator.getLastKnownPosition(
desiredAccuracy: LocationAccuracy.best);
}
if (position != null) {
// fetch weather data with position data
}
You should add
<uses-permission android:name="android.permission.INTERNET" />
to the AndroidManifest.xml. It helps to use the internet to fetch location data in an Android device, because your app still need to get data from URL openweathermap.
using geolocator: ^7.0.1
and adding
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
to AndroidManifest.xml file
void getLocation() async {
print('getLocation');
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
print(position);
}
and using in onPressed
onPressed: () {
getLocation();
},
solves the problem.

I want to execute a function when the Flutter app starts

I want to send an ID to the server and receive json when the app is launched.
Flow
1.Start my app (Show splash screen)
2.Json request to server
3.If there is data, display page1. If not, display page2
it seems you my need to get a bit more learning about Flutter, my sugest is to start with this one only 10 euros will give you base from where will be easier to learn the rest, that said, to get a databse i'm using this code:
//lib/services/networking_service.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class NetworkHelper {
final String json;
final url = 'HERE YOU CAN PUT YOUR API URL';
NetworkHelper(this.json);
Map<String, String> headers = {
"Content-type": "application/x-www-form-urlencoded"
};
Future getData(BuildContext context) async {
http.Response response = await http.post(url, body: json, headers: headers);
if (response.statusCode == 200) {
Map<String, dynamic> decodedResp = jsonDecode(response.body);
print(decodedResp);
return decodedResp;
} else {
print(response.statusCode);
return null;
}
}
}
You can call it from your main like this:
static getCategories(BuildContext context) async {
String json =
'q={"f":"listCategories","Store_id":"$storeId","LANG":"$lang","UID":"$uid"}';
//THIS json VARIABLE IS WHERE YOU NEED TO PUT YOUR API CALL LÓGIC TO GET THAT ID, I LEAVE THIS FOR YOUR BETTER UNDERSTANDING
NetworkHelper networkHelper = NetworkHelper(json);
var decodedResp = await networkHelper.getData(context);
final CategoriesModel respData = CategoriesModel.fromJson(decodedResp);
print(respData);
//HERE YOU MAY RETURN O STORE IN PROVIDER YOUR RESPONSE AND SEND THE USER TO THE PAGE YOU CONSIDER
}
If you need more help I'm happy to help, but consider taking the course o learn a bit more, it will be lots more easy and enjoyable after.
use SchedulerBinding it runs when page is opened and widgets are build.
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
// your code after page opens,splash keeps open until work is done
});
}
#override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),// you can do your stuff here when splash screen run
() => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => LoginScreen())));}
and please put this code into the spalsh screen

How do you call one future and then pass that return value into another future to run?

I am trying to take a picture with my Android camera, upload that picture to Google Firebase Storage, get the downloadable URL of that image on Storage, and update the user's photo feed on Firestore. If I only call takeImage() it takes the image and uploads successfully to storage. If I call _uploadImage with a dummy image url, it correctly updates the feed. But I cannot get the result of takeImage to pass as a parameter to _uploadImage().
void takeAndSave() async {
url = await takeImage();
_uploadImage(url);
}
Future<String> takeImage() async {
// open camera
var image = await ImagePicker.pickImage(source: ImageSource.camera);
// save image to temp storage
final String fileName = "${Random().nextInt(10000)}.jpg";
Directory directory = await getApplicationDocumentsDirectory(); // AppData folder path
String appDocPath = directory.path;
// copy image to path
File savedImage = await image.copy('$appDocPath/' + fileName);
// upload file to Firebase Storage
final StorageReference ref = FirebaseStorage.instance.ref().child(fileName);
final StorageUploadTask task = ref.putFile(savedImage);
String downloadURL = await ref.getDownloadURL();
url = downloadURL;
// _image = image;
return downloadURL;
}
Future<void> _uploadImage(String url) async {
final FirebaseUser user = await widget.auth.currentUser();
String uid = user.uid;
print('uid = ' + uid);
print(url);
// upload URL to Firebase Firestore Cloud Storage
Firestore.instance.runTransaction((Transaction transaction) async {
DocumentReference _newPhoto = Firestore.instance.collection('users').document(user.uid);
await _newPhoto.collection('cards').add({"url" : url});
});
}
To chain future tasks: means if we have two future tasks and second is dependent upon result of first response then we can use "Future.wait()". In the below example i have created two methods with async keyword that will fetch data from server and i want to execute "fetchPostAgain()" method after the response of first "fetchPost()" then i can use "Future.wait()".
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_app/models/Post.dart';
import 'package:http/http.dart' as http;
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
}
FetchFirstPost getFirstPost;
String myString = "Loading...";
void _takeImage() {
Future.wait([fetchPost()]).then((FutureOr) => {
fetchPostAgain()
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Center(
child: Column(
children: <Widget>[
SingleChildScrollView(
child: Text(myString),
),
RaisedButton(
child: Text("Run Future"),
onPressed: _takeImage,
),
],
),
/*child: CallApiDemo(),*/
),
),
);
}
Future<Post> fetchPost() async {
final Completer completer = Completer();
final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
log('data: ' + response .statusCode.toString());
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
setState(() {
myString = response.body;
});
return postFromJson(response.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
Future<Post> fetchPostAgain() async{
final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
log('GOT SECOND RESPONSE');
log('data: ' + response .statusCode.toString());
if (response.statusCode == 200) {
setState(() {
myString = myString + "\n\n\nAGAIN\n\n\n" + response.body;
});
return postFromJson(response.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
}
As per your code, it should work properly, but there could be a chance that your takeImage() method is returning an exception. Try catching that exception and see if it helps.
Below is referenced from https://www.dartlang.org/tutorials/language/futures#async-await
If a Future-returning function completes with an error, you probably want to capture that error. Async functions can handle errors using try-catch:
Future<String> takeImage() async {
try {
// Your code
} catch (e) {
// Handle error...
}
}