With Flutter/Dart How should I query data that doesn't build a widget? - flutter

I'm working on an app and I'm relatively new to this scene, but I'm running into some troubles when I'm looking to simply query some data.
I'm calling a function after someone logs in. The login function only sends the necessary information and I want to do a secondary ping to my server to get a little more user information, more than just verifying the login info.
getUser (userid) async{
List<Map<String, dynamic>> user=[] ;
var client = new http.Client();
try {
var req = await client.post(urlPath+'mobileGetUser', body: {'thisUserID': userid'});
var jsonResponse = convert.jsonDecode(req.body);
//print('Here: '+jsonResponse.toString());
var id = jsonResponse['id'] ?? '';
var joinDate = jsonResponse['joinDate'] ?? '';
var userEmail = jsonResponse['userEmail'] ?? '';
var displayName = jsonResponse['displayName'] ?? '';
var timezone = jsonResponse['timezone'] ?? '';
var verified = jsonResponse['verified'] ?? '';
user = [{'id': id}];
user = [{'joinDate': joinDate}];
user = [{'userEmail': userEmail}];
user = [{'displayName': displayName}];
user = [{'timezone': timezone}];
user = [{'verified': verified}];
return user
} finally {
client.close();
}
}
I'm used to working in PHP. In that language, I'd pass the JSON object or associative array back to the calling function and accessing the individual fields would be as simple as
$displayName = $user['displayName'];
But this isn't PHP. List types are a little strange to me still.
From my calling function, I try to test this with:
thisUser = getUser(userid);
print('Successful Login: Display name: '+thisUser.toString());
And I get a message of:
Successful Login: Display name: Instance of 'Future<dynamic>'
How can I access this data? I've tried a few ways to get it. Also, is there a better way to create my List? I'm definitely going through a few steps that feel unnecessary. I'm honestly like to just pass it the entire JSONresponse. I'm just a bit out of my depth.

You could do this:
thisUser = await getUser(userid);
print('after'); // this will print after getUser() finishes
If you don't await for getUser() to finish, then you'll receive a Future.
Another way would be to use that Future and add a listener to it, like this:
getUser(userid).then((value) {
print('after'); // this will print after getUser() finishes
thisUser = value;
});
print('before'); // this will print before getUser() finishes
This is assuming you aren't using this on widgets. Otherwise you could use FutureBuilder to detect when the Future finishes and show something.

Related

What is the proper way to run fetch calls which use reactive components from a store?

I am getting two reactive variables I need from a store to use for my fetch calls. I need these fetch calls to rerun when the data in these store values change. I am able to make this work however when I reload the page it causes my app to crash because there are no values that are getting from the store. I am able to make it work if I disable ssr on the +page.js file.
I also believe it is relevant to mention that I am using a relative URL (/api) to make the fetch call because I have a proxy server to bypass CORS
What is the proper way to get this data by rerunning the fetch calls using a reactive component from a store without disabling ssr? Or is this the best/only solution?
+page.svelte
<script>
import { dateStore, shiftStore } from '../../../lib/store';
$: shift = $shiftStore
$: date = $dateStore
/**
* #type {any[]}
*/
export let comments = []
/**
* #type {any[]}
*/
let areas = []
//console.log(date)
async function getComments() {
const response = await fetch(`/api/${date.toISOString().split('T')[0]}/${shift}/1`)
comments = await response.json()
console.log(comments)
}
async function getAreas() {
const response = await fetch(`/api/api/TurnReportArea/1/${date.toISOString().split('T')[0]}/${shift}`)
areas = await response.json()
console.log(areas)
}
// both of these call function if date or shift value changes
$: date && shift && getAreas()
$: date , shift , getComments()
</script>
I tried to use the +page.js file for my fetch calls, however I cannot use the reactive values in the store in the +page.js file. Below the date variable is set as a 'Writble(Date)' When I try to add the $ in front of the value let dare = $dateStore, I get the error 'Cannot find name '$dateSrote'' If i put the $ in the fetch call I get the error 'Cannot find $date'. Even if I were able to make this work, I do not understand how my page would know to rerender if these fetch calls were ran so I do not think this is the solution. As I mentioned, the only solution I have found is to disable ssr on the +page.js, which I do not think is the best way to fix this issue.
import { dateStore, shiftStore } from "../../../lib/store"
export const load = async ({ }) => {
let shift = shiftStore
let date = dateStore
const getComments = async() => {
const commentRes = await fetch(`/api/${date.toISOString().split('T')[0]}/${shift}/1`)
const comments = await commentRes.json()
console.log(comments)
}
const getAreas = async () => {
const areasRes = await fetch(`/api/api/TurnReportArea/1/${date.toISOString().split('T')[0]}/${shift}`)
const areas = await areasRes.json()
console.log(areas)
}
return {
comments: getComments(),
areas: getAreas()
}
}

Firestore transaction futures are confusing me

I've been trying to figure out transactions all morning and I'm stuck. I keep going around in circles, and I know what I need to do(I think), but I'm not sure how to do it.
Here's my code:
Future<String> obtainUniqueUsername(
String requestedUsername,
String currentUserID,
) async {
var userID = currentUserID;
var userName = requestedUsername.toLowerCase();
try {
// Create a reference to a usernames collection .doc(id)
final userRef = FirebaseFirestore.instance.doc('usernames/$userName');
// Start a transaction to check if the username is assigned
// if assigned a .doc(userName) will exist.
FirebaseFirestore.instance.runTransaction((transaction) async {
var userSnapshot = await transaction.get(userRef);
if (userSnapshot.exists) {
// Username is assigned as .doc(userName) exists
return 'Username is already assigned';
} else {
// Assign username by created .doc(userName)
// insert document reference to user
transaction.set(userRef, {'uid': userID});
}
}).then((value) => 'Username assigned', onError: (e) => 'Transaction error ${e.toString()}');
} // endtry
catch (e) {
return e.toString();
} // endcatch
return 'Transaction has failed, please try again later';
} // end
My problem is that it keeps hitting the final return statement, even if it has created the document. My understanding is that the transaction will keep trying until it is successful and returns a value, or it times out and throws an error. I've read that using .then doesn't await a value, and the function continues uninterrupted until it hits the end, but shouldn't it be either a value or an error?
I feel like I'm missing the point somewhere, sorry if this is super basic, I've really been trying to get it to work.
I got it sorted out! It took forever though, lots of trial and error. I think my issue was that I didn't understand how to return the value from the future using the dart shorthand from the examples I was following.
My understanding now is that I'm awaiting usernameAssigned to complete, and the future is completed when I return 'success'.
If the transaction didn't go through, it would throw an exception, and be caught and I would get a message about what went wrong. I think the assignment of transactionStatus isn't required, but I kind of like having it there for my own understanding of what's happening. Maybe when I get more experience I wont need stuff like that.
I also didn't really know how to use a try/catch block, but I think I've improved how I do that as well. Now, I throw an exception if something weird happens instead of going right to return. I think that's better?
Also, the comment about letting firestore assign the unique uid: Yeah, you are right, but I want to have firestore enforce unique human readable usernames. The autogenerated uid is used for everything else.
The usecase is having a usernames collection built like: .doc(uniqueUsername).data({"uid:" uid})
Firestore won't let duplicate doc ID's happen in that collection, so it gives me what I want. My understanding is this is one of the "easier" ways to enforce unique usernames.
Anyways, thanks for the comments and please send me any feedback you may have!
Future<String> obtainUniqueUsername(
String requestedUsername,
String currentUserID,
) async {
Future<String> obtainUniqueMarkerUsername(
String requestedUsername,
String currentUserID,
) async {
var userID = currentUserID;
var userName = requestedUsername.toLowerCase();
var transactionStatus = '';
try {
// Start a transaction to check if the username is assigned
// if assigned a .doc(userName) will exist.
var usernameAssigned = await FirebaseFirestore.instance
.runTransaction((transaction) async {
// Create a reference to a usernames collection .doc(id)
var userRef = FirebaseFirestore.instance.doc('usernames/$userName');
var userSnapshot = await transaction.get(userRef);
if (userSnapshot.exists == true) {
// Username is assigned as .doc(userName) exists
throw Exception('Username already exists');
} else {
// Assign username by created .doc(userName)
// insert document reference to user
transaction.set(userRef, {"uid": userID});
return 'Success';
}
});
transactionStatus = usernameAssigned;
} // endtry
catch (e) {
return e.toString();
} // endcatch
userID = '';
userName = '';
return transactionStatus;
} // end

Variable value update scope in dart

String name = "Jack";
String aName = "John";
if (!newChapter) {
example....
} else {
http.get(Uri.parse("https://api.quran.com/api/v4/chapters/" + currentChapter)).then((result) {
var results = json.decode(result.body);
name = results["chapter"]["name_simple"];
aName = results["chapter"]["name_arabic"];
print(name); // updated value
});
print(name); // default value: Jack
}
print(name); // default value: Jack
Why does the value isn't updated outside of the block?
Is there a way to approach this?
The reason why is not updated is that in the else statement it's written to make an API call and update the name but before this is complete the next line of code is executed since we are not awaiting it to complete. To fix this you can add an await before the api call
await http.get(Uri.parse("https://api.quran.com/api/v4/chapters/" + currentChapter)).then((result) {
var results = json.decode(result.body);
name = results["chapter"]["name_simple"];
aName = results["chapter"]["name_arabic"];
print(name); // updated value
});
It is because the inner print(name) is printed after the http request is successfully made i.e the then part of the http.get promise
Whereas , the outer print(name) is not waiting for the http request to complete
To overcome this you can use one of the following methods
To overcome this you can use either of 3 methods:
callback function
promise
async/await
The async/await is simple and preferred to be used , hence use the following code:
await http.get(Uri.parse("https://api.quran.com/api/v4/chapters/" + currentChapter)).then((result) {
var results = json.decode(result.body);
name = results["chapter"]["name_simple"];
aName = results["chapter"]["name_arabic"];
print(name); // updated value
});

How display data which comes from async function?

I use api for get information which need to be display
Future <String> Get_Amount_Jackpot() async {
// SERVER LOGIN API URL
var url2 = 'https://www.easytrafic.fr/game_app/get_jackpot_lotto.php';
// Starting Web API Call.
var response2 = await http.get(url2,headers: {'content-type': 'application/json','accept': 'application/json','authorization': globals.token});
// Getting Server response into variable.
Map<String, dynamic> jsondata2 = json.decode(response2.body);
return jsondata2["value"];
}
I call it here :
void initState() {
ListLotto = Grille_display();
jackpot = Get_Amount_Jackpot();
super.initState();;
}
How i can display the value "jackpot" which is a simple number on mobile screen. Note i use futurebuilder for another api request, this is why it is complicated. Normally i use futurebuilder for display async data but there i need 2 différents api request so 2 futureBuilder ??? Is it possible and how do that ?
You can use then to get the returned value from the function.
Get_Amount_Jackpot().then((value){
//value is what is returned from the function
jackpot = value;
});
I'm not sure if you can use uppercase letter as the start of a function name, but i copied your function name for my answer. Hope this helps.

ASP.NET Core, where is FindByID?

I have an ASP.NET Core 2.0 application and I'm trying to attach a user to a model:
var user = _userManager.FindByIdAsync(Model.Author);
var promotion = new Promotion()
{
Title = Model.Title,
User = user //error here,
Created = DateTime.Now
};
The problem with this code is that I can't assign user to promotion.User as user is the result of an async operation. I'd prefer not to use FindByIdAsync but for some reason I can't find FindById.
UserManager contains only async API and FindByIdAsync actually returns Task<User> instead of User. So you need to make your code async also and use FindByIdAsync like this:
var user = await _userManager.FindByIdAsync(Model.Author); // will return the User
Only if it is not possible leave your code synchronous, e.g. by calling Result property of the Task which will cause your thread to block until the result is available
var user = _userManager.FindByIdAsync(Model.Author).Result;