How to display message on toast when raised button is clicked? - flutter

When login button is pressed, I want to show a toast that displays user details in successful or "not found" message otherwise.
new RaisedButton(
onPressed: () {
Future<User> user = _sendToServer();
if (user != null) {
Toast.show("You have logged as ${user.toString()}", context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
} else {
Toast.show("Not found", context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
}
},
child: new Text('log in'),
),
I know that I receive the right response but just unable to display it using toast.
Future<User> _sendToServer() async {
DatabaseHelper helper = DatabaseHelper.instance;
if (_key.currentState.validate()) {
// No any error in validation
_key.currentState.save();
User user = await helper.getLogin(email, password);
print("login user ${user.email}");
return user;
} else {
// validation error
setState(() {
_validate = true;
});
return null;
}
}

RaisedButton(
onPressed: () async{ // mark it async
User user = await _sendToServer(); // await to get user
if (user != null) {
Toast.show("You have logged as ${user.toString()}", context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
} else {
Toast.show("Not found", context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
}
},
child: new Text('log in'),
)

You should make the onPressed as async and call your function with await or use it like a promise.
For exa -
yourFunctiomWithFuture().then((value) {
// Run code here
}, onError: (error) {
print(error);
});

Related

How to pass this 'Dio Post Request' Problem in Flutter

I have to send an object from the request. Although it works on Swagger, I cannot send it from my flutter code. When I was trying it, it always gave me Http status error 400 This problem is coming my wrong request. I examined the code of the Internet. I thought I implemented it correctly but It still does not work. Please, could you help me to solve it? Here is my interested code areas:
my api side =>
Future changePassword(id, password) async {
try {
var prefs = await SharedPreferences.getInstance();
// User sendingData = User(
// id: id,
// );
// final dataSend = sendingData.toJson();
final response = await dio.post('http://192.168.1.108:2308/api/Account/ChangePassword',
data: jsonEncode({
"id": "$id",
"password": "$password"
}),
options: Options(headers: {
HttpHeaders.acceptHeader: '*/*',
HttpHeaders.contentTypeHeader: 'application/json',
}));
if (response.statusCode != 200) {
// debugPrint('burası-------------------------' + response.statusCode.toString());
}
prefs.remove('password');
return response;
}
catch (e) {
debugPrint(e.toString());
}
}
this is my screen side that I called it =>
onPressed: () async {
var prefs = await SharedPreferences.getInstance();
var pwd = prefs.getString('password');
var email = prefs.getString('email');
final usrId = prefs.getInt('userId');
pwdFormKey.currentState!.save();
if (pwdFormKey.currentState!
.validate()) {
try {
if(oldPwdController.text==pwd){
if(newPwdController.text==newPwdController2.text) {
await api.changePassword(usrId, newPwdController.text);
Navigator.pop(context);
Fluttertoast.showToast(
msg: "Şifreniz başarıyla değiştirilmiştir.",
toastLength: Toast
.LENGTH_SHORT,
gravity: ToastGravity
.BOTTOM,
backgroundColor: CupertinoColors
.activeGreen,
textColor: CupertinoColors
.black,
fontSize: 16.0);
}
// }
// }
else {
Fluttertoast.showToast(
msg: "Yeni girdiğiniz şifreler aynı değil, lütfen kontrol ediniz.",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: CupertinoColors.systemRed,
textColor: CupertinoColors.black,
fontSize: 16.0);
}
}
else{
Fluttertoast.showToast(
msg: "Eski Şifreniz Hatalıdır.",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: CupertinoColors.systemRed,
textColor: CupertinoColors.black,
fontSize: 16.0);
}
} catch (e) {
return showDialog(
context: context,
builder: (BuildContext
context) {
return AlertDialog(
title: Text(
"Hatalı Deneme"),
content:
SingleChildScrollView(
child: ListBody(
children: [
Text(
"Hatalı şifre değiştirme işleminde bulundunuz. Lütfen tekrar deneyiniz.")
],
),
),
actions: [
TextButton(
child: const Text(
'Tamam'),
onPressed: () {
Navigator.of(
context)
.pop();
},
),
],
);
});
}
}
},
In post method there are 2 diff values. One is data and another one is queryParameters. You can try in this way.
Map<String, dynamic>? param = {"userName": "name"};
return await (_dio.post(
"http://192.168.1.108:2308/api/Account/ChangePassword",
data: data, //Optional
queryParameters: params,
)).catchError((e) {
if (!checkSessionExpire(e, context)) {
throw e;
}
});

error dart migration tool - Expected a value of type '(UserCredential) => FutureOr<UserCredential>', but got one of type '(dynamic) => Future<Null>'

I try to figure out, how I can get my database request "signUpUser" to work again. Before I used null-safety my code worked fine, but now after using the dart migration tool I am getting the error:
Expected a value of type '(UserCredential) => FutureOr<UserCredential>', but got one of type '(dynamic) => Future<Null>'
How can I fix this error? I am really desperate, I don't understand this error and I need to have a working code. If I can't fix this error, then I must use a not null-safety code, so I hope you can help me.
The registration works fine, just the database request is throwing an error.
Database request:
Future<bool> signUpUser(String username, String email, String password,
String firstName, String lastName, String? birthday, String? gender) async {
try {
_status = Status.Authenticating;
notifyListeners(); //changing status
FirebaseApp secondaryApp = await Firebase.initializeApp(
name: 'Secondary',
options: Firebase.app().options,
);
try {
UserCredential credential = await FirebaseAuth.instanceFor(
app: secondaryApp)
.createUserWithEmailAndPassword(email: email, password: password)
.then((result) async {
//User user = result.user;
_userServices.createUser(
uid: result.user!.uid,
username: username,
email: email,
firstName: firstName,
lastName: lastName,
birthday: birthday,
gender: gender,
status: 'aktiv',
role: 'User',
);
}as FutureOr<UserCredential> Function(UserCredential));
if (credential.user == null) {
throw 'An error occured. Please try again.';
}
await credential.user!.sendEmailVerification();
} on FirebaseAuthException catch (e) {
print(e);
}
await secondaryApp.delete();
return true;
} catch (e) {
_status = Status.Unauthenticated;
notifyListeners();
print(e.toString());
return false;
}
}
registration:
InkWell(
onTap: () async {
final Formkey = _formKeys.currentState;
print('pw confirmed:' +
passwordConfirmedController.text.trim());
print('pw:' + passwordController.text.trim());
//password and passworconfirm check
if (passwordConfirmedController.text.trim() ==
passwordController.text.trim()) {
//checking if all textfields are not empty
if (usernameController.text.trim() != null &&
emailController.text.trim() != null &&
passwordController.text.trim() != null &&
passwordConfirmedController.text.trim() !=
null &&
firstNameController.text.trim() != null &&
lastNameController.text.trim() != null &&
isDateSelected != false &&
_genderSelected != null) {
// checking if all textfields are valid
if (Formkey!.validate()) {
print('validate okok');
// input is the authProvider.emailController, which provides the written email
// output are all the user informations in a Map<String, dynamic>
// used to check the status and role of the user
mapUserinformations =
await authProvider.getUserByEmail(
emailController.text.trim());
//when email exist, then check status
if (mapUserinformations!.isNotEmpty) {
print('email is already existing');
mapUsernameExist = await authProvider.UsernameExist(mapUserinformations!['username']);
//if username already exist, then print error otherwise it is free
if (mapUsernameExist!.isEmpty){
print('username is free');
//checking if status is deleted
if (mapUserinformations!['status'] ==
'gelöscht') {
print('email is deleted');
//recreate the deleted user
try {
//update user informations
await authProvider.updateUserSignup(
mapUserinformations!['uid'],
mapUserinformations!['username'],
mapUserinformations!['email'],
mapUserinformations!['firstName'],
mapUserinformations!['lastName'],
_birthDateInString,
_genderSelected,
'User');
//input: emailcontroller, output: send password reset link
try {
await FirebaseAuth.instance
.sendPasswordResetEmail(
email: emailController.text
.trim());
} on FirebaseAuthException catch (e) {
print(e);
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(
e.message.toString()),
);
});
}
clearController();
isDateSelected = false;
_genderSelected = null;
// deleted user got recreated - now print a message that the registration process is completed
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Registration abgeschlossen.\nDein Account war gelöscht, daher wurde dir eine E-Mail zum zurücksetzen deines persönlichen Passworts zugesendet.\nNachdem du das Passwort abgeändert hast, kannst du "
"dich nun in unserer App einloggen.",
textAlign:
TextAlign.center),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context)
.pop();
},
)
],
);
});
} catch (e) {
print(e);
}
}
// email is already existing and the status is not deleted
else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Error: Es existiert schon ein Account mit dieser E-Mail Adresse."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context)
.pop();
},
)
],
);
});
}
}else{
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Error: Der Username wird bereits verwendet. Bitte benutze einen anderen."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
}
// email not existing in database -> mapUserinformations = null
else {
try {
print('email existiert noch nicht');
// sign up user in database with the birthday and gender + all controllers from authProvider
await authProvider.signUpUser(
usernameController.text.trim(),
emailController.text.trim(),
passwordController.text.trim(),
firstNameController.text.trim(),
lastNameController.text.trim(),
_birthDateInString,
_genderSelected);
clearController();
isDateSelected = false;
_genderSelected = null;
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Registration abgeschlossen. Du kannst dich nun in unserer App einloggen."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context)
.pop();
},
)
],
);
});
} catch (e) {
print(e);
}
}
} else {
print('validate email notgoodatall');
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Error: Bitte überprüfe, ob alle deine Eingaben ein gültiges Format aufweisen."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
}
// not all Textfields/Buttons are filled
else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Error: Registration gescheitert! Bitte alle Felder ausfüllen."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
return;
}
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
"Error: Passwort und Passwort wiederholen müssen gleich sein!"),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
},
child: Container(
decoration: BoxDecoration(
color: Colors.deepPurple,
borderRadius: BorderRadius.circular(20)),
alignment: Alignment.center,
width: double.maxFinite,
padding: EdgeInsets.symmetric(vertical: 16),
child: Text(
"Registrieren",
style: TextStyle(
color: Colors.white,
),
))),
#jamesdlin Answered my question. The edited database request looks like this:
Future<bool> signUp(
String username,String email,String password,String firstName,
String lastName,String? birthday,String? gender,String? role) async {
try {
FirebaseApp secondaryApp = await Firebase.initializeApp(
name: 'Secondary',
options: Firebase.app().options,
);
try {
UserCredential? credential = await FirebaseAuth.instanceFor(
app: secondaryApp)
.createUserWithEmailAndPassword(email: email, password: password);
_userServices.createUser(
uid: await credential.user!.uid,
username: username,
email: email,
firstName: firstName,
lastName: lastName,
birthday: birthday,
gender: gender,
status: 'aktiv',
role: role,
);
if (credential.user == null) {
throw 'An error occured. Please try again.';
}
await credential.user!.sendEmailVerification();
print('send verfication');
await secondaryApp.delete();
print('deleted instance');
} on FirebaseAuthException catch (e) {
print(e);
}
return true;
} catch (e) {
print(e.toString());
return false;
}
}

Do not use BuildContexts across async gaps flutter [duplicate]

This question already has answers here:
Do not use BuildContexts across async gaps
(10 answers)
Closed last month.
I've made a function for registration and I'm getting a warning Do not use BuildContexts across async gaps. on Utils.flushBarErrorMessage("No Internet", context); I'm new to flutter and want to know how to use async and await.
Future _registration() async {
String name = _nameController.text.trim();
String email = _emailController.text.trim();
String password = _passwordController.text.trim();
String phone = _phoneController.text.trim();
if (name.isEmpty) {
Utils.flushBarErrorMessage("Type your name", context);
} else if (email.isEmpty) {
Utils.flushBarErrorMessage("Type your email", context);
} else if (!GetUtils.isEmail(email)) {
Utils.flushBarErrorMessage("Type valid email address", context);
} else if (password.isEmpty) {
Utils.flushBarErrorMessage("Type your password", context);
} else if (password.length < 6) {
Utils.flushBarErrorMessage(
"password can't be less than 6 characters", context);
} else if (phone.isEmpty) {
Utils.flushBarErrorMessage("Type your phone", context);
}
else {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile ||
connectivityResult == ConnectivityResult.wifi) {
ApiCall.signUp(name, email, password, phone).then((value) {
if (value.statusCode == 200) {
if (json.decode(value.body)['success'] != null) {
if (json.decode(value.body)["success"]) {
RegisterResponse registerResponseModel =
RegisterResponse.fromJson(json.decode(value.body));
Navigator.pushNamed(context, VerifyUser.routeName);
Utils.flushBarErrorMessage(
'User Registered Successfully', context);
if (kDebugMode) {
print('User Registered Successfully');
}
} else {
Utils.flushBarErrorMessage(
json.decode(value.body)["en_message"], context);
if (kDebugMode) {
print(json.decode(value.body).toString());
}
}
}
} else {
Utils.flushBarErrorMessage('invalid data', context);
if (kDebugMode) {
print(json.decode(value.body).toString());
}
}
});
} else {
Utils.flushBarErrorMessage("No Internet", context);
}
}
}
calling this _registration()
ElevatedButton(
onPressed: () {
_registration();
},
child: const Text('SignUp')),
Here is my flushBarErrorMessage.
class Utils {
static void flushBarErrorMessage(String message, BuildContext context) {
showFlushbar(
context: context,
flushbar: Flushbar(
forwardAnimationCurve: Curves.decelerate,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
padding: const EdgeInsets.all(15),
titleColor: Colors.white,
duration: const Duration(seconds: 3),
borderRadius: BorderRadius.circular(10),
reverseAnimationCurve: Curves.easeInOut,
icon: const Icon(
Icons.error,
size: 28,
color: Colors.white,
),
flushbarPosition: FlushbarPosition.TOP,
positionOffset: 20,
message: message,
backgroundColor: Colors.red,
)..show(context));
}
}
The problem is that after an await, every use of the BuildContext will show this warning. This warning happens because using a BuildContext after an await could happen after the widget is disposed of. This way, the context wouldn't exist anymore and the app could even crash because of this. Check out the official lint documentation:
Storing BuildContext for later usage can easily lead to difficult-to-diagnose crashes. Asynchronous gaps are implicitly storing BuildContext and are some of the easiest to overlook when writing code.
The easy solution, from the official docs, is the need to check for State.mounted. The code would look something like this on every place the warning shows up:
...
} else {
if (mounted) Utils.flushBarErrorMessage("No Internet", context);
}
...
Try this:
///Add context
Future _registration(BuildContext context) async {
...
if(!mounted) return;
Navigator.pushNamed(context, VerifyUser.routeName);
...
}
When calling:
ElevatedButton(
onPressed: () {
//make sure your class is of StatefulWidget()
_registration(context); ///Add context
},
child: const Text('SignUp')),
The simplest answer for this warning is:
StatelessWidget
keep a reference of the context usage before the await keyword.
example:
// navigation
onPressed: () async {
final router = GoRouter.of(context);
final result = await [
Permission.location,
Permission.locationAlways,
Permission.locationWhenInUse
].request();
if (...) {
router.go(AppRouter.dashboard);
} else {
router.go(AppRouter.askForCustomLocation);
}
// cubit
onPressed: () async {
final appSettingsCubit = BlocProvider.of<AppSettingsCubit>(context);
final result = await [
Permission.location,
Permission.locationAlways,
Permission.locationWhenInUse
].request();
if (...) {
appSettingsCubit.locationProptAsked();
} else {
appSettingsCubit.locationProptAsked();
}
StatefullWidget
just wrap the context usage with a if(mounted) logic
example:
// navigation
onPressed: () async {
final result = await [
Permission.location,
Permission.locationAlways,
Permission.locationWhenInUse
].request();
if (...) {
if(mounted) {
router.go(AppRouter.dashboard);
}
} else {
if(mounted) {
router.go(AppRouter.dashboard);
}
}
// cubit
onPressed: () async {
final result = await [
Permission.location,
Permission.locationAlways,
Permission.locationWhenInUse
].request();
if (...) {
if(mounted) {
BlocProvider.of<AppSettingsCubit>(context).locationProptAsked();
}
} else {
if(mounted) {
BlocProvider.of<AppSettingsCubit>(context).locationProptAsked();
}
}
That's all from my experience.

How to display N snackbar in a single page with scaffold?

I am trying to send a reset password link to users email.
Case 1: Display a snackbar when User has no email registered.
Case 2: Display a snackbar when email is sent.
Case 3: Display a snackbar if error.
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
This is the build function:
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: InkWell(...
This function Displays the snackbar:
//SnackBar
void showInSnackBar(String value) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(value),
duration: const Duration(seconds: 5),
action: SnackBarAction(
label: 'OKAY',
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
));
}
I am calling show the snackbar for this button:
CustomButton(
name: 'Get Confirmation Email',
color: Colors.redAccent,
onPressed: () async {
if (_formKey.currentState.validate()) {
FocusScope.of(context).unfocus();
try {
//Check if email already exist
List<String> res =
await _auth.checkEmail(_emailController.text);
print(res);
if (res == ['password']) {
await _auth.resetPassword(_emailController.text);
showInSnackBar('Email sent.Check your mail');
} else {
showInSnackBar('Email is not registered');
}
} catch (e) {
showInSnackBar(e);
}
}
},
)
Output:[password]
Problem:
on the UI it shows:Email is not registered,but the it should be Email sent.Check your mail.
I do not unserstand what is wrong here.Is it not getting the right context?
you just test your condition like
String res = await _auth.checkEmail(_emailController.text);
print(res);
if (res=="password") {
await _auth.resetPassword(_emailController.text);
showInSnackBar('Email sent.Check your mail');
}

How to open a link automatically after Scanning QR Code in Flutter without clicking some Button

I have a little question about QR Code Scanning in Flutter. How to open a URL website after successfully reading data from the QR Code?
I use this package for using QR Code, and this for open a URL and this is my function to checking the value of data from QR code is a URL or not and if it's a URL, then run the function to open the website.
checkingValue() {
if (_result != null || _result != "") {
if (_result.contains("https") || _result.contains("http")) {
return _launchURL(_result);
} else {
Toast.show("Invalide URL", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
}
} else {
return null;
}
}
_launchURL(String urlQRCode) async {
String url = urlQRCode;
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
The _result variable is a String to get a value from QR code data.
And this is the full of my code:
class _ScannerPageState extends State<ScannerPage> {
String _password;
String _result = "";
Future _scanQR() async {
try {
String qrResult = await BarcodeScanner.scan();
setState(() {
_result = qrResult;
});
} on PlatformException catch (ex) {
if (ex.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
_result = "Camera permission was denied";
Toast.show(_result, context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
});
} else {
setState(() {
_result = "Unknown Error $ex";
Toast.show(_result, context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
});
}
} on FormatException {
setState(() {
_result = "You pressed the back button before scanning anything";
Toast.show(_result, context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
});
} catch (ex) {
setState(() {
_result = "Unknown Error $ex";
Toast.show(_result, context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
});
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
return showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return PopUp(
content: "Are you sure want to exit?",
cancelText: "No",
acceptText: "Yes",
onTapCancel: () => Navigator.of(context).pop(),
onTapAccept: () async {
await SessionManager().removeSession();//
await SystemNavigator.pop();
},
);
}
);
},
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.lock),
onPressed: () {
Navigator.pushNamed(context, '/login');
},
),
],
),
body: Column(
children: <Widget>[
Text(_result.contains("https") || _result.contains("http") ? _result : "Invalid URL"),
],
),
floatingActionButton: FloatingActionButton.extended(
icon: Icon(Icons.camera_alt),
label: Text("Scan"),
onPressed: () => _scanQR(),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
),
);
}
checkingValue() {
if (_result != null || _result != "") {
if (_result.contains("https") || _result.contains("http")) {
return _launchURL(_result);
} else {
Toast.show("Invalide URL", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
}
} else {
return null;
}
}
_launchURL(String urlQRCode) async {
String url = urlQRCode;
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
}
So, where I put the checkingValue() function for running it after scanning the QR Code?
Pass qrResult to checkingValue method
Future _scanQR() async {
try {
String qrResult = await BarcodeScanner.scan();
checkingValue(qrResult);
//....
}
checkingValue method
checkingValue(String url) {
//...
}
or call checkingValue() after
setState(() {_result = qrResult;});
checkingValue();
//...