Flutter ShowDialog not closing - flutter

I can't solve this problem. I'm trying to do this step by step. When users push the button, first they gonna see AlertDialog, then the function startFilePickerBig will run. After the user chose files and is done with the startFilePickerBig function AlertDialog must close. But when I add Navigator.pop(context) after "await startFilePickerBig()" , alertdialog not working. When I remove "Navigator.pop(context)" which I wrote under the "await startFilePickerBig()", AlarmDialog works fine. How can I fix this?
OutlinedButton(
style: ButtonStyle(side: MaterialStateProperty.all(const BorderSide(width: 1.0, color: Color(0xFFF38F1D))), shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(7))), foregroundColor: MaterialStateProperty.all(const Color(0xFFF38F1D))),
onPressed: () async {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(''),
content: const Text('Pease wait, Images loading... '),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Close')),],
);
});
await startFilePickerBig();
Navigator.pop(context);
},
child: const Text("Add Image"),
)

What I might do here is to use ProgressIndicator instead of AlertDialogBox with the help of ternary operator on the build function.
However, as per your approach, this might work:
onPressed: () async {
await startFilePickerBig();
bool result= await showDialog(context:context, builder:( BuildContext context){
return AlertDialog(
title:Text("Pease wait, Images loading..."),
actions: [
TextButton(
child: Text("Picked"),
onPressed: (){
Navigator.of(context).pop(true);
},
),
TextButton(
child: Text("Not picked"),
onPressed: (){
Navigator.of(context).pop(false);
},
),
],
);
});
if(result==true){
//Do whatever you want
Fluttertoast.showToast(
msg: 'Items picked',
toastLength: Toast.LENGTH_LONG,
backgroundColor: kPrimaryColor);
removeFromList(item);
}else{
//Your code
}
}

Related

How to retrieve each element in a List to display as Text in a ElevatedButton?

I have a button that calls a function that adds user input to a List. If the list.isNotEmpty then an ElevatedButton is displayed with the user's input as it's label. I have a numberPicker that allows the user to select an amount, also in the same dialog box. The code works as intended apart from adding the amount to the List (I assume I would need some sort of Map<int, String>?)
I would like to loop through the List and add a new ElevatedButton every time the user enters a new item and amount. So far, the code only creates a new button with the list.first as its text. How do I create a loop to iterate through the List every time the user adds a new item that corresponds with the index to display the correct item?
Code that creates new button:
children: [
ElevatedActionButton(
icon: const Icon(Icons.add),
buttonText: const Text('Add Ingredient'),
onPressedAction: () {
_addIngredient(context);
},
),
if (ingredientList.isNotEmpty)
ElevatedButton(
onPressed: () {},
child: Text(
'$_currentValue ${ingredientList.first}'),
),
],
Code that showsDialog:
Future<void> _addIngredient(BuildContext context) async {
valueText = 'Add Ingredient';
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add Ingredient'),
content: Row(
children: [
Container(
width: 150,
child: TextField(
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: _ingredientController,
decoration: InputDecoration(hintText: '"Tomato"'),
),
),
NumberPicker(
value: _currentValue,
minValue: 0,
maxValue: 100,
onChanged: (value) => setState(() => _currentValue = value),
),
],
),
actions: <Widget>[
ElevatedButton(
child: Text('CANCEL'),
onPressed: () {
setState(() {
Navigator.pop(context);
});
},
),
ElevatedButton(
child: Text('OK'),
onPressed: () {
setState(() {
if (_ingredientController.text.isNotEmpty) {
ingredientList.add(_ingredientController.text);
codeDialog = valueText;
print(ingredientList);
print(ingredientList.length);
Navigator.pop(context);
} else {
Navigator.pop(context);
}
});
},
),
],
);
});

How to close the navigation drawer when returning back from a particular screen automatically in flutter?

I am working on a flutter project which has a navigation drawer with three routes. Whenever i go to a particular route and come back, the navigation drawer gets opened automatically. I want the navigation drawer to remain closed until and unless the user specifically clicks on it. Is there any way to achieve this?
Here's my code:
class NavList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
UserAccountsDrawerHeader(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/orange.png'),
fit: BoxFit.cover,
)),
arrowColor: Colors.deepOrangeAccent[700],
accountName: Text(''),
accountEmail: Text(
'username#gmail.com',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0,
),
),
currentAccountPicture: CircleAvatar(
radius: 22.0,
backgroundColor: Colors.deepOrangeAccent[700],
backgroundImage: AssetImage('images/profile.png'),
),
),
ListItems(
listTitle: 'Shops',
listIcon: Icon(Icons.shop),
listFunction: () {
Navigator.pushNamed(context, ShopScreen.id);
},
),
ListItems(
listTitle: 'Orders',
listIcon: Icon(Icons.monetization_on),
listFunction: () {
Navigator.pushNamed(context, OrderScreen.id);
},
),
ListItems(
listTitle: 'Logout',
listIcon: Icon(Icons.logout),
listFunction: () {
Navigator.pushNamed(context, LoginScreen.id);
},
),
],
),
);
}
}
I have refactored ListItems.
class ListItems extends StatelessWidget {
ListItems({this.listTitle, this.listIcon, this.listFunction});
final String listTitle;
final Icon listIcon;
final Function listFunction;
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(listTitle),
leading: IconButton(
icon: listIcon,
onPressed: () {
},
),
onTap: listFunction,
);
}
}
There are some ways to do that like:
onPressed:(){
Navigator.pop(context);
Navigator.pushNamed(context, ShopScreen.id);
}
or use .then of the navigator:
onPressed:(){
Navigator.pushNamed(context, ShopScreen.id).then((val){
Navigator.pop(context);
});
}
additionally, you can check If the drawer currently open or not:
onPressed:(){
Navigator.pushNamed(context, ShopScreen.id).then((val){
if(Scaffold.of(context).isDrawerOpen){
Navigator.pop(context);
}
});
}
I hadn't tested this out. Maybe this might work
onPressed: () async {
await Navigator.pushNamed(
context, "Screen").then((value) =>
Scaffold.of(context).openEndDrawer());
},
This may help you
onPressed: () {
Navigator.pop(context);
Navigator.of(context).pushNamed('/settings');
}

How to get return value with pop on Flutter?

I would like to know how to get two values with pop on Flutter.
I tried to write the code. but I got the exception "A non-null String must be provided to a Text widget."
Here is the codes.
First Screen
ElevatedButton(
child: const Text('move to second screen'),
style: ElevatedButton.styleFrom(
primary: Colors.orange, onPrimary: Colors.white),
onPressed: () async {
final List<String> _response =
await Navigator.pushNamed(
context, '/adminCategoryPicker');
emoji = _response[0];
emojiName = _response[1];
},
),
Second Screen
return Card(
child: ListTile(
leading: Text(targetElectricsEmojiLists[index]),
title: Text(targetElectricsNameLists[index]),
onTap: () {
setState(() {
_selectedName = targetElectricsNameLists[index];
_selectedEmoji = targetElectricsEmojiLists[index];
});
Navigator.pop(
context,
{_selectedName, _selectedEmoji},
);
},
),
);
I think this issue is below section. Do you know other way to write the codes?
First Screen
onPressed: () async {
final List<String> _response =
await Navigator.pushNamed(
context, '/goToSecondScreen');
emoji = _response[0];
emojiName = _response[1];
},
Second Screen
Navigator.pop(
context,
{_selectedName, _selectedEmoji},
);
I changed the codes. Moreover, I got the errors.
[ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: type 'MaterialPageRoute' is not a subtype of type 'Route<List>?' in type cast
First Screen
ElevatedButton(
child: const Text('move to second screen'),
style: ElevatedButton.styleFrom(
primary: Colors.orange, onPrimary: Colors.white),
onPressed: () async {
final List<String> _response =
await Navigator.pushNamed(
context, '/adminCategoryPicker');
emojiName = _response[0];
emoji = _response[1];
},
),
Second Screen
return Card(
child: ListTile(
leading: Text(targetElectricsEmojiLists[index] ?? 'null'),
title: Text(targetElectricsNameLists[index] ?? 'null'),
onTap: () {
setState(() {
_selectedName = targetElectricsNameLists[index];
_selectedEmoji = targetElectricsEmojiLists[index];
});
Navigator.pop(
context,
[_selectedName, _selectedEmoji],
);
},
),
);
You can try something like below
First Screen
ElevatedButton(
child: const Text('move to second screen'),
style: ElevatedButton.styleFrom(
primary: Colors.orange, onPrimary: Colors.white),
onPressed: () {
Navigator.pushNamed(context,'/adminCategoryPicker')
.then((value)
{
emoji = (value as Map)['emoji'];
emojiName = (value as Map)['emojiName'];
});
},
),
Second Screen
return Card(
child: ListTile(
leading: Text(targetElectricsEmojiLists[index]),
title: Text(targetElectricsNameLists[index]),
onTap: () {
setState(() {
_selectedName = targetElectricsNameLists[index];
_selectedEmoji = targetElectricsEmojiLists[index];
});
Navigator.pop(
context,{'emoji':_selectedName,'emojiName':_selectedEmoji}
);
},
),
);
I think you're returning the List of String in a wrong format. The strings need to be inside square brackets. Like this:
Navigator.pop(
context,
[_selectedName, _selectedEmoji],
);
And about the error "A non-null String must be provided to a Text widget.", this occurs when you're passing a null value to a Text Widget. You can change the Text Widgets in your second screen like this to check for null values and have a non-null dummy text inside the Text widgets and get rid of the error:
return Card(
child: ListTile(
leading: Text(targetElectricsEmojiLists[index] ?? 'NULL value here'),
title: Text(targetElectricsNameLists[index] ?? 'NULL value here'),
onTap: () {
setState(() {
_selectedName = targetElectricsNameLists[index];
_selectedEmoji = targetElectricsEmojiLists[index];
});
Navigator.pop(
context,
{_selectedName, _selectedEmoji},
);
},
),
);
First Screen
onPressed: () async {
final dynamic _response =
await Navigator.pushNamed(
context, '/goToSecondScreen');
emoji = _response["selectedName"];
emojiName = _response["selectedEmoji"];
},
Second Screen
Navigator.pop(
context,
{"selectedName":_selectedName,"selectedEmoji": _selectedEmoji},
);

Flutter: Pop from AlertBuilder and then show SnackBar

I am new in Flutter as well as App Development, What I want to achieve is Show SnackBar after poping from alertbuilder.
I think following code will be useful for understanding my intentions
onSubmitted: (value) async {
try {
await FirebaseAuth.instance
.sendPasswordResetEmail(
email: _loginEmail);
} catch (e) {
SnackBar(
backgroundColor: Colors.pink,
content: Text(e.toString()),
duration: Duration(seconds: 5),
);
Navigator.of(context).pop();
}
SnackBar(
backgroundColor: Colors.pink,
content: Text('Email sent with a link!'),
duration: Duration(seconds: 5),
);
Navigator.of(context).pop();
},
I hope it'll solve your problem
1, You should await your showDialog at your onpressed event like this
var result = await showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: Text("your title")
content: Text("your content")
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context, 'success'); // here you can make a callback
},
child: Text('Okay')),]
);
},
);
2, so you can write
if(result=="success"){}
3, Then, show your SnackBar after that.
Before showing SnackBar you have to add your snackbar prop like I write below
final snackbar = SnackBar(content: Text('Your context message'));
and then you can show the snackbar with your context like this
Scaffold.of(context).showSnackBar(snackbar);

Flutter showDialog with loading spinner and confirmation

In flutter, I have a showDialog() with cancel and confirm button. The confirm button will trigger a call to an API. What I need is to show a "loading..." in the showDialog window after the click and once the API call is finished to show a success or failure. How can I manage this? Or should I close the window, waiting for the reply and popup a new dialog window with success or false? Thx for any help or better suggestion. Here what I have so far:
void _showAlert(String textLabel, String action, String linkId) {
showDialog(
context: context,
//barrierDismissible: false, // on external click
builder: (_) => new AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
title: new Text(
'Please confirm:',
style: TextStyle(color: Colors.deepOrange, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
content: new Text(
textLabel,
style: new TextStyle(fontSize: 20.0),
),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: new Text('CANCEL')),
new FlatButton(
onPressed: () {
_postAction(action, linkId).then((_) => setState(() {}));
Navigator.pop(context);
},
child: new Text('I CONFIRM')),
],
));
}
You can try this,this is just the idea..
class _SampleState extends State<Sample> {
bool isSuccessFromApi = false;
bool isLoading = false;
Widget popup() {
showDialog(context: context, builder: (builder) {
return AlertDialog(
content: !isSuccessFromApi ? Container(
child: Text('Are you Sure???'),
) : Container( child: isLoading ? CircularProgressIndicator() : Text('Success'),),
actions: <Widget>[
Text('Cancel'),
InkWell(child: Text('OK'), onTap: apiCall,)
],
);
});
}
void apiCall(){
setState(() {
isLoading = true;
});
//call the api
//after success or failure
setState(() {
isLoading = false;
isSuccessFromApi = true;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: popup(),
);
}
}