Display pop up menu when icon button widget is clicked Flutter - flutter

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>(
builder: (BuildContext context) {
return AlertDialog(
title: Text('Update details'),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))),
content: StatefulBuilder(
builder: (context, setState) { return Container(
width: 400,
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Stack(
alignment: Alignment.center,
children: [
Container(
width: 100.0,
height: 100.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
image: new DecorationImage(
fit: BoxFit.cover,
colorFilter: new ColorFilter.mode(Colors.black.withOpacity(0.2), BlendMode.darken),
image: data != null ? MemoryImage(data) : AssetImage("web/icons/contactsDefaultImage.png")
)
)
),
IconButton(icon: Icon(Icons.edit), onPressed: () async {
//display option here
_showPopupMenu();
})
]),
Container(
child: TextFormField(
decoration: InputDecoration(
labelText: 'name'
),
),
),
TextFormField(
decoration: InputDecoration(
labelText: 'email'
),
),
],
),
),
);},
),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(child: Text('Save'),
onPressed: () {
// save
},
)
],
);
},
);
I tried to user showMenu for that. But as the position has to be hard-coded I don't was to use it.
what I tried:
void _showPopupMenu() async {
await showMenu(
context: context,
position: RelativeRect.fromLTRB(100, 100, 100, 100),
items: [
PopupMenuItem(
child: Text("add"),
),
PopupMenuItem(
child: Text("remove"),
),
],
elevation: 8.0,
);
}
Now, what i want to know is how can i display it where the icon-button is tapped (without hard-coding the value). And is there another way to do it .i.e without using showMenu.

You can write a method like this and call it on your icon button's onPressed
showPopupMenu(){
showMenu<String>(
context: context,
position: RelativeRect.fromLTRB(25.0, 25.0, 0.0, 0.0), //position where you want to show the menu on screen
items: [
PopupMenuItem<String>(
child: const Text('menu option 1'), value: '1'),
PopupMenuItem<String>(
child: const Text('menu option 2'), value: '2'),
PopupMenuItem<String>(
child: const Text('menu option 3'), value: '3'),
],
elevation: 8.0,
)
.then<void>((String itemSelected) {
if (itemSelected == null) return;
if(itemSelected == "1"){
//code here
}else if(itemSelected == "2"){
//code here
}else{
//code here
}
});
}
Edit: (to show menu at the position where user tapped)
We can have a method like so -
void showPopUpMenuAtTap(BuildContext context, TapDownDetails details) {
showMenu(
context: context,
position: RelativeRect.fromLTRB(
details.globalPosition.dx,
details.globalPosition.dy,
details.globalPosition.dx,
details.globalPosition.dy,
),
// other code as above
);
}
and use it with GestureDetector like so -
GestureDetector(
child: const Icon(Icons.menu),
onTapDown: (details) => showPopUpMenuAtPosition(context, details),
);

Solution if you wish to re-use your button and not a Gesture detector:
Create a key and assign your button the key. Then:
TextButton(
key: _accKey,
text: "Account",
onPressed: () {
final RenderBox renderBox =
_accKey.currentContext?.findRenderObject() as RenderBox;
final Size size = renderBox.size;
final Offset offset = renderBox.localToGlobal(Offset.zero);
showMenu(
context: context,
position: RelativeRect.fromLTRB(
offset.dx,
offset.dy + size.height,
offset.dx + size.width,
offset.dy + size.height),
items: [
PopupMenuItem<String>(
child: const Text('menu option 1'), value: '1'),
PopupMenuItem<String>(
child: const Text('menu option 2'), value: '2'),
PopupMenuItem<String>(
child: const Text('menu option 3'), value: '3'),
]);
}),

what you are looking to is showdialog and alertdialog.
Void<String> testdialog(BuildContext context) {
return showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return StatefulBuilder(builder: (context, setState) {
return AlertDialog(
title: ....

There are a lot of options you can choose:
You can use:
Banner
Card
Dialog
PopupMenuButton
Or even BottomSheet
I hope it will help

Related

how to put GestureDetector inside Container alert

I wanna put GestureDetector with container alert but it show error. anyone know how to make this code works? Here the code below which i try to put GestureDetector for the alert container.
Without GestureDetector it works fine but i wanna make whole screen touch able to return to other page.
showPopup(BuildContext context) {
// set up the buttons
// ignore: deprecated_member_use
// set up the AlertDialog
GestureDetector(
Container alert = Container(
child: Stack(
children: <Widget>[
if (controllers!.isNotEmpty)
CarouselSlide2(
controllers: controllers!,
),
Padding(
padding: const EdgeInsets.only(top:688.0,left: 90),
child: GestureDetector(
onTap: () async {
isPop = false;
Navigator.pop(context);
_checkTimer();
},
// child: Icon(Icons.arrow_back,color: Colors.white,size: 100,),
child: DefaultTextStyle(
style: TextStyle(color: Colors.white,fontSize: 30),
child: Text("Tap to return",),
)
),
)
],
)));
// show the dialog
showDialog(
barrierDismissible: true,
context: context,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async {
const shouldPop = true;
isPop = false;
Navigator.pop(context);
_checkTimer();
return shouldPop;
},
child: alert);
},
);
}
You are using widget in a wrong way, try this:
Widget alert = GestureDetector(
onTap: () {
print("tap");
},
child: Container(
child: Stack(
children: <Widget>[
if (controllers!.isNotEmpty)
CarouselSlide2(
controllers: controllers!,
),
Padding(
padding: const EdgeInsets.only(top: 688.0, left: 90),
child: GestureDetector(
onTap: () async {
isPop = false;
Navigator.pop(context);
_checkTimer();
},
// child: Icon(Icons.arrow_back,color: Colors.white,size: 100,),
child: DefaultTextStyle(
style: TextStyle(color: Colors.white, fontSize: 30),
child: Text(
"Tap to return",
),
)),
)
],
)),
)

Flutter toast dialog

How can I can make such toast dialog in flutter? (create service, create venue)
Interactive example:
https://files.fm/f/dw6qvmznc
You can use PopupMenuButton
PopupMenuButton<String>(
icon: const Icon(Icons.add_circle_outline_outlined),
color: Colors.grey, // background color
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: "create service",
onTap: () {},
child: const Center(
child: Text(
'create service',
),
),
),
PopupMenuItem<String>(
value: "create venue",
child: const Center(
child: Text(
'create venue',
),
),
onTap: () {},
),
],
)
Here's a neat package that takes care of that with different styles as well.
StarMenu(
onStateChanged: (state) {
print('State changed: $state');
},
params: StarMenuParameters(
shape: MenuShape.linear,
linearShapeParams: LinearShapeParams(
angle: 270,
space: 30,
alignment: LinearAlignment.center),
onItemTapped: (index, controller) {
// don't close if the item tapped is not the ListView
if (index != 1) controller.closeMenu();
}),
// lazyItemsLoad let you build menu entries at runtime
lazyItems: () async {
return [
Container(
color: Color.fromARGB(255, Random().nextInt(255),
Random().nextInt(255), Random().nextInt(255)),
width: 60,
height: 40,
),
Container(
width: 150, // CHANGE WIDTH
height: 200, // CHANGE HEIGHT
child: Card(
elevation: 6,
margin: EdgeInsets.all(6),
child: Column(
children: [
// ADD YOUR ACTIONS HERE
]
),
),
),
),
];
},
child: FloatingActionButton( // THIS WILL BE YOUR BUTTON YOU WANT TO SHOW THE MENU FROM
onPressed: () {
print('FloatingActionButton Menu1 tapped');
},
child: Icon(Icons.looks_one),
),
),
You can simple Dialog of CupertinoDailog...
onPress:(){
showCupertinoDialog(context: context,
builder: (context) {
return CupertinoAlertDialog(
content: Column(
children: [
Text("Option 1"),
Text("Option 1"),
Text("Option 1"),
],
),
);})
}

Flutter - whitespaces displaying for image in portrait and landscape

I keep seeing lot of white space if I do landscape or portrait within my image. I do need the slidable so didn't want to tweak the code too much, but I do want it to look representable
is there something wrong with my code?
I did add a picture this is happening in both landscape and portrait mode
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: new Text(
"1 Alif-laam-meem آلم, Pg2",
style: new TextStyle(color: styling.appBarTextcolor),
),
leading: new IconButton(
icon: new Icon(styling.appBarBackArrowIcon),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NavDrawer(),
));
})),
body: LayoutBuilder(builder:
(BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: Stack(children: <Widget>[
new Slidable(
delegate: new SlidableDrawerDelegate(),
actionExtentRatio: styling.sizeofenglishandforward,
child: SafeArea(
top: true,
bottom: true,
right: true,
left: true,
child: new Container(
child: new Image.asset(
"test/assets/Para 1 - Alif-laam-meem no color/quranpg2-1.png",
// fit: BoxFit.fitidth,
fit: BoxFit.cover,
),
),
),
actions: <Widget>[
new IconSlideAction(
caption: styling.englishIconText,
color: styling.englishIconColorstripe,
icon: styling.englishIcon,
foregroundColor: styling.englishIconColor,
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Changepg2topg2Color()),
);
}),
new IconSlideAction(
caption: styling.forwardIconText,
color: styling.forwardIconColorstripe,
icon: styling.forwardIcon,
foregroundColor: styling.forwardIconColor,
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => Changepg2topg3()),
// );
// }
),
// ),
],
secondaryActions: <Widget>[
new IconSlideAction(
caption: styling.backIconText,
color: styling.backIconColorstripe,
icon: styling.backIcon,
foregroundColor: styling.backIconColor,
// onTap: () => _showSnackBar('More'),
),
new IconSlideAction(
caption: styling.arabicIconText,
color: styling.arabicIconColorstripe,
icon: styling.arabicIcon,
foregroundColor: styling.arabicIconColor,
// onTap: () =>
),
],
),
]));
}));
}
}
SafeArea is preventing your image to go at some restricted areas of screen like, underneath the notch area. Or for some devices there are NavigationKeys in the bottom of screen. Try removing these
Cheers :)
child: SafeArea(
top: true,
bottom: true,
right: true,
left: true,

Flutter modal bottom sheet full height

I was trying things out with ModalBottomSheet. How can I achieve 90% height of device screen size for modal sheet. I did mediaquery but still it does not give me more than half of the screen size. How do I solve this?
Here is the code:
class _TestFileState extends State<TestFile> {
modalSheet() {
showModalBottomSheet(
context: context,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0)),
),
builder: (context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
ListTile(
leading: Icon(Icons.email),
title: Text('Send email'),
onTap: () {
print('Send email');
},
),
ListTile(
leading: Icon(Icons.phone),
title: Text('Call phone'),
onTap: () {
print('Call phone');
},
),
],
);
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Center(child: Text('Testing Modal Sheet')),
),
body: Center(
child: InkWell(
onTap: () {
modalSheet();
},
child: Container(
color: Colors.indigo,
height: 40,
width: 100,
child: Center(
child: Text(
'Click Me',
style: TextStyle(color: Colors.white),
),
)),
),
),
),
);
}
}
Here is the output:
you have to pass isScrollControlled: true and use mediaquery as given below
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) {
return Container(
height: MediaQuery.of(context).size.height * 0.5,
color: Colors.red,
//height: MediaQuery.of(context).size.height,
);
});
As I remember that's a restriction about the native implementation of Flutter modal bottom sheet.
You can use the package modal_bottom_sheet to achieve that.
Install:
dependencies:
modal_bottom_sheet: ^0.2.2
And minimal example:
showMaterialModalBottomSheet(
context: context,
expand: true, //this param expands full screen
builder: (context, scrollController) => Container(),
)

Flutter create new instance of class on button tap and update values

I am creating a feature for users to be able to add occasions (title and date) to a list in Flutter. I have set up the features but i'm struggling to understand how to firstly, create a new instance of my DateToRemember class when my "add button" is pressed and then, when a title text value is entered, and a date selected from my datepicker, update that instance with those values. Then users will be able to click a submit button and their list updated.
Here is my date to remember model:
class DateToRemember {
String title;
DateTime date;
DateToRemember(this.title, this.date);
}
And the datestoremember page code:
class DatesToRemember extends StatefulWidget {
#override
_DatesToRememberState createState() => _DatesToRememberState();
}
class _DatesToRememberState extends State<DatesToRemember> {
TextEditingController _titleController = new TextEditingController();
DateTime startDate = DateTime.now();
DateTime pickedDate = DateTime.now();
DateFormat formatter = DateFormat('dd/MM/yyyy');
_DatesToRememberState();
Future displayDatePicker(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: startDate,
firstDate: startDate,
lastDate: DateTime(DateTime.now().year + 100));
if (picked != null)
setState(() {
pickedDate = picked;
print(DateFormat('dd/MM/yyyy').format(pickedDate).toString());
});
}
final List<DateToRemember> occasions = [
DateToRemember("Occasion 1", DateTime.now()),
DateToRemember("Occasion 2", DateTime.now()),
DateToRemember("Occasion 3", DateTime.now()),
];
String input;
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: GestureDetector(
onTap: () => Navigator.pop(context),
child: Icon(Icons.arrow_back)),
title: Text('Dates to Remember'),
),
backgroundColor: Colors.white,
body: Center(
child: Column(children: [
SizedBox(
height: 10.0,
),
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Text(
"It can be difficult to ",
style: TextStyle(fontFamily: FontNameDefault),
),
),
SizedBox(
height: 50.0,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.centerRight,
child: FloatingActionButton(
backgroundColor: kPrimaryColor,
child: Icon(Icons.add),
onPressed: () {
// CREATE NEW INSTANCE OF DATETOREMEMBER CLASS
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Add occasion'),
content: Container(
height: 150.0,
child: Column(
children: [
TextField(
decoration: InputDecoration(
hintText: 'Occasion Title'),
//UPDATE TITLE IN CLASS WITH INPUT
//
),
TextField(
decoration: InputDecoration(
hintText: 'Pick Date'),
onTap: () async {
await displayDatePicker(context);
//SELECT DATE AND UPDATE INSTANCE OF CLASS
},
),
FlatButton(
child: Text('Submit'),
onPressed: () {
//Navigator.of(context).pop();
},
),
],
),
),
);
});
}),
),
),
Container(
height: MediaQuery.of(context).size.height * 0.6,
width: MediaQuery.of(context).size.width * 0.9,
decoration:
BoxDecoration(border: Border.all(color: Colors.black26)),
child: occasions.isEmpty
? Center(
child: Text(
'Add an occasion',
style: TextStyle(color: Colors.black),
))
: ListView.builder(
itemCount: occasions.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
direction: DismissDirection.endToStart,
onDismissed: (direction) {
occasions.removeAt(index);
Scaffold.of(context).showSnackBar(new SnackBar(
content: Text('Occasion Removed'),
duration: Duration(seconds: 5),
));
},
key: UniqueKey(),
child: Card(
elevation: 8.0,
margin: EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: ListTile(
title: Text(occasions[index].title),
subtitle: Text(DateFormat('dd/MM/yyyy')
.format(occasions[index].date)
.toString()),
trailing: IconButton(
icon: Icon(
Icons.delete,
color: Colors.red,
),
onPressed: () {
setState(() {
occasions.removeAt(index);
Scaffold.of(context)
.showSnackBar(new SnackBar(
content: Text('Occasion Removed'),
duration: Duration(seconds: 5),
));
});
},
),
),
),
);
}),
)
]),
));
}
}
I'm still a flutter/dart novice so not entirely sure if what i'm asking is the best way to achieve what I want, so open to new ideas also. Thanks.