How to get value from Future in flutter - flutter

I'm trying to make a currency converter app in flutter.
The process I've planned was..
At the initState, get current currency data from API
Get currency data and assign it to 'currentCur'
Calculate converted currency value with 'currentCur' and display the value
But, I got an error that since 'currentCur' is Instance of 'Future<dynamic' so it can't calculated cause it is not subtype of 'num'
How can I get just value from Future in initState?
class _ConverterWidgetState extends State<ConverterWidget> {
late final TextEditingController _current;
late final currentCur;
late final currency;
fetchData() async {
try {
http.Response response = await http.get(
Uri.parse(
'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD',
),
);
String jsonData = response.body;
var basePrice = jsonDecode(jsonData)[0]['basePrice'];
devtools.log(basePrice.toString());
return basePrice;
} catch (e) {
devtools.log(e.toString());
}
}
getCurrency(a) async {
return await Future.value(a);
}
#override
void initState() {
super.initState();
_current = TextEditingController(text: 1000.toString());
currentCur = fetchData();
devtools.log(currentCur.toString());
}

Specify the function is going to return a value with the "Future" keyWord
Future<num> fetchData() async {
var basePrice = 0;
try {
http.Response response = await http.get(
Uri.parse(
'https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.KRWUSD',
),
);
String jsonData = response.body;
basePrice = jsonDecode(jsonData)[0]['basePrice'];
devtools.log(basePrice.toString());
} catch (e) {
devtools.log(e.toString());
}
return basePrice;
}
void updateCurrentCur ()async{
var basePrice = await fetchData();
setState(() {
currentCur = basePrice;
});
}
#override
void initState() {
super.initState();
updateCurrentCur()
}

Related

LateInitializationError: Field 'check' has not been initialized

I'm trying to Use data that I fetched from database and i got an error : "LateInitializationError: Field 'check' has not been initialized. "
, i tried to remove the late word and adding " ? " and it gives another error "Expected a value of type 'num', but got one of type 'Null'
"
class _letterssState extends State<letterss> {
late var check;
Future getData() async{
var url = 'http://ip/getSpell.php';
http.Response response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body);
check=data;
print(data.toString());
}
bool searchRes (String s){
int x=0;
for ( var i=0 ; i<check.length;i++ )
{
if (check[i]['letter']==s){
x=i;
}
}
if (check[x]['result']=='true')
{
return true;
}
else
{
return true;
}
}
initState()
{
getData();
}
It will take some frame to get data from getData future method and assigning on check.
It would better to use FutureBuilder for future methods. Follow this doc example
Future<List<yourDataType>?> getData() async {
var url = 'http://ip/getSpell.php';
http.Response response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body);
return data;
}
late final future = getData();
#override
Widget build(BuildContext context) {
return FutureBuilder<List<YourDataType>?>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasData) {
//todo:
}
return CircularProgressIndicator();
},
);
}

Flutter Try correcting the name to one that is defined, or defining the name

I made a function to post some data using http.post, when using the function i pass as parameter the id, now im using this function in other places but it says that it has 1 positional argument but 0 found.
Future<Response?> postLocationId(String id) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/$id/workingTimeframes';
try {
response = await http.post(
Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString(),
"Content-Type": "application/json"
},
);
} catch (er) {}
return response;
}
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
futureDataForStatus = postLocationId(id); <<<< getting error here
}
ElevatedButton(
onPressed: () async {
final WorkingLocationData locationData =
workingData!.firstWhere(
(some) => some.id ==
chooseLocation);
await postLocationId(locationData.id); <<<
The main problem is because you are passed a parameter (id == null) that doesn’t exist before initState(),
If you try and change the id word for a '1' string, this work, but doesnt the better solution.
You need to modify your logic in your initState() because you pass an argument that is null you have a lot of options:
void initState() {
super.initState();
getLocationData():
}
void getLocationData() async {
final locationData = {...} // This return location data information
postLocationId(locationData.id)
}
or You can omit the end line in your initState()
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
// futureDataForStatus = postLocationId(id);
}
Edit your logic inside your onPressed function to this:
final locationData =
workingData!.firstWhere((some) => some.id == chooseLocation);
if (locationData != null) {
await postLocationId(locationData.id);
setState(() {
_flag = !_flag;
});
} else {
print('locationData is null);
}

Change bool in initState flutter

I have a page with this code:
class _HomeScreenState extends State<HomeScreen> {
bool isFirstLoading = true;
#override
void initState() {
super.initState();
if (isFirstLoading) {
getInfo();
setState(() {
isFirstLoading = false;
});
} else {
getInfoFromSharedPref();
}
}
Future<http.Response> getInfo() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
Loader.show(context,
isAppbarOverlay: true,
isBottomBarOverlay: true,
progressIndicator: CircularProgressIndicator());
var url = kLinkAPI + "/getInfo";
var response =
await http.post(url, headers: {"Content-Type": "application/json"});
var resObj = jsonDecode(response.body);
if (response != null) {
setState(() {
if (resObj.length > 0) {
address = resObj[0]['address'];
countryInfo = resObj[0]['country_info'];
phone = resObj[0]['phone'];
latitude = resObj[0]['latitude'];
longitude = resObj[0]['longitude'];
isFirstLoading = false;
prefs.setString('address', address);
prefs.setString('countryInfo', countryInfo);
prefs.setString('phone', phone);
prefs.setString('latitude', latitude);
prefs.setString('longitude', longitude);
}
});
}
Loader.hide();
}
void getInfoFromSharedPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
address = prefs.getString('address');
countryInfo = prefs.getString('countryInfo');
phone = prefs.getString('phone');
latitude = prefs.getString('latitude');
longitude = prefs.getString('longitude');
});
}
}
I would like to make sure that the first time I enter the page, the isFirstLoading variable is set to false and then calls the getInfo function with the http call while if it is false it takes from the shared preferences.
isFirstLoading is now always true
how could I solve?
I think you're overcomplicating your code. Let me know if this solves your issue.:
class _HomeScreenState extends State<HomeScreen> {
SharedPreferences prefs;
#override
void initState() {
super.initState();
getInfo();
}
// ...
}
Now, the first time this widget is inserted into the tree:
initState() will be called once.
Therefore, getInfo() will be called. getInfo() will make the http call and update the prefs variable using setState, which you have already done.
Whenever the widget is reloaded, the prefs variable will not be lost since it is a stateful widget.
Next, if you would like to save the preference settings locally instead of making an http call every time the user opens the app, you should handle that inside of getInfo() itself. Something like this:
getInfo() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getBool("isFirstLoading") == false) {
// setState to update prefs variable
} else {
// make http call
// save prefs (optional)
// setState to update prefs variable
}
}
If I undestand correctly, you are trying to only call the getInfo method on the first load, and the getInfoFromSharedPref all the other time.
My suggestion is to save the isFirstLoading bool as a preference like so:
class _HomeScreenState extends State<HomeScreen> {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isFirstLoading = prefs.getBool("isFirstLoading") ?? true;
#override
void initState() async {
super.initState();
if (isFirstLoading) {
await getInfo();
await prefs.setBool("isFirstLoading", false);
isFirstLoading = false;
} else {
getInfoFromSharedPref();
}
}
Future<http.Response> getInfo() async {
// …
}
void getInfoFromSharedPref() async {
// …
}
}

Flutter initState wait for async function to complete

in my main.dart i have among others those two functions:
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
}
And
#override
void initState() {
super.initState();
_fetchMasterData();
}
What i would like to have is to wait in initState till _fethcMasterData is done bevore Widgert build is called.
Is that possible? Many thanks for any help!
Here how I use an async func in initstate;
builder() async {
favoriteDatabase =
await $FloorFavoriteDatabase.databaseBuilder('favorite_database.db')
.build();
setState(() {
favoriteDao = favoriteDatabase.favoriteDao;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) =>
getNamePreferences().then(updateName));
});
builder();
favoriteDao.findAllMoviesAsStreamW();
favoriteDao.findAllMoviesAsStream();
}
Also you can check this mini article too.
It is not possible to await in initState, so when you finish all loading process then you can call SetState method which populate your widget with actual data.
Second solution could be use of futurebuilder or streambuilder where you want to show data but it is only possible if any methods data is not dependent on each other.
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
SetState((){}); // added line
}

Correct way to call an api by provider in fflutter?

I have been trying to make a app in flutter where an api is called and data is updated in TextField
Used provider for state management, here is the code for it.
class ProfileProvider with ChangeNotifier {
var profileData;
String _url = "http://10.0.2.2:3000/api/v1/user/loggedin_user";
void getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var data = await http.get(
_url,
headers: {
"accept": "application/json",
"content-type": "application/json",
'Token': token,
},
);
var infoOfPerson = json.decode(data.body);
profileData = new ProfileObject(
name: infoOfPerson['name'],
mobile: infoOfPerson['mobile'],
email: infoOfPerson['email'],
role: infoOfPerson['role'],
);
notifyListeners();
}
ProfileObject get profileInfo {
return profileData;
}
}
I am getting the data fine, now i have to show it in the UI, but sometime data is populated, sometime its not. Can someone please point me the right direction why this is happening.
Here is the code for UI.
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final emailController = TextEditingController(text: '');
final nameController = TextEditingController(text: '');
final mobileController = TextEditingController(text: '');
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final profileData = Provider.of<ProfileProvider>(context);
profileData.getData();
if (profileData.profileInfo != null) {
emailController.text = profileData.profileInfo.name;
nameController.text = profileData.profileInfo.email;
mobileController.text = profileData.profileInfo.mobile;
}
_isInit = false;
super.didChangeDependencies();
}
}
#override
Widget build(BuildContext context) {
final profileData = Provider.of<ProfileProvider>(context);
return Scaffold(
drawer: NavigationDrawer(),
body: profileData.profileInfo == null
? Center(
child: CircularProgressIndicator(),
)
: Builder(
builder: (context) => SingleChildScrollView(
child: Padding(.....
Below the padding, there is normal TextField, can someone tell me why the data is being populated sometime and sometime its coming empty, even I wrapped it with CircularProgressIndicator() and a check the notifyListeners(); is not working there. The loader is not being shown and data is not being loaded.
Thanks
for StatelessWidget.
Inside the build method use:
Future.microtask(() async {
context.read<SomeProvider>().fetchSomething();
});
For StatefulWidgets if you want to call it once. Do this inside the initState() or didChangeDependencies (better if the latter). This will be called at the end of the frame which means after the build or rendering finishes..
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<SomeProvider>().fetchSomething();
});
}
EDIT: WidgetsBinding will also work on build. I forgot on why I used microtask lol
i've created a function which called nextTick, i call it in initState and it works for now, but want to see others method
void nextTick(Function callback, [int milliseconds = 0]) {
Future.delayed(Duration(milliseconds: 0)).then((_) {
callback();
});
}
then use it like below
#override
void initState() {
super.initState();
nextTick((){
ProfileProvider profileProvider = Provider.of<ProfileProvider>(context);
profileProvider.getProfile();
});
}
Edit: i store couple of variables to manage them on ui, like isLoading, hasError and errorMessage. Here is my provider class
class ProfileProvider extends ChangeNotifier {
bool _hasError = false;
bool _isLoading = true;
String _errorMsg = '';
Profile _profileResponse;
bool get hasError => _hasError;
bool get isLoading => _isLoading;
String get errorMsg => _errorMsg;
Profile get profileResponse => _profileResponse;
Future<void> getProfile() async {
this.setLoading = true;
this.setError = false;
this.setErrorMsg = '';
try {
await dio.post('$api/p/view', data: {}).then((res) {
print(res.data);
_profileResponse = Profile.fromJson(jsonDecode(res.data));
print(_profileResponse.data);
notifyListeners();
}).whenComplete(() {
this.setLoading = false;
});
} catch (e) {
this.setError = true;
this.setErrorMsg = '$e';
}
this.setLoading = false;
}
set setError(bool val) {
if (val != _hasError) {
_hasError = val;
notifyListeners();
}
}
set setErrorMsg(String val) {
if (val != null && val != '') {
_errorMsg = val;
notifyListeners();
}
}
set setLoading(bool val) {
_isLoading = val;
notifyListeners();
}
}