flutter alert dialog for updating value - flutter

I want to update value in alert dialog i am using following:
Future showAlert() async {
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Alert in Dialog'),
content: Container(
height: 40.0,
width: 60.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_itemCount != 1
? new IconButton(
icon: new Icon(Icons.remove),
onPressed: () => setState(() => _itemCount--),
)
: new Container(),
new Text(_itemCount.toString()),
new IconButton(
icon: new Icon(Icons.add),
onPressed: () => setState(() => _itemCount++))
],
),
),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
I am calling this function in Listview Builder
GestureDetector(
child: Align(
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(Icons.add_box,
color: Color(0xFFfccd01)),
),
),
onTap: () {
showAlert();
/* FutureBuilder<String>(
future: Add_to_cart(USER_ID,
snapshot.data[index].id, "1"),
builder: (context, snapshot) {
print(snapshot.data);
});*/
},
),
But on + click my value of alert dialog is not going to update after i close and reopen it it will update but i want to update on tap of icon button.

You can use StreamBuilder to update within Dialog or that screen also on single event thru Streams

You must insert AlertDialog into Statefulll Widget
see this example:
class ShowDialog extends StatefulWidget {
#override
_ShowDialogState createState() => _ShowDialogState();
}
class _ShowDialogState extends State<ShowDialog> {
#override
Widget build(BuildContext context) {
return AlertDialog(
//your container
);
}
}
and call ShowDialog into showDialog()

Related

ValueListenableBuilder<String> widget cannot be marked as needing to build. setState() or markNeedsBuild() called during build. File_manager flutter

I am using the file_manager package. When I try to run the example code given, I get the below error. I'm not familiar with ValueNotifier and ValueListenableBuilder so I can't figure out, what seems to be the issue here.
The following assertion was thrown while dispatching notifications for ValueNotifier<String>:
setState() or markNeedsBuild() called during build.
This ValueListenableBuilder<String> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
Below is the code. I have not added a single line of my own code. It is package example code
import 'dart:io';
import 'package:file_manager/file_manager.dart';
import 'package:flutter/material.dart';
class HomeView extends StatelessWidget {
final FileManagerController controller = FileManagerController();
HomeView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
// Creates a widget that registers a callback to veto attempts by the user to dismiss the enclosing
// or controllers the system's back button
return WillPopScope(
onWillPop: () async {
if (await controller.isRootDirectory()) {
return true;
} else {
controller.goToParentDirectory();
return false;
}
},
child: Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () => createFolder(context),
icon: const Icon(Icons.create_new_folder_outlined),
),
IconButton(
onPressed: () => sort(context),
icon: const Icon(Icons.sort_rounded),
),
IconButton(
onPressed: () => selectStorage(context),
icon: const Icon(Icons.sd_storage_rounded),
)
],
title: ValueListenableBuilder<String>(
valueListenable: controller.titleNotifier,
builder: (context, title, _) => Text(title),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () async {
await controller.goToParentDirectory();
},
),
),
body: Container(
margin: const EdgeInsets.all(10),
child: FileManager(
controller: controller,
builder: (context, snapshot) {
final List<FileSystemEntity> entities = snapshot;
return ListView.builder(
itemCount: entities.length,
itemBuilder: (context, index) {
FileSystemEntity entity = entities[index];
return Card(
child: ListTile(
leading: FileManager.isFile(entity)
? const Icon(Icons.feed_outlined)
: const Icon(Icons.folder),
title: Text(FileManager.basename(entity)),
subtitle: subtitle(entity),
onTap: () async {
if (FileManager.isDirectory(entity)) {
// open the folder
controller.openDirectory(entity);
// delete a folder
// await entity.delete(recursive: true);
// rename a folder
// await entity.rename("newPath");
// Check weather folder exists
// entity.exists();
// get date of file
// DateTime date = (await entity.stat()).modified;
} else {
// delete a file
// await entity.delete();
// rename a file
// await entity.rename("newPath");
// Check weather file exists
// entity.exists();
// get date of file
// DateTime date = (await entity.stat()).modified;
// get the size of the file
// int size = (await entity.stat()).size;
}
},
),
);
},
);
},
),
)),
);
}
Widget subtitle(FileSystemEntity entity) {
return FutureBuilder<FileStat>(
future: entity.stat(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (entity is File) {
int size = snapshot.data!.size;
return Text(
"${FileManager.formatBytes(size)}",
);
}
return Text(
"${snapshot.data!.modified}",
);
} else {
return const Text("");
}
},
);
}
selectStorage(BuildContext context) {
showDialog(
context: context,
builder: (context) => Dialog(
child: FutureBuilder<List<Directory>>(
future: FileManager.getStorageList(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final List<FileSystemEntity> storageList = snapshot.data!;
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: storageList
.map((e) => ListTile(
title: Text(
"${FileManager.basename(e)}",
),
onTap: () {
controller.openDirectory(e);
Navigator.pop(context);
},
))
.toList()),
);
}
return const Dialog(
child: CircularProgressIndicator(),
);
},
),
),
);
}
sort(BuildContext context) async {
showDialog(
context: context,
builder: (context) => Dialog(
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text("Name"),
onTap: () {
controller.sortedBy = SortBy.name;
Navigator.pop(context);
}),
ListTile(
title: const Text("Size"),
onTap: () {
controller.sortedBy = SortBy.size;
Navigator.pop(context);
}),
ListTile(
title: const Text("Date"),
onTap: () {
controller.sortedBy = SortBy.date;
Navigator.pop(context);
}),
ListTile(
title: const Text("type"),
onTap: () {
controller.sortedBy = SortBy.type;
Navigator.pop(context);
}),
],
),
),
),
);
}
createFolder(BuildContext context) async {
showDialog(
context: context,
builder: (context) {
TextEditingController folderName = TextEditingController();
return Dialog(
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: TextField(
controller: folderName,
),
),
ElevatedButton(
onPressed: () async {
try {
// Create Folder
await FileManager.createFolder(
controller.getCurrentPath, folderName.text);
// Open Created Folder
controller.setCurrentPath =
controller.getCurrentPath + "/" + folderName.text;
} catch (e) {}
Navigator.pop(context);
},
child: const Text('Create Folder'),
)
],
),
),
);
},
);
}
}

Add new widget upon clicking floating action button in flutter

I am a beginner in Flutter. I am trying to add a new list item widget to screen when floating action button is pressed. How do I achieve this?
I am trying to create a list of items. When the floating action button is clicked, a dialog box is prompted and user is asked to enter details. I want to add a new list item with these user input details.
This is my input_page.dart file which I am calling in main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MedPage extends StatefulWidget {
#override
_MedPageState createState()=> _MedPageState();
}
class _MedPageState extends State<MedPage> {
Future<String>createAlertDialog(BuildContext context) async{
TextEditingController customController= new TextEditingController();
return await showDialog(context: context,builder: (context) {
return AlertDialog(
title: Text("Name of the Pill"),
content: TextField(
controller: customController,
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: (){
Navigator.of(context).pop(customController.text.toString()); // to go back to screen after submitting
}
)
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My med app'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget> [
Expanded(
child: ListView(
padding: const EdgeInsets.all(8),
children: <Widget>[
ReusableListItem(Color(0xFFd2fddf),"Name 1"),
ReusableListItem(Colors.orange,"Name 2"),
ReusableListItem(Color(0xFF57a1ab), "Name 3"),
],
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
print("Clicked");
createAlertDialog(context).then((onValue){
print(onValue);
setState(() {
});
});
},
child: Icon(Icons.add),
),
);
}
}
class ReusableListItem extends StatelessWidget {
ReusableListItem(this.colour,this.pill);
Color colour;
String pill;
#override
Widget build(BuildContext context) {
return Container(
height: 50,
margin: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: colour,
borderRadius: BorderRadius.circular(15.0)
),
child: Center(
child: Text(pill)
),
);
}
}
You don't need to change much in your code, maintain a variable that stores the values entered to be able to show them in the list. You should use Listview.builder() in order to dynamically render the items.
Here's your code:
class MedPage extends StatefulWidget {
#override
_MedPageState createState() => _MedPageState();
}
class _MedPageState extends State<MedPage> {
List<String> items = [];
Future<String> createAlertDialog(BuildContext context) async {
TextEditingController customController = new TextEditingController();
return await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Name of the Pill"),
content: TextField(
controller: customController,
),
actions: <Widget>[
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop(customController.text
.toString()); // to go back to screen after submitting
})
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My med app'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
return ReusableListItem(Color(0xFFd2fddf), items[index]);
},
itemCount: items.length,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
print("Clicked");
createAlertDialog(context).then((onValue) {
// print(onValue);
setState(() {
items.add(onValue);
});
});
},
child: Icon(Icons.add),
),
);
}
}
class ReusableListItem extends StatelessWidget {
ReusableListItem(this.colour, this.pill);
final Color colour;
final String pill;
#override
Widget build(BuildContext context) {
return Container(
height: 50,
margin: const EdgeInsets.all(8),
decoration:
BoxDecoration(color: colour, borderRadius: BorderRadius.circular(15.0)),
child: Center(child: Text(pill)),
);
}
}
Firstly you need to use ListView.builder() rather than ListView because you have dynamic content. Also you need to hold your items in a list.
// create a list before
ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
return Text(list[index]);
}
)
When you click on FloatingActionButton() you will call AlertDialog() method.
FloatingActionButton(
onPressed: (){
AlertDialog(
content: Form(), // create your form here
actions: [
// add a button here
]
)
})
This method will show a dialog(you will add a form inside of the dialog). When the user completes the form(after clicking the button) you will add a new object to the list and update the state with setState({})
onPressed: (){
setState({
// add new object to the list here
});
Navigator.pop(context); // this will close the dialog
}

Navigator.of(context).pop(); makes the whole screen disappear, not the popup

In my flutter app I want to show the popup with two buttons when user presses a button, I'm doing it with the following code:
class ProfileScreen extends StatefulWidget {
#override
_ProfileScreenState createState() {
return _ProfileScreenState();
}
}
class _ProfileScreenState extends State<ProfileScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
padding: EdgeInsets.all(16),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 400),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
...[
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
showAlertDialog(context);
},
child: Text('Remove account'),
),
),
and the code for showAlertDialog is as follows:
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = FlatButton(
child: Text("Cancel"),
onPressed: () {
Navigator.of(context).pop();
},
);
Widget continueButton = FlatButton(
child: Text("Continue"),
onPressed: () {},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text("AlertDialog"),
content: Text("Would you like to continue learning how to use Flutter alerts?"),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
It works, the popup shows correctly, but when I click cancel, popup stays up front, but the screen beneath it goes away (and it stays black). Why so? And how could I fix it? Thanks!
Navigator.of(context, rootNavigator: true).pop();

How to show pop up menu where icon button widget is clicked

I have made an alert dialog where user can update their profile details. In that with image container there is icon button widget. What I want is that when user clicks icon button, pop up menu will display with add/remove image option. Here is my code for alert dialog:
showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Edit detail'),
content: StatefulBuilder(
builder: (context, setState) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Stack(
alignment: Alignment.center,
children: [
Container(
//image container
),
GestureDetector(
// the pop up menu displays away from alert dialog
onTapDown: (TapDownDetails details) {
if (imageData != null) {
_showPopupMenu(details.globalPosition);
print('choose image/remove image');
} else {}
},
child: IconButton(icon: Icon(Icons.edit),
onPressed: () async {}),
)
]),
],
),
);
},
),
actions: <Widget>[
//action button
)],
);
},);
Here is the code for popup menu.
void _showPopupMenu(Offset offset) async {
double left = offset.dx;
double top = offset.dy;
await showMenu<String>(
context: context,
position: RelativeRect.fromLTRB(left, top, 0.0, 0.0), //position where want to show the menu on screen
items: [
PopupMenuItem<String>(
child: const Text('Add image'), value: '1'),
PopupMenuItem<String>(
child: const Text('Delete image'), value: '2'),
],
elevation: 8.0,
)
.then<void>((String itemSelected) {
if (itemSelected == null) return;
if(itemSelected == "1"){
} else {
}
});}
The menu displays outside the alert dialog. I have seen similar post with gesture detector but I can't seem to understand my mistake. Please help me and any improvements are welcome. Thanks in advance.
You can do it using the keys of AlertDialog and IconButton as the following
GlobalKey cKey = new GlobalKey();
GlobalKey pKey = new GlobalKey();
void _showPopupMenu() async {
await showMenu<String>(
context: context,
position: RelativeRect.fromRect(_getWidgetGlobalRect(pKey),_getWidgetGlobalRect(cKey)),
items: [
PopupMenuItem<String>(child: const Text('Add image'), value: '1'),
PopupMenuItem<String>(child: const Text('Delete image'), value: '2'),
],
elevation: 8.0,
).then<void>((String itemSelected) {
if (itemSelected == null) return;
if (itemSelected == "1") {
} else {}
});
}
Rect _getWidgetGlobalRect(GlobalKey key) {
RenderBox renderBox = key.currentContext.findRenderObject();
var offset = renderBox.localToGlobal(Offset.zero);
return Rect.fromLTWH(
offset.dx, offset.dy, renderBox.size.width, renderBox.size.height);
}
void showDialig() {
showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
key: cKey,
title: Text('Edit detail'),
content: StatefulBuilder(
builder: (context, setState) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Stack(alignment: Alignment.topRight, children: [
Container(
//image container
),
GestureDetector(
// the pop up menu displays away from alert dialog
onTapDown: (TapDownDetails details) {
_showPopupMenu();
},
child: IconButton(
key: pKey,
icon: Icon(Icons.edit),
onPressed: () async {}),
)
]),
],
),
);
},
),
actions: <Widget>[
//action button
],
);
},
);
}
If you'r using GestureDetector, there's a easy way i just did, Just put a GestureDetector inside another one, then set the onTap action if that's your case on both GestureDetector's, with the diference that in one you are gonna put an action, an in the other one you can just leave it empty, just like this.
GestureDetector(
onTap: () { //The Gesture you dont want to afect the rest
Navigator.pop(context);
},
child: Container(
color: Colors.transparent,
child:GestureDetector(
onTap: () {}, //This way is not going to afect the inside widget
child: Container() //your widget
)
)
)

Snackbar in SimpleDialog Flutter

I faced an error code below when adding a snackbar to an on-pressed method in my Simpledialog.
[Scaffold.of() called with a context that does not contain a Scaffold.]
I would like to seek your advice on how to provide the correct context to resolve it.
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new AlertApp()));
}
class AlertApp extends StatefulWidget {
#override
_AlertAppState createState() => _AlertAppState();
}
class _AlertAppState extends State<AlertApp> {
SimpleDialog _simdalog;
void sDialog(){
_simdalog = new SimpleDialog(
title: new Text("Add To Shopping Cart"),
children: <Widget>[
new SimpleDialogOption(
child: new Text("Yes"),
onPressed: (){
final snackBar = SnackBar(content: Text('Purchase Successful'));
Scaffold.of(context).showSnackBar(snackBar);
},
),
new SimpleDialogOption(
child: new Text("Close"),
onPressed:() {Navigator.pop(context);},
),
],
);
showDialog(context: context, builder: (BuildContext context){
return _simdalog;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
child: new Text("Add to Shopping Cart [Simple]"),
onPressed:(){
sDialog();
}),
],
),
),
);
}
}
Solution 1: as Mazin Ibrahim mentioned in comments Scaffold.of() called with a context that does not contain a Scaffold
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
...
Scaffold(
key: _scaffoldKey,
...
onPressed: () {
_scaffoldKey.currentState.showSnackBar(
SnackBar(
content: Text('Purchase Successful'),
duration: Duration(seconds: 3),
));
}
Solution 2:
With package flushbar, you can also display notification on top
Flushbar link : https://github.com/AndreHaueisen/flushbar
Another suggestion to use flushbar How to show snackbar after navigator.pop(context) in Flutter?
Flushbar(
title: "Hey Ninja",
message: "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
flushbarPosition: FlushbarPosition.TOP,
flushbarStyle: FlushbarStyle.FLOATING,
reverseAnimationCurve: Curves.decelerate,
forwardAnimationCurve: Curves.elasticOut,
backgroundColor: Colors.red,
boxShadows: [BoxShadow(color: Colors.blue[800], offset: Offset(0.0, 2.0), blurRadius: 3.0)],
backgroundGradient: LinearGradient(colors: [Colors.blueGrey, Colors.black]),
isDismissible: false,
duration: Duration(seconds: 4),
icon: Icon(
Icons.check,
color: Colors.greenAccent,
),
mainButton: FlatButton(
onPressed: () {},
child: Text(
"CLAP",
style: TextStyle(color: Colors.amber),
),
),
showProgressIndicator: true,
progressIndicatorBackgroundColor: Colors.blueGrey,
titleText: Text(
"Hello Hero",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0, color: Colors.yellow[600], fontFamily: "ShadowsIntoLightTwo"),
),
messageText: Text(
"You killed that giant monster in the city. Congratulations!",
style: TextStyle(fontSize: 18.0, color: Colors.green, fontFamily: "ShadowsIntoLightTwo"),
),
)..show(context);
You can return a bool from the showDialog method and use that to determine whether to show the snackbar:
void main() {
runApp(MaterialApp(
home: AlertApp(),
));
}
class AlertApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MyShoppingButton(),
],
),
),
);
}
}
// Separate out the button from _AlertAppState so that the call to
// showSnackBar comes from a different BuildContext
class MyShoppingButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("Add to Shopping Cart [Simple]"),
// Use an async onPressed method so that we can wait for the
// result from the dialog before deciding whether to show the snackbar
onPressed: () async {
bool result = await showDialog<bool>(
context: context,
builder: (BuildContext context) {
return MyShoppingDialog();
},
);
// Check if result is null below as Flutter will throw Exception if
// tries determining whether to enter an if branch will a null boolean
if (result != null && result) {
final snackBar = SnackBar(content: Text('Purchase Successful'));
Scaffold.of(context).showSnackBar(snackBar);
}
},
);
}
}
class MyShoppingDialog extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text("Add To Shopping Cart"),
children: <Widget>[
SimpleDialogOption(
child: Text("Yes"),
onPressed: () {
// Pop with a result of true so that MyShoppingButton
// knows to show snackbar. In any other case
// (including the user dismissing the dialog), MyShoppingButton
// null receive null, and so will not show the snackbar
Navigator.of(context).pop(true);
},
),
SimpleDialogOption(
child: Text("Close"),
onPressed: () {
Navigator.pop(context);
},
),
],
);
}
}
You should create a Scaffold widget inside of showDialog and a Builder widget as child of the Scaffold and pass context as parameter.
void sDialog({BuildContext context}){
_simdalog = new SimpleDialog(
title: new Text("Add To Shopping Cart"),
children: <Widget>[
new SimpleDialogOption(
child: new Text("Yes"),
onPressed: (){
final snackBar = SnackBar(content: Text('Purchase Successful'));
Scaffold.of(context).showSnackBar(snackBar);
},
),
new SimpleDialogOption(
child: new Text("Close"),
onPressed:() {Navigator.pop(context);},
),
],
);
showDialog(context: context, builder: (BuildContext context){
return GestureDetector(
onTap: (){Navigator.of(context).pop();},
child: Scaffold(
body: Builder(
builder: (context){
return _simdalog(context: context);
}
),
),);
});
}