How to dismiss an AlertDialog in Flutter? - flutter

I have a utility class that shows an alert dialog to which I'm passing a VoidCallback which usually contains Navigator.of(context).pop() to dismiss the alert dialog. However it throws a _CastError (Null check operator used on a null value) on button press.
class CustomAlertDialog {
static void show(
BuildContext context, {
Key? key,
required VoidCallback onPress,
}) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ElevatedButton(
onPressed: onPress,
child: const Text(
"test",
),
),
),
);
}
}
If I explicitly pass Navigator.pop(context,true) to the onPress method it works just fine, however I need this to be a reusable static method with different onPress functionalities that does more than just pop the dialog.
class CustomAlertDialog {
static void show(
BuildContext context, {
Key? key,
required VoidCallback onPress,
}) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ElevatedButton(
onPressed: () {
return Navigator.pop(context, true);
},
child: const Text(
"test",
),
),
),
);
}
}

Your Parent Context does not contain the CustomAlertDialog.
You should pass the context provide by the builder into the onPress function and call navigator.pop with the context provided in the builder function in showDialog.
class CustomAlertDialog {
static void show(
BuildContext context, {
Key? key,
required void Function(BuildContext context) onPress,
}) {
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ElevatedButton(
onPressed: (){ onPress(context) },
child: const Text(
"test",
),
),
),
);
}
}

you can try this :
void show(BuildContext context, String title, {Function callback})
onPressed: () {
if (callback != null) {
Navigator.pop(context, true);
callback();
} else {
Navigator.pop(context, true);
}
},

Related

Check content function (Flutter)

I use the following function to check and display the content either in Dialog or Bottom Sheet, but when executing it does not work properly, as it displays both together, what is the reason and how can the problem be solved?
Is it possible to suggest a better name for the function?
Content function:
content(BuildContext context, dynamic dialog, dynamic bottomSheet) {
(MediaQuery.of(context).orientation == Orientation.landscape) ? dialog : bottomSheet;
}
Implementation:
ElevatedButton(
child: Text('Button'),
onPressed: () {
content(context, dialog(context), bottomSheet(context));
},
),
How can this be solved?
In order to determine the Orientation of the screen, we can use the OrientationBuilder Widget. The OrientationBuilder will determine the current Orientation and rebuild when the Orientation changes.
void main() async {
runApp(const Home(
));
}
class Home extends StatefulWidget {
const Home({Key key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(
body: Center(
child: OrientationBuilder(
builder: (context, orientation) {
return ElevatedButton(
child: Text('Button'),
onPressed: () {
revealContent(orientation,context);
},
);
},
)
),
));
}
revealContent(Orientation orientation, BuildContext context) {
orientation == Orientation.landscape ? dialog(context) : bottomSheet(context);
}
dialog(BuildContext context){
showDialog(
context: context,
builder: (context) => const Dialog(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Text('test'),
),
)
);
}
bottomSheet(final BuildContext context) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (builder) => const Padding(
padding: EdgeInsets.all(20.0),
child: Text('test'),
),
);
}
}
here are screenshots:
happy coding...
The reason the function is not working properly is because you're not actually showing the dialog or bottom sheet. To show the dialog or bottom sheet, you need to call showDialog or showModalBottomSheet, respectively, and pass them the result of calling dialog or bottomSheet.
try this
void revealContent(BuildContext context, Widget dialog, Widget bottomSheet) {
(MediaQuery.of(context).orientation == Orientation.landscape)
? showDialog(context: context, builder: (context) => dialog)
: showModalBottomSheet(context: context, builder: (context) => bottomSheet);
}
You have a fundamental misunderstanding as to what your code is doing.
Take your "Implementation" and revealContent code, for example:
ElevatedButton(
child: Text('Button'),
onPressed: () {
revealContent(context, dialog(context), bottomSheet(context));
},
),
revealContent(BuildContext context, dynamic dialog, dynamic bottomSheet) {
(MediaQuery.of(context).orientation == Orientation.landscape) ? dialog : bottomSheet;
}
You think that revealContent will invoke either dialog or bottomSheet based on the orientation of the screen. What you are actually doing, however, is you are invoking both of them and then passing the result of the invocations to revealContent, which isn't actually doing anything with them.
What you need to be doing is passing the functions as callbacks to revealContent and then invoking the callbacks within the function:
ElevatedButton(
child: Text('Button'),
onPressed: () {
revealContent(context, () => dialog(context), () => bottomSheet(context));
},
),
revealContent(BuildContext context, void Function() dialog, void Function() bottomSheet) {
if (MediaQuery.of(context).orientation == Orientation.landscape) {
dialog()
} else {
bottomSheet();
}
}
You should be calling showDialog and showModalBottomSheet inside revealContent.
Dialog
dialog(BuildContext context){
return Dialog( //.. );
}
BottomSheet
bottomSheet(final BuildContext context) {
return Widget( /.. );
}
Reveal Content
void revealContent(BuildContext context, Widget dialog, Widget bottomSheet) {
if (MediaQuery.of(context).orientation == Orientation.landscape) {
return showDialog(context: context, builder: (context) => dialog);
} else {
return showModalBottomSheet(context: context, builder: (context) => bottomSheet);
}
}

In flutter, how do I dynamically update an alert dialog's action list?

I am trying to dynamically update an alert dialog's action list when something takes place in the alert dialog. Essentially, by default the dialog has a "Cancel" action button. But once the user does something in the dialog, I want it to have a "Cancel" and an "Accept" button. I have tried using a StatefulBuilder, which is how I am getting the rest of the dialog to update state. However, it is not working with the action buttons.
I've tried conditionally rendering the button, as well as generating a list to use for the dialog actions, and using setState to add to the list when an action takes place. Neither works, although other state updates within the dialog's content work with the StatefulBuilder. The dialog opens with only the "Cancel" action, and will not update to include the "Accept" action as well.
await showDialog<void>(
context: context,
builder: (BuildContext context) {
int? selectedRadio = 0;
return AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int? value) {
setState(() => selectedRadio = value);
},
);
}),
actions: <Widget>[
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
selectedRadio == 1 ? TextButton(
child: Text('Accept'),
onPressed: () {
Navigator.of(context).pop();
},
) : SizedBox.Shrink(),
],
);
},
),
);
},
);
Try putting the AlertDialog inside the StatefulBuilder
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
Create a StatefulWidget to display your AlertDialog, and manage selectedRadio there. Also take advantage of Dart's collection if to handle the conditional button:
class MyDialog extends StatefulWidget {
const MyDialog({Key? key}) : super(key: key);
#override
State<MyDialog> createState() => _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
int selectedRadio = 0;
#override
Widget build(BuildContext context) {
return AlertDialog(
actions: <Widget>[
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
if (selectedRadio == 1)
TextButton(
child: Text('Accept'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
content: Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int? value) {
setState(() => selectedRadio = value!);
},
);
}),
));
}
}
After this, you can display your dialog for example like this:
TextButton(
child: Text('Pressme'),
onPressed: () async => await showDialog<void>(
context: context,
builder: (BuildContext context) {
return const MyDialog();
},
))

Listview Builder Navigate to page when onTab

Using listview builder, I wanted to pass a string variable (userlist[index].page) to each list so that when onTap is pressed it it navigate to that page.
can someone help to fix the code?
this is the code
child: ListTile(
title: Text(
userlist[index].title,
),
trailing: IconButton(
icon: Icon(
alreadySaved ? Icons.star : Icons.star_border,
color: alreadySaved ? Colors.blue : Colors.blue,
),
onPressed: () {
setState(() {
if (alreadySaved) {
usersavedlist.remove(userlist[index]);
} else {
usersavedlist.add(userlist[index]);
}
});
},
), //subtitle: Text(subtitle),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => (userlist[index].page())));
}),
You need to create a page to navigate and pass the item list as a parameter. By example:
class UserDetail extends StatelessWidget {
final User user;
//constructor
UserDetail(this.user);
Widget build(BuildContext context) {
// Return the widget with the user info or whatever you want
return ...
}
}
And in your navitgator you pass like this:
onTap: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => UserDetail(userlist[index])));
}),

Alert Dialog not shown on button press

Class for Alert Dialog
class AlertWindow extends StatelessWidget {
final String title;
const AlertWindow({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Builder(
builder:(BuildContext context) {
return AlertDialog(
title: Text(this.title),
actions: <Widget>[
new FlatButton(
onPressed: (){
Navigator.of(context).pop();
},
child: new Text(
"OK"
)
),
],
);
}
);
}
}
Its been called in a aysnc function like this
Future<ParseUser> SignUP(username, pass, email) async {
var user = ParseUser(username, pass, email); // You can add columns to user object adding "..set(key,value)"
var result = await user.create();
if (result.success) {
setState(() {
_parseUser = user; // Keep the user
});
print(user.objectId);
new AlertWindow(
title: "Signup success " + user.objectId,
);
} else {
print(result.error.message);
new AlertWindow(
title: "Signup error " + result.error.message,
);
}
}
on running this, I can see the print statements in the console, but the AlertWindow doesn't show up.
I've got a hunch that its probably got something to do with the parent BuildContext not been passed to the AlertDialog when I'm creating it.
Try to use function instead of using a widget
Create a new function which return a future
Future<dynamic> _showDialog(BuildContext context){
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(this.title),
actions: <Widget>[
new FlatButton(
onPressed: (){
Navigator.of(context).pop();
},
child: new Text(
"OK"
)
),
],
);
}
);
}
You need to call the function showDialog for the AlertDialog to appear:
class AlertWindow {
final String title;
final BuildContext context;
const AlertWindow({Key key, this.title, this.context});
void widget(){
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(this.title),
actions: <Widget>[
new FlatButton(
onPressed: (){
Navigator.of(context).pop();
},
child: new Text(
"OK"
)
),
],
);
}
);
}
}
Working example:
https://dartpad.dev/051f787e1737de84609a390d31c36ee0
https://api.flutter.dev/flutter/material/showDialog.html

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();
}