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!
Related
I want to show a What's New style AlertDialog to inform users what has changed in my app after updating. I've created the following function to see if the app has been updated:
Future<bool> checkNewVersion() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appVersion = packageInfo.version;
SharedPreferences prefs = await SharedPreferences.getInstance();
final String? currVersion = prefs.getString("version");
print("App version: $appVersion");
print("Current version: $currVersion");
if (currVersion == null) {
await prefs.setString("version", appVersion);
return true;
}
if (currVersion != appVersion) return true;
return false;
}
When this function is called in the build method below, the print statements output the following, but the alert dialog is not shown:
flutter: App version: 2.0
flutter: Current version: null
#override
Widget build(BuildContext context) {
if (checkNewVersion() == true) {
showDialog(context: context, builder: (_) =>
AlertDialog(
title: const Text("What's New / Que ha Cambiado"),
content: Text(updateInfo),
actions: <Widget>[
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
return Scaffold(
// app main menu
...
);
}
You are Performing an async operation to get the result. So when performing an async function that returns some Future you must await for it otherwise it will return you an incomplete future.
So when calling checkNewVersion()
You should await for its result like
var versionResult = await checkNewVersion();
Your code will be like
#override
Widget build(BuildContext context) {
var versionResult = await checkNewVersion();
if (versionResult) {
showDialog(context: context, builder: (_) =>
AlertDialog(
title: const Text("What's New / Que ha Cambiado"),
content: Text(updateInfo),
actions: <Widget>[
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
return Scaffold(
// app main menu
...
);
}
your checkNewVersion is an async function so you have to wait for its result, Try this:
#override
Widget build(BuildContext context) {
bool result = await checkNewVersion();
if (result) {
showDialog(context: context, builder: (_) =>
AlertDialog(
title: const Text("What's New / Que ha Cambiado"),
content: Text(updateInfo),
actions: <Widget>[
TextButton(
child: const Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
}
return Scaffold(
// app main menu
...
);
}
i want to make a download progress message inside the alert dialog but state not updating inside the the modal
the is the download function using dio
downloadBook() async {
Directory tempDir = await getTemporaryDirectory();
var response = await dio.download(widget.bookModel.acf.bookLink,
tempDir.path + '/books/' + widget.bookModel.title.rendered + '.epub',
options: Options(
responseType: ResponseType.bytes,
), onReceiveProgress: (actualbytes, totalbytes) {
var percenatge = actualbytes / totalbytes * 100;
_percentageBar = percenatge / 100;
setState(() {
downloadMessage = 'Downloading... ${percenatge.floor()} %';
});
});
}
and this is the alert dialogue function
void _showDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Alert!!"),
content: new Text(downloadMessage),
actions: <Widget>[
new FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
and i made call like this inside the text button but the message stuck on 0% and not updated;
onPressed: () {
// _showDialog(context);
downloadBook();
showDialog(
context: context,
builder: (context) {
String contentText = "Content of Dialog";
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: new Text("Alert!!"),
content: new Text(downloadMessage),
actions: <Widget>[
new FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
);
},
You can use
StatefullBuilder
widget top of your alert dialog and pass
setState
to it where you call it.
In this way you can access to State from outside.
As I understood from your question, you are trying to have a different state for your alert dialog, so I will just update your code regarding the alertDialog and you can do what you want after that.
showDialog(
context: context,
builder: (context) {
String contentText = "Content of Dialog";
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: new Text("Alert!!"),
content: new Text(downloadMessage),
actions: <Widget>[
new FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
},
);
So you can use setState for any progress you want inside.
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');
},
),
],
),
);
}
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();
}
I am trying to refresh the list of recipes after i delete one item and i have tried using SetState but I've got no result. I think the issue is that i get to this screen after making a GET call and that call is not executed again after deleting the item. What can I do?
void _pushRecipesByTypeScreen(String type) async {
var recipes = await entityApi.getRecipesByType(type);
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new Scaffold(
appBar: new AppBar(title: new Text(type)),
body: ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
final item = recipes[index];
return _buildEntityItem2(item);
})
);
}));
}
Widget _buildEntityItem2(Entity entity) {
return ListTile(
title: Text(entity.name),
onTap: () => _pushDeleteScreen(entity),
);
}
void _pushDeleteScreen(Entity entity) {
showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: new Text('Delete "${entity.name}"?'),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () => Navigator.of(context).pop()),
new FlatButton(
child: new Text('CONFIRM'),
onPressed: () {
_removeEntityItem(entity);
setState(() {});
Navigator.of(context).pop();
})
]);
});
}
void _removeEntityItem(Entity entity) async {
if (await connectivity.checkConnectivity() != ConnectivityResult.none) {
bool success = await entityApi.deleteEntity(entity);
if (success) {
setState(() {});
}
}
}
The setstate should be used outside of dialog for it to reload your listview
void _pushRecipesByTypeScreen(String type) async {
var recipes = await entityApi.getRecipesByType(type);
Navigator.of(context).push(new MaterialPageRoute(builder: (context) {
return new Scaffold(
appBar: new AppBar(title: new Text(type)),
body: ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
final item = recipes[index];
return _buildEntityItem2(item);
})
);
}));
}
Widget _buildEntityItem2(Entity entity) {
return ListTile(
title: Text(entity.name),
onTap: () async {
if(await _pushDeleteScreen(entity)) {
await _removeEntityItem(entity); //await until this completes
setState(() {});
}
}
);
}
Future<bool> _pushDeleteScreen(Entity entity) {
return showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: new Text('Delete "${entity.name}"?'),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () => Navigator.of(context).pop(false)),
new FlatButton(
child: new Text('CONFIRM'),
onPressed: () {
Navigator.of(context).pop(true);
})
]);
});
}
// make this return future
Future<void> _removeEntityItem(Entity entity) async {
if (await connectivity.checkConnectivity() != ConnectivityResult.none) {
bool success = await entityApi.deleteEntity(entity);
if (success) {
setState(() {});
}
}
}