Flutter "Do not use BuildContexts across async gaps" - flutter

Basically I want to return to my LoginView when the user presses Logout in the dialog.
onSelected: (value) async {
switch (value) {
case MenuAction.logout:
final shouldLogout = await showLogOutDialog(context);
final navigator = Navigator.of(context);
if (shouldLogout) {
await FirebaseAuth.instance.signOut();
navigator.pushNamedAndRemoveUntil(
'/login',
(route) => false,
);
}
}
},
showLogoutDialog function:
Future<bool> showLogOutDialog(BuildContext context) {
return showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Sign out'),
content: const Text('Are you sure you want to sign out?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(false);
},
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop(true);
},
child: const Text('Logout'),
),
],
);
},
).then((value) => value ?? false);
I get this error: "Do not use BuildContexts across async gaps.".
Anyone who can help me?
Thanks in advance!

It is unsafe, try checking if the widget is not mounted as shown on the Flutter YouTube Channel.
if (!mounted) return
Navigator.of(context);

Related

How to auto dismiss an AlertDialog box after getting GPS point?

I am trying to make a dialog box that displays "text" after getting the location and then doing... Navigator.of(context).pop(); to auto close the dialog
Help! I am starting at Flutter... here's the code
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
......
showProgressDialogGPS(BuildContext context,
{String message = 'Loading...'}) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
Future.delayed(Duration(milliseconds: 50), () {
_getPosition();
Navigator.of(context).pop();
});
return AlertDialog(
actions: [],
content: Row(
children: <Widget>[
CircularProgressIndicator(),
SizedBox(width: 20),
Expanded(child: Text(message)),
],
),
);
},
);
}
Future _getPosition() async {
await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.low)
.then((value) {
print(value);
});
}
.......
Thanks guys!!!
You can modify your code like this :
Future.delayed(Duration(milliseconds: 50), () async{
await _getPosition();
Navigator.of(context).pop();
});
here you are waiting for _getPosition to be executed then the statement Navigator.of(context).pop(); will be executed.

Flutter - Custom back button doesn't work

I have an alertdialog which is supposed to return a bool depending on the user's choice, the message gets removed, but the back button isn't working!
Future<bool?> showwarning(BuildContext context) async {
showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: new Text("Alert!!"),
content: new Text("Return To Main Page?!"),
actions: [
TextButton(
child: new Text("Yes"),
onPressed: () {
Navigator.pop(context, true);
},
),
TextButton(
child: new Text("Nope"),
onPressed: () {
Navigator.pop(context, false);
},
)
],
));
}
Widget build(BuildContext context) {
// TODO: implement build
return WillPopScope(
onWillPop: () async {
final user_decision = await showwarning(context);
return user_decision ?? false;
},
Future<bool?> showwarning(BuildContext context) async {
return showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: new Text("Alert!!"),
content: new Text("Return To Main Page?!"),
actions: [
TextButton(
child: new Text("Yes"),
onPressed: () {
Navigator.pop(context, true);
},
),
TextButton(
child: new Text("Nope"),
onPressed: () {
Navigator.pop(context, false);
},
)
],
));
}
Widget build(BuildContext context) {
// TODO: implement build
return WillPopScope(
onWillPop: () async {
final user_decision = await showwarning(context);
return user_decision ?? false;
},
The problem is that I wasn't returning anything from showwarning function , hence, it was always false!

wait until showdialog is closed flutter

I am trying to get the location permissions and I want to display a message before requesting the permission. but when I run the APP it doesn't wait until I close the showdialog
Future<void> checkPermission() async {
var status = await Permission.location.status;
print(status.toString());
if (status.isUndetermined) {
await showAlertPopup(context, '',
'OK')
.then((val) {
var statuses = Permission.location.request();
print(statuses);
});
}
if (status.isDenied ||
status.isPermanentlyDenied ||
status.isRestricted ||
status.isUndetermined) {
await showAlertPopup(context, '',
'error')
.then((val) {
openAppSettings();
});
}
if (status.isGranted) {
_geoAlowed = true;
}
}
and this is my code to display the popup
showAlertPopup(BuildContext context, String title, String detail) async {
showDemoDialog({BuildContext context, Widget child}) async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return child;
});
}
return showDemoDialog(
context: context,
child: AlertDialog(
title: Text(title),
content: Text(detail),
backgroundColor: grayLight,
actions: [
FlatButton(
child: Text('OK'),
onPressed: () {
Navigator.pop(context, 'OK');
},
),
],
),
);
}
As per your question, you want to request permission when user clicks 'OK' button in dialog.
You can create a callback, that is when user clicks 'OK'
showAlertPopup(context, '','OK', (){
//Code you want to execute when user clicks 'OK'
});
And little changes in you showAlertPopup
showAlertPopup(BuildContext context, String title, String detail, Function onClick) async {
showDemoDialog({BuildContext context, Widget child}) async {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return child;
});
}
return showDemoDialog(
context: context,
child: AlertDialog(
title: Text(title),
content: Text(detail),
backgroundColor: grayLight,
actions: [
FlatButton(
child: Text('OK'),
onPressed: () {
//Your callback
onClick();
Navigator.pop(context, 'OK');
},
),
],
),
);
}

close Simple Dialog in flutter when setState needs to called

I'm having a problem calling Navigator.of(context).pop() on my onPressed property in SimpleDialogOption widget. I need to set the state and dismiss the dialog. But calling setState is preventing my dialog to close. Without setState the dialog closes. Here is my dialog
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(
builder: (BuildContext context) {
return SimpleDialog(
children: _children(suburbs),
backgroundColor: Colors.white,
title: Text('Pick your suburb'),
);
},
context: context);
});
and the method I use for the list of the Dialog:
List<Widget> _children(List<Suburb> suburbs) {
return suburbs
.map((suburb) => SimpleDialogOption(
onPressed: () {
print('#####################');
setState(() {
postcode = suburb.name;
});
Navigator.of(context).pop();
},
child: Text(suburb.name)))
.toList();
}
you can await until the return value comes from the navigator.pop,
and then call a setState
WidgetsBinding.instance.addPostFrameCallback((_) async {
postcode = await showDialog(
builder: (BuildContext context) {
return SimpleDialog(
children: _children(suburbs),
backgroundColor: Colors.white,
title: Text('Pick your suburb'),
);
},
context: context);
setState(() {
postcode;
});
});
List<Widget> _children(List<Suburb> suburbs) {
return suburbs
.map((suburb) => SimpleDialogOption(
onPressed: () {
print('#####################');
Navigator.of(context).pop(suburb.name);
},
child: Text(suburb.name)))
.toList();
}

Flutter: future not returned after closing alert dialog

I created a http method in Flutter like below:
Future<void> addProduct(Product product) {
const url = 'https://flutter-shop-3de16.firebaseio.com/products';
return http
.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
)
.then((response) {
...
notifyListeners();
}).catchError((error) {
print(error);
throw error;
});
}
I know that my url is wrong because i want to get error.
In .catchError i throw an error and in the main page by provider i used addProduct like this:
Provider.of<Products>(context, listen: false)
.addProduct(_editedProduct)
.catchError((error) {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An error occurred!'),
content: Text('Something went wrong.'),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
);
}).then((_) {
print('after catch accoured');
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
});
}
}
I catchError i got error and i show an alert dialog.After tap on Okay button i want to execute then block so i returned showDialog because it return a future.
But i do not know why
.then((_) {
print('after catch accoured');
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
});
}
No to run after alert dialog is closed?
make sure using return showDialog<Null>() instead of return showDialog()
also have a try-catch with throw error when using json.decode() inside products provider
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An error occurred!'),
content: Text('Something went wrong.'),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
).then((value) => null);
the above then((value) => null ); will solve your problem, it would help you i think.
The then block will run after the main Future; it won't run after catchError. If you want to run a piece of code after catchError, use a whenComplete block.
Provider.of<Products>(context, listen: false)
.addProduct(_editedProduct)
.catchError((error) {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An error occurred!'),
content: Text('Something went wrong.'),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
);
}).whenComplete(() { // This will run after execution
print('after catch accoured');
setState(() {
_isLoading = false;
});
Navigator.of(context).pop();
});
}
}
Below code should solve your issue. moving then statement after show dialog would make it work again.
Provider.of<Products>(context, listen: false)
.addProduct(_editedProduct)
.catchError((error) {
return showDialog(
context: context,
builder: (ctx) =>
AlertDialog(
title: Text('An error occurred!'),
content: Text('Something went wrong.'),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
).then((_) {
print('after catch accoured');
setState(() {
isLoading = false;
});
Navigator.of(context).pop();
});;
});