Shows warning: Do not use BuildContexts across async gaps - flutter

if (_formKey.currentState!.validate()) {
try {
final newUser =
await _auth.createUserWithEmailAndPassword(
email: email.text, password: password.text);
if (newUser != null) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => DashboardScreen(),
// ));
Navigator.pushNamed(context, 'dashboard');
}
setState(() {});
} catch (e) {
print(e);
}
}
},
this warning shown on Navigator.pushNamed(context,'dashboard');
trying to navigate to the dashboar screen.

1.
You have to put delay for other process can finish till then
Future.delayed(Duration(milliseconds: 200)).then((value) {
Navigator.pushNamed(context, 'dashboard')
});
2.
add if (!mounted) return; before Navigator.pushNamed(context, 'dashboard')
3.
Please put await before the navigator flutter because you used an asynchronously method call so you have to wait until the process is finished then you can navigate to your pages
await Navigator.pushNamed(context, 'dashboard');
4.
Also, you can store your navigator into a var and then use it.
final nav = Navigator.of(context);
nav.pushNamed('dashboard');

Related

Flutter Future.delayed timer dispose when navigating to other page

In my flutter app, I have a function that has delaye for 5 seconds to activate a button. When I navigate to other page, the timer of the delayed is still working even though I use the "pushReplacement" navigator. Can anyone help me find a way to dispose or cancel this timer when I navigate to other page.
here is the code:
Future sendVerificationEmail() async {
try{
final user =FirebaseAuth.instance.currentUser!;
await user.sendEmailVerification();
setState(() => canResendEmail = false);
await Future.delayed(const Duration(seconds: 5)); // this is the line causing the error
setState(() => canResendEmail = true);
}catch (e) {
Utils.showSnackBar(e.toString());
}
}
and here is the navigation button function:
Future<void> SignOut() async {
await FirebaseAuth.instance.signOut();
Navigator.pushReplacement(
context,MaterialPageRoute(builder: (context) => mainPage()),
);
}
Try using a timer instead
Timer timer = Timer(Duration(seconds: 5), () {
//do something here();
});
// You can dispose the timer like this
timer.cancel();

Flutter Get Navigator context for async task

When I press the login button, I receive a Future. If true, then I change page, else I display a popup dialog.
onPressed: () async {
final navigator = Navigator.of(context); // store the Navigator to enable .push
bool? res = await sendUser('6#gmail.com', 'password');
// if connection succeeds
if (res) {
navigator.push(MaterialPageRoute(builder: (context) => const newScreen()));
} else {
showPopUp(context);
}
}
I have the warning 'Do not use BuildContexts accros async gaps' for the popup. I had this warning for Navigator.push, so I fixed it by storing the Navigator but I don't know what to do for the popup. Can I get like the context of navigator ?
Try surrounding your showPopUp function inside an if (mounted) like so:
onPressed: () async {
final navigator = Navigator.of(context); // store the Navigator to enable .push
bool? res = await sendUser('6#gmail.com', 'password');
// if connection succeeds
if (res) {
navigator.push(MaterialPageRoute(builder: (context) => const newScreen()));
} else if (mounted) { //Changed here <-------------
showPopUp(context);
}
}
PS: Caution when using ternary operators while this issue is open

How to send a verification email on registerUsingEmailPassword() in flutter

I wan't when a user clicks sign up button an email verification is sent. So far with my code on signup an email verification is sent but user can't navigate to the next page (CircularProgressIndicator keeps on loading)
Here is my code
onPressed: () async {
if (_regFormKey.currentState!.validate()) {
setState(() {
_isProcessing = true;
});
User? user = await FireAuth.registerUsingEmailPassword(
name: nameController,
email: _emailController.text,
password: _passwordController.text,
);
if (user != null) {
bool EmailSent = user.sendEmailVerification() as bool;
//I think something is wrong here
if (EmailSent) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => ProfilePage(user: user),
),
ModalRoute.withName('/'),
); }
} else{
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(' Account exists or Network problems'),
backgroundColor: Colors.red,
));}
setState(() {
_isProcessing = false;
});
}}
sendEmailVerification() returns a Future<void> so EmailSent is not going to get set. You should await the verification call in a try...catch to handle the response.
More like this:
if (user != null) {
try {
await user.sendEmailVerification();
/// sent successfully
// TODO: put your navigation here
} catch (e) {
/// error sending verification
// TODO: show snackbar
// TODO: set _isProcessing to false
}
}

Flutter Firebase Auth throws NoSuchMethodError: The getter 'data' was called on null

During the email app signup process using firebaseAuth.createUserWithEmailAndPassword, when I try to do an upload or save to prefs in the .then part it throws this error:
NoSuchMethodError: The getter 'data' was called on null.
So I can work around this by Navigating to a new screen and postponing processing of the user's TextFormField input till there, but it's messy and bugs me.
Doing anything big in the .then seems problematic but I don't really know what's causing the problem, or what in fact the best way is to solve this kind of issue for future clarity. Education appreciated!
void registerToFb() {
firebaseAuth
.createUserWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((result) async {
Person user = new Person();
user.email = emailController.text;
user.firstName = firstNameController.text;
user.surname = surnameController.text;
user.postcode = postcodeController.text;
user.password = passwordController.text;
user.city = cityController.text ?? "Edinburgh";
user.firebaseId = result.user.uid;
Map<String, dynamic> firebaseUpload = user.toMap();
print("Attempting to reduce upload");
firebaseUpload.removeWhere((key, value) => value == null);
user.country = "GB";
String path = "${user.country}/${user.city}/People";
print("Attempting record upload");
DocumentReference autoId =
await myFirestore.collection(path).add(firebaseUpload);
user.personId = autoId.id;
user.saveToPrefs(prefs);
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => MyHomePage()));
}).catchError((err) {
print("Login thrown an error...\n${err.toString()}");
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error 10"),
content: Text("${err.toString()}"),
actions: [
ElevatedButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
});
A suggestion from me is to completely remove the .then() callback, since you have it stated as async. A better approach would be to make the whole function async, so you can do all your async code directly inside that.
Make the function async
void registerToFb() async { ...
Change the .then() callback to a simple await and store the result in your result variable.
var result = await firebaseAuth.createUserWithEmailAndPassword(email: emailController.text, password: passwordController.text);
I would highly suggest surrounding this statement with a try/catch block, to avoid unhandled errors:
try {
var result = await firebaseAuth.createUserWithEmailAndPassword(
email: emailController.text,
password: passowrdController.text
);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('password too weak.');
} else if (e.code == 'email-already-in-use') {
print('email already exists');
}
} catch (e) {
print(e);
}
You might get this error because you marked the .then() call as async, since it then executes asynchronously and the data might not be "there" yet, but I am not sure about this one.

How to use barcode result to open webview in flutter

I am using BarcodeScanner package. I get the result after scan the QR code. My question is how to use that result then open the website. Here is my code:
Future scan() async{
Completer<WebViewController> _controller = Completer<WebViewController>();
try {
String barcode = await BarcodeScanner.scan();
setState(() {
this.barcode = barcode;
print(this.barcode);
WebView(
initialUrl: this.barcode,
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
),
});
} on PlatformException catch (e) {
if (e.code == BarcodeScanner.CameraAccessDenied) {
setState(() {
this.barcode = 'The user did not grant the camera permission!';
});
} else {
setState(() => this.barcode = 'Unknown error: $e');
}
} on FormatException{
setState(() => this.barcode = 'null (User returned using the "back"-button before scanning anything. Result)');
} catch (e) {
setState(() => this.barcode = 'Unknown error: $e');
}
}
}
If you use Webview here, after you receive QR code from scanner you need you navigate to new screen. Like this
Navigator.push(
context,
MaterialPageRoute(builder: (context) => WebView(initUrl: data[index].homeLink))
);
Otherwise, you can use url_launcher plugin
https://pub.dev/packages/url_launcher
controller.pauseCamera();
if (await canLaunch(scanData.code)) {
await launch(scanData.code);
}
controller.resumeCamera();
For more details, see here