Execute a code after await function finishes - flutter

I have this scenario where I have a list of books that is populated with a listview builder via an API. And each list has an add button where a user can add the books into the local db (sqlite). I am trying to check on the tap the add button if the book already exists, the user will be alerted with a pop and won't be able to add.
This is the code where I check if the book exists in the DB or not. The function _checkBookExist gets called on the onPressed in button in the list.
My problem is, till the time SQL query gets executed, the rest of the statements finishes. Any elegant way to address the issue ? I tried using .then() and .whenComplete() but those didn't work either.
_checkBookExist(id, author, title, thumbnail) async {
final List<BookShelf> result = await bookshelfDb.bookExists(id);
// below statements where I check if the book exist
if (result.length == 0 || result[0].volumeid == id) {
final booksToAdd = BookShelf(
author: author,
volumeid: id,
thumbnail: thumbnail,
title: title,
dateAdded: date.toString());
// below statements adds the book into the DB
await bookshelfDb.addItem(booksToAdd).then((value) => {
SnackBarMessage.snackBar(context, "Added to Bookshelf", "Ok", 2000),
});
} else {
print("Book Exist");
}
}

await waits until asynchronous addItem is executed and only after the execution of the rest continues.
await bookshelfDb.addItem(booksToAdd);
SnackBarMessage.snackBar(context, "Added to Bookshelf", "Ok", 2000);

Related

How to restrict the user from creating more than one doc in firestore database?

I have a form on my flutter app where the userr can submit his personal info:
try {
await FirebaseFirestore.instance
.collection('users')
.doc(userID).collection('personalInfo')
.add(_insertProfInfoToDBToDB.toJson());
This function is triggered by a sunmit button, now the problem is that if the user doesnt have an internet connection for example and he submit this form, then he will get an error that there is no internet connection which is coming from another function
Future<void> _tryConnection() async {
try {
final response = await InternetAddress.lookup('Example.com');
setState(() {
_isConnectionSuccessful = response.isNotEmpty;
});
} on SocketException catch (e) {
print(e);
setState(() {
_isConnectionSuccessful = false;
});
}
if (_isConnectionSuccessful != true){
Utils.showSnackBar("Please check your internet connection");
}
}
but this doesnt restric him from pressing the submit button again and again and again...
So what is happening is that once the user gets an internet connection, there will be multiple docs created at the same time inside the 'personalInfo' collection.
My question is how to restrict the user from creating more than one doc inside a collection?
Use setData
Firestore.instance.
collection('users').
document('userID').collection('collection name').document('docId')
set(data, merge: true)
Now this will create a new entry if it doesnt exist and update it if its existing
From the docs
setData(Map<String, dynamic> data, {bool merge: false}) → Future
Writes to the document referred to by this DocumentReference. If the document does not yet exist, it will be created.
.If merge is true, the provided data will be merged into an existing document instead of overwriting.>

How to handle reading from database when API-request hasn't finished yet to save to database in Flutter?

For my App i'm loading the link for the background-image for each screen from my API.
Right after the query that downloads and saves the image-link, i'm querying the link from the database.
The problem now is, that the function isn't waiting for the download and saving to finish although i'm using await, therefor i get an empty result from the database and get an error from the imageloader.
Future<String> downloadAsset(String name) async {
final Map<String, String> _json = {
'mandant': Config.mandant,
'name': name
};
final query = MakePost('get_app_assets', false, _json, saveAsset);
await query.queryAPI(); // Function won't wait for this to finish
String ret = await dbAppAssets.getText(name); // Get link from database
return ret;
}
I've already tried to use .then(), but the same thing happens.
This only happens initially on the first call of each screen, but how is this normally beeing handled?
I'm using riverpod with futureProviders if that matters.
I do not know where you are using the downloadAsset function, but when you use Future on a function you should also await the function where you are using it, for example:
Future<void> _reverseScrollToTopAnimation() async {
await controller!.reverse();
_showBackToTopButton = false; // hide the back-to-top button
}
then, wherever you call it you should also await that function:
await _reverseScrollToTopAnimation();
If not the function will act as a synchronous operation even though you are using await inside the function.

Flutter - stuck on multiple google sheets calls

New to flutter and need help.
I'm using google sheets as a database. Supposed to use multiple sheets each with different sheetId.
I have 2 function to get the data:
getChildSchedule - to get data from one specific sheet
getAllSchedule - to get data from multiple sheets.
In the widget I'm checking if I'm supposed to display data from a particular sheet or from all the sheets and call the appropriate func. When I'm calling getChildSchedule from the widget all works perfectly and it shows the data.
But when I need the getAllSchedule it gets stuck. It doesn't
stop running but seems as if it's in an infinite loop though there is no such loop to get stuck on.
From the prints and the tracking I did, it calls on the getChild with index 0 but never returns from it - though the child data is being printed inside getChild.
What am I doing wrong here?
Future<List<Lesson>> getChildSchedule(int childId) async {
print('in getChild: child $childId: ${ChildrenManager.children[childId].spreadsheetId}');
spreadsheetId = ChildrenManager.children[childId].spreadsheetId;
await init();
final lessons = await _scheduleSheet.values.allRows(fromRow: 2);
print('in getChild: child $childId lessons: $lessons');
return List.generate(
lessons.length,
(index) => Lesson(
weekDay: lessons[index][0],
startTime: double.tryParse(lessons[index][1] ?? ''),
endTime: double.tryParse(lessons[index][2] ?? ''),
grade: ChildrenManager.children[childId].grade,
teacher: lessons[index][3],
header: lessons[index][4],
description: lessons[index][5],
zoomLink: Uri.tryParse(lessons[index][6] ?? ''),
meetingID: lessons[index][7],
meetingCode: lessons[index][8],
supplies: lessons[index][9],
assignment: Uri.tryParse(lessons[index][10] ?? ''),
),
);
}
Future<List<Lesson>> getAllSchedule() async {
List<List<Lesson>> schedules = List<List<Lesson>>();
for (int i = 0; i < ChildrenManager.children.length; i++) {
print('in getAll schedules: $i');
schedules[i] = await getChildSchedule(i);
print('in getAll schedules: got $i child'); //this never gets printed
}
print('in getAll schedules: $schedules');
List<Lesson> schedule = List<Lesson>();
for (List<Lesson> sc in schedules) {
schedule.addAll(sc);
}
schedule.sort((a, b) {
int result = a.startTime.compareTo(b.startTime);
if (result == 0) {
return a.endTime.compareTo(b.endTime);
}
return result;
});
return schedule;
}
I think the issue here was because of repeated calls that changed the sheet id while the previous was still running.
I've moved the getAll to another class and called it with a new manager object(that contains the getChild) for each child and it solved the issue.

Automate validation of result based on auto clicking a button

There is a button on a webpage, If I click that button first time, it takes some time to get the status in progress. once it is in progress then If I click after sometime say 3 minutes then the status is coming as successful.
The problem here is I have to give sleep time between two clicks to verify the status and sometimes because of this sleep time, a status not in sync. For example I click a button and status in progress and if I click after 3 minutes, sometimes status is successful or sometimes it remains in progress which is failing my TC.
isn't there any way that button should be clicked automatically until status comes in progress and then I can verify the status. same for successful status as well.
I am using sleep between two clicks
browser.sleep(25000)
button.click();
expect(inprogress_class.getText()).toContain('in progress');
browser.sleep(100000) // waiting for a defined time to click a buttonassuming that status will be successful
button.click();
expect(successful_class.getText()).toContain('successful);
Expected: Button keeps on getting clicked until status changes
Actual: giving sleep time between click and waiting for status to change
Try the below option
To click for the first time and wait for InProgress State
await button.click();
await browser.wait(ExpectedConditions.textToBePresentInElement(await inprogress_class), 25000, 'The wait for inProgress state');
To click the button until we get the success try the below one
while (await successful_class.getText() !== 'successful') {
await button.click();
await browser.sleep(10000); // update the time with how often the button needs to be clicked
}
expect(await successful_class.getText()).toContain('successful);
Hope it helps you
If you can use async/await then it would be more easier otherwise you will have to use Promises chaining.
try with async/await approach :
await browser.wait(async () => {
await button.click();
var buttonText = await inprogress_class.getText();
return buttonText === 'in progress';
}, 180000);
expect(inprogress_class.getText()).toContain('in progress');
await browser.wait(async () => {
await button.click();
var buttonText = await successful_class.getText();
return buttonText === 'successful';
}, 180000);
expect(successful_class.getText()).toContain('successful);

Can I create follow-up actions on Actions on Google?

I know that I can deep link into my Google Home application by adding to my actions.json.
I also know that I can parse raw string values from the app.StandardIntents.TEXT intent that's provided by default, which I am currently doing like so:
if(app.getRawInput() === 'make payment') {
app.ask('Enter payment information: ');
}
else if(app.getRawInput() === 'quit') {
app.tell('Goodbye!');
}
But does Actions on Google provide direct support for creating follow-up intents, possibly after certain user voice inputs?
An example of a conversation flow is:
OK Google, talk to my app.
Welcome to my app, I can order your most recent purchase or your saved favorite. Which would you prefer?
Recent purchase.
Should I use your preferred address and method of payment?
Yes.
OK, I've placed your order.
My previous answer won't work after testing.
Here is a tested version.
exports.conversationComponent = functions.https.onRequest((req, res) => {
const app = new ApiAiApp({request: req, response: res});
console.log('Request headers: ' + JSON.stringify(req.headers));
console.log('Request body: ' + JSON.stringify(req.body));
const registerCallback = (app, funcName)=>{
if (!app.callbackMap.get(funcName)){
console.error(`Function ${funcName} required to be in app.callbackMap before calling registerCallback`);
return;
}
app.setContext("callback_followup", 1, {funcName});
}
const deRegisterCallback = (app)=>{
const context = app.getContext("callback_followup");
const funcName = app.getContextArgument("callback_followup", "funcName").value;
const func = app.callbackMap.get(funcName);
app.setContext("callback_followup", 0);
return func;
}
app.callbackMap = new Map();
app.callbackMap.set('endSurvey', (app)=>{
if (app.getUserConfirmation()) {
app.tell('Stopped, bye!');
}
else {
app.tell('Lets continue.');
}
});
app.callbackMap.set('confirmationStartSurvey', (app)=>{
const context = app.getContext("callback_follwup");
if (app.getUserConfirmation()) {
registerCallback(app, 'endSurvey');
app.askForConfirmation('Great! I\'m glad you want to do it!, do you want to stop?');
} else {
app.tell('That\'s okay. Let\'s not do it now.');
}
});
// Welcome
function welcome (app) {
registerCallback(app, 'confirmationStartSurvey');
const prompt = "You have one survey in your task list, do you want to proceed now?";
app.askForConfirmation(prompt);
}
function confirmationCalbackFollowup (app) {
const context = app.getContext("callback_followup");
if (! context){
console.error("ERROR: confirmationCallbackFollowup should always has context named callback_followup. ");
return;
}
const callback = deRegisterCallback(app);
return callback(app);
}
const actionMap = new Map();
actionMap.set(WELCOME, welcome);
actionMap.set('confirmation.callback.followup', confirmationCalbackFollowup );
app.handleRequest(actionMap);
});
The previous solution won't work because app is generated everytime the action function is called. I tried to save a callback function into app.data but it won't be existing next intent coming. So I changed another way. Register the callback function to app.callbackMap inside the function. so it will be there anyway.
To make it work, one important thing is Api.Ai need to have context defined in the intent. See the Api.Ai Intent here.
Make sure you have event, context, and action of course. otherwise, this intent won't be triggered.
Please let me know if you can use this solution. sorry for my previous wrong solution.
thanks
Can you give an example of a conversation flow that has what you are trying to do?
If you can use API.AI, they have Follow Up intents in the docs.
I do not think your code
if(app.getRawInput() === 'make payment') {
app.ask('Enter payment information: ');
}
else if(app.getRawInput() === 'quit') {
app.tell('Goodbye!');
}
is a good idea. I would suggest you have two different intent to handle "Payment information" and "Quit".