passing a function as a parameter to a regular class in flutter - flutter

I am trying to pass a function to a regular class (not a widget class) in flutter and inside that class, I have a dialog box. I want to call the dialog box and when the user presses a button the function is passed as a parameter that should trigger.
This is my regular class code
import 'package:finsec/core/res/strings.dart';
import 'package:flutter/material.dart';
import '../../../../core/res/text_sizes.dart';
import '../../data/repositories/app_database.dart';
class ShowDialog {
final void Function() onPressCallback;
BuildContext context;
ShowDialog (this.onPressCallback, this.context) ;
Future<String> showMyDialog() async {
return showDialog<String>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Income Data'),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text(
'Do you want to apply these changes for future income transactions?',
style: TextStyle(
fontSize: text_size_18,
),
),
Text(
'\nPress NO if changes are only for this week income. Press Yes to apply changes to future weeks.',
style: TextStyle(
fontSize: text_size_18,
),
),
],
),
),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context, successful);
},
child: const Text(cancelButton),
),
TextButton(
onPressed: () {
onPressCallback();
Navigator.pop(context, successful);
},
child: const Text(noButton),
),
TextButton(
onPressed: () {
onPressCallback();
Navigator.pop(context, successful);
},
child: const Text(yesButton),
),
],
);
},
);
}
}
I am calling ShowDialog class like this in my widget class. Below is the function call
CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () async {
await ShowDialog(
await database.deleteIncomeData(transaction),
context,
);
},
),
My code is working but not as expected. When I pass my function as a parameter, the database.deleteIncomeData(transaction) executes immediately and my showMyDialog() function in ShowDialog class doesn't get called. When I pass a function as a parameter to ShowDialog class, I don't want the function to execute immediately. I want my dialog box function to be called and show a dialog box. When the user presses a button on the dialog box, then the function parameter should execute.
Can someone help me how to modify my code to accomplish what I described above? Thanks in advance!

I recommend learning more about Future and async/await.
Be careful, because your class ShowDialog is waiting for two parameters, one is a Function and the other is the context.
There are some errors in this line
await ShowDialog(
await database.deleteIncomeData(transaction),
context,
);
First, you don't need to await because here you are calling the constructor. You have to call the showMyDialog method.
await ShowDialog(
await database.deleteIncomeData(transaction),
context,
).showMyDialog();
So, in that case, you will have something to await. The result of showMyDialog.
And the second one is that you are not passing a Function to the constructor, you are passing the value that await database.deleteIncomeData(transaction) returns.
(Unless the value that returns is a Function, in that case, it is ok)
And to fix that you have to pass a Function that calls database.deleteIncomeData(transaction)
await ShowDialog(
() => await database.deleteIncomeData(transaction),
context,
).showMyDialog();

Related

Returning data from .pop() to use it anywhere

I have written a code that return bool type variables. Whether you like the movie. If you like the movie then it returns true, if you don't like the movie then it returns false. But since the .pop() method works in ElevatedButton, I cannot reach it from another class. How can I reach the value?
ElevatedButton(
child: Text(
"Go to new page"
),
onPressed: () async {
final answer = await Navigator.of(context).push<bool>(
MaterialPageRoute(
builder: (context) {
return VideoScreen("Did you like the video?");
},
)
);
},
),
However, I cannot say like:
ElevatedButton(
child: Text(
"Go to new page"
),
onPressed: () async {
final answer = await Navigator.of(context).push<bool>(
MaterialPageRoute(
builder: (context) {
return VideoScreen("Did you like the video?");
},
)
);
},
),
Text(answer)
],
);
So how can I reach that value? Callback or something? Thanks in advance
Use can pass value to parent route while pop like
Navigator.of(context).pop(YourValue);
While you push make sure to await.
final result = await Navigator.of(context)....;
print(result);

How to pop out double alert message

I am new to Flutter. I made my first pop out confirmation alert dialog (Figure 1) but I want to pop another alert dialog window after.
What I am trying to achieve, it's the following: after I click Yes (Figure 2) the app would lead me to my homescreen and pop out another alert dialog.
You could create a method for the second Alert to show up, and call it when you click "YES" on the first one.
void showSecond(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text("Thank you for paying with us"),
content: Icon(Icons.check_circle_outline),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Okay'),
),
],
),
);
}
and your onPressed() of "YES" in the first alert should look something like:
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const SuccessPay()));
showSecond(context);
},
It was a bit hard to replicate your code from an image, so if something it's not accurate let me now. For the next time, post your code in a code block instead of a picture :)
you can call showAlertDialog to show second popup.(you can create new method to show second popup as content is different)This line can be added after
Navigator.of(context).pop() of first popup
You can use the .then() method. Call it if the user pressed the "YES" button.
Add value when poping your dialog like this Navigator.of(dialogCtx).pop(true);
showDialog(
context: context,
builder: (dialogCtx) => AlertDialog(
// title:
// content:
),
actions: [
TextButton(
onPressed: () {
Navigator.of(dialogCtx).pop(false);
},
child: const Text('CANCEL'),
),
TextButton(
onPressed: () {
Navigator.of(dialogCtx).pop(true);
},
child: const Text('YES'),
),
],
),
).then(
(value) => {
if (value == true)
{
// display the next dialog with the scaffolds context
},
},
);

How to wait for value before calling method Flutter/Dart

I am having a pretty hard time with certain actions in flutter. I currently have a method in an outside class that updates a db that my widget relies on for displaying info. I am correctly updating the values in the db and updating the UI correctly. BUT I am having a hard time getting an input first, THEN having that method function. I have tried having it all in the same body and no dice, I have tried to have the addStock method show the input and does not work. The only thing that has been a ban-aid has been to use Navigator.push to the screen again or using a time delayed. Both have produced undesired consequences. I have also tried having the addStock method inside the displayAmountToADD on pressing okay and does not update UI.
//a button inside the UI
onPressed: () async {
displayAmountToAdd(context, index);
setState(() {});
},
....
Future<void> displayAmountToAdd(
BuildContext context,
int index,
) async {
final _textFieldController = TextEditingController();
double materialKG = 0;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Enter amount to add'),
content: Row(
children: <Widget>[
Expanded(
child: TextField(
onChanged: (materialQuanity) {
materialKG = double.parse(materialQuanity);
},
controller: _textFieldController,
decoration: InputDecoration(hintText: "KG"),
),
),
],
),
actions: <Widget>[
TextButton(
child: Text('OK'),
onPressed: () {
materialKG = double.parse(_textFieldController.text);
addStock(context, mapM[index]['quanity'], mapM[index]['name'],
materialKG);
Navigator.pop(context);
},
),
TextButton(
child: Text("Cancel"),
onPressed: () {
Navigator.pop(context);
})
],
);
},
);
//return Future.delayed(Duration(seconds: 4),()=>materialKG); //TRYING TO AVOID THIS
}
//outside the ui file
addStock(
BuildContext context,
double currentQuanity,
String name,
double amountToAdd
) async {
//final db = await database;
double newStock;
late double materialKG;
newStock=currentQuanity+amountToAdd;
await db.rawUpdate(
'UPDATE materials SET quanity = $newStock WHERE name = "$name" ');
mapM = await db.query('materials'); //update values
//the following is only because setState is not working properly on other screen
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Added $amountToAdd KG to $name"),
));
}
displayAmountToAdd and showDialog method are future. Use await before theses to hold method to finish.
A sample example:
const oneSecond = Duration(seconds: 1);
// ···
Future<void> printWithDelay(String message) async {
await Future.delayed(oneSecond);
print(message);
}
Learn more about async-await.

Flutter FlushBar does not work if I have to pre-process data

I am trying to build a form in Flutter. A user enters a value and clicks on a button, I run some basic validation on that value then show an AlertDialogue as a confirmation. If the user clicks on confirm I want to attempt an API call, get the result and display the result of that API call to let the user know what happened.If I display a Flushbar with hardcoded values it works. But If I try to do some string manipulation on the object first the Flushbar does not display. If I try to print the response from the function right into the Flushbar that also does not work.
Any advice on why this problem is occurring in the first place, and what would be the best way to solve it?
Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
textStyle: TextStyle(
fontSize: 30,
),
primary: Colors.lightGreen, // background
onPrimary: Colors.white, // foreground
),
onPressed: () {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState.validate())
{
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Confirmation'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Please confirm your action'),
Text('Would you like to buy ' + finalValue.toString() + ' worth of merchandise'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('No'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Confirm'),
onPressed: () {
Navigator.of(context).pop();
var response = bb.trade(_character, finalValue, true); //API CALL
*//String resp = response.body.split(',')[1].split(':')[1];
//resp = resp.substring(1);
//resp = resp.split('.')[0];
//print(resp);* //The real string manipulation we want to do but then flushbar does not display
Flushbar(
title: "Result",
message: "lol"+response.body.toString(), //FLUSHBAR DOES NOT DISPLAY AT ALL
duration: Duration(seconds: 5),
)..show(context);
},
),
],
);
},
);
}
},
child: Text('Buy'),
),
),
I Think the problem is that you are trying to access data from API call immediately , however data coming over API call must be awaited for.
and it is preferred to pop after showing the flush bar .
so if bb.trade(_character, finalValue, true); return future you should do like this
onPressed: () async {
var response = await bb.trade(_character, finalValue, true);
Flushbar(
title: "Result",
message: "lol"+response.body.toString(),
duration: Duration(seconds: 5),
)..show(context).then((value) => Navigator.pop(context));
},

Run a function AFTER that the alertbox has been dismissed

I already read countless links like this one but it does not help.
The use case is very simple, I want to run a function AFTER the alert box has been dismissed.
void dummyFunc() {
sleep(Duration(seconds:3));
print("done");
}
Future<bool> displayDialog() async {
return showDialog<bool>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('AlertDialog Title'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('This is a demo alert dialog.'),
Text('Would you like to approve of this message?'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Decline'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
FlatButton(
child: Text('Approve'),
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
elevation: 24.0,
shape:RoundedRectangleBorder(),
);
},
);
}
var button = AnimatedOpacity(
opacity: 1.0,
duration: Duration(milliseconds: 500),
child: FloatingActionButton(
tooltip: 'Start',
child: Icon(iconShape),
onPressed: () async {
bool shouldUpdate = await displayDialog();
print(shouldUpdate);
if (shouldUpdate)
dummyFunc();
})
);
But the alert dialog is dismissed 3sd after.
Kindly let me know what I am doing wrong, Thank you~
I think this is happening because you are using sleep. Instead of that use Future delay.
void dummyFunc() {
print("done");
}
If you don't want delay, then you can also remove this future, this function will executed after dialog box dismissed.
Sleep will hold the process, that’s why you are facing this error.
Solved it thanks to Viren who gave me a good intuition, Timer works also nicely if you are not using a loop:
void dummyFunc() {
Timer(Duration(seconds: 3), () {
print("done");
});
}
Edit: Actually Viren's answer work better with loops! This can work also. Just avoid sleep. Spent 3h on this, now I hate sleep().