I have been trying to make a dialog popup when pressing a popupmenubutton, however it does not seem to work, I run the app in debug mode through chrome as my Laptop is unable to run in debug mode through androud studio emulator.
child: Scaffold(
resizeToAvoidBottomInset: true,
backgroundColor: Color.fromRGBO(31, 31, 47, 1),
appBar: AppBar(
actions: [
PopupMenuButton<String>(
padding: EdgeInsets.all(0),
onSelected: (value) {
print(value);
},
itemBuilder: (BuildContext context) {
return [
PopupMenuItem(
child: Text('Cancel'),
onTap: () {BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
backgroundColor: Color(Color.getAlphaFromOpacity(0.5)),
child: _dialogContent(),
)
);
},
),
];
},
),
], ),
Widget _dialogContent() {return SimpleDialog(
title: Text('Enter Cancel Code'),
children: <Widget>[
// TextFormField()
ElevatedButton(
onPressed: () => Navigator.pop(context, false),
child: Text('Okay')),
],);}
}
the reason why I doesn't work is because you didn't specify a value to your popupMenuItem thats why your onSelected property dont know what to do when you tapped on an item, so to make it work you need to do like so
PopupMenuButton<int>(
padding: const EdgeInsets.all(0),
onSelected: (value) {
if (value == 0) {
showDialog(context: context, builder: (context) => _dialogContent,);
}
},
itemBuilder: (BuildContext context) {
return [
const PopupMenuItem<int>(
value: 0,
child: Text('Show dialog'),
),
];
},
),
:
if you want to use the onTap property of the PopupMenuItem, you need to delay your showDialog because when you call the onTap, it is triggering the Navigator.of(context).pop(). So it looks like nothing is happening when you try to show dialog because its being popped immediately. So to make it work, you need to do it like so:
PopupMenuButton<int>(
padding: const EdgeInsets.all(0),
itemBuilder: (BuildContext context) {
return [
const PopupMenuItem<int>(
value: 0,
child: Text('Show dialog'),
onTap: ()=> Future.delayed(Duration(milliseconds:1),(){
showDialog(context:context, builder: (context)=> _dialogContent)
})
),
];
},
),
Related
I want to call 2 API endpoints at the same time, and I try to use MultiBlocProvider but it does not work, and here is some code.
https://pub.dev/documentation/flutter_bloc/latest/flutter_bloc/MultiBlocProvider-class.html
how could I use it in the correct ways, or I should another ways.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("${allTranslations.text('filterHistory')}"),
),
body: MultiBlocProvider(
providers: [
BlocProvider<HistoryBloc>(
create: (BuildContext context) => historyBloc,
child: BlocListener<HistoryBloc, HistoryState?>(
listener: (context, state) {
print('State loading ${state.runtimeType}');
if(state is HistoryStateSuccess) {
nextPageData(state.historyResModel);
}
if (state is HistoryStateError) {
appShowSnackBar(context, state.runtimeType.toString());
}
},
),
),
BlocProvider<PackingBloc>(
create: (BuildContext context) => packingBloc,
child: BlocListener<PackingBloc, PackingState?>(
listener: (context, state) {
print('State loading ${state.runtimeType}');
if(state is GetPackingStateSeccess) {
getParkingList(state.packingList);
}
if (state is GetPackingStateError) {
appShowSnackBar(context, state.runtimeType.toString());
}
},
),
),
],
child: _bodyBuilder()
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
heroTag: 'selectDateRange',
elevation: 10.0,
child: const Icon(Icons.calendar_today),
onPressed: () => {_selectDateRange()},
),
const SizedBox(height: 6),
FloatingActionButton(
heroTag: 'onMapTypeButtonPressed',
elevation: 10.0,
child: const Icon(Icons.map),
onPressed: _onMapTypeButtonPressed,
),
const SizedBox(height: 6),
FloatingActionButton(
heroTag: 'vehicleLocation',
child: const Icon(Icons.car_repair_sharp),
onPressed: () => _controller.animateCamera(
CameraUpdate.newCameraPosition(VehicleLocationPint())),
),
const SizedBox(height: 6),
FloatingActionButton(
heroTag: 'inittialCameraPosition',
elevation: 10.0,
child: const Icon(Icons.gps_fixed),
onPressed: () => _controller.animateCamera(
CameraUpdate.newCameraPosition(_inittialCameraPosition())),
),
],
),
);
};
Thanks you for your answer.
I try to manage a state "playerList" in two screens:
The code below shows how the provider was built
class PlayerList with ChangeNotifier {
List<Player> _playerList = [];
List<Player> get playerList {
return [..._playerList];
}
void addPlayer(Player player) {
_playerList.add(player);
notifyListeners();
}
void deletePlayer(int index) {
_playerList.removeAt(index);
notifyListeners();
}
}
In screen 1, everything works perfect. However, when I do the same, but then within an AlertDialog, the updated value only shows after closing the dialog and opening it again.
The code below shows the relevant part of the build method.
#override
Widget build(BuildContext context) {
PlayerList _playerListData = Provider.of<PlayerList>(context);
List<Player> _playerList = _playerListData.playerList;
return WillPopScope(
onWillPop: () async => showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'Are you sure you want to leave? The current game will be lost.'),
actions: <Widget>[
ElevatedButton(
child: Text('Yes'),
onPressed: () => Navigator.of(context).pop(true)),
ElevatedButton(
child: Text('No'),
onPressed: () => Navigator.of(context).pop(false)),
])),
child: Scaffold(
backgroundColor: Color(0xFF6ca0dc),
body: Center(
child: Column(children: [
_playChallenge(_playerList),
]),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Player List'),
content: Column(mainAxisSize: MainAxisSize.min, children: [
Container(
height: MediaQuery.of(context).size.height *
0.5, // Change as per your requirement
width: MediaQuery.of(context).size.width *
0.5, // Change as per your requirement
child: ListView.builder(
shrinkWrap: true,
itemCount: _playerList.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(
_playerList[index].name,
style: Theme.of(context).textTheme.headline6,
),
trailing: IconButton(
icon: Icon(Icons.delete),
color: Theme.of(context).errorColor,
onPressed: () =>
_playerListData.deletePlayer(index),
));
},
),
),
How can I solve this issue?
I solved the problem by adding a StatefulBuilder and using the Provider.of inside the statefulbuilder. See:
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'Are you sure you want to leave? The current game will be lost.'),
actions: <Widget>[
ElevatedButton(
child: Text('Yes'),
onPressed: () => Navigator.of(context).pop(true)),
ElevatedButton(
child: Text('No'),
onPressed: () => Navigator.of(context).pop(false)),
])),
child: Scaffold(
backgroundColor: Color(0xFF6ca0dc),
body: Center(
child: Column(children: [
_playChallenge(Provider.of<PlayerList>(context).playerList),
]),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(builder: (context, setState) {
PlayerList _playerListData =
Provider.of<PlayerList>(context);
List<Player> _playerList = _playerListData.playerList;
return AlertDialog(
title: Text('Player List'),
content:
Column(mainAxisSize: MainAxisSize.min, children: [
Container(
height: MediaQuery.of(context).size.height *
0.5, // Change as per your requirement
width: MediaQuery.of(context).size.width *
0.5, // Change as per your requirement
child: ListView.builder(
shrinkWrap: true,
itemCount: _playerList.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(
_playerList[index].name,
style:
Theme.of(context).textTheme.headline6,
),
trailing: IconButton(
icon: Icon(Icons.delete),
color: Theme.of(context).errorColor,
onPressed: () =>
_playerListData.deletePlayer(index),
));
},
),
),
I need to separate my dialog to separate widgets an call from anywhere on some button click. Now my code looks like:
import 'package:flutter/material.dart';
class PlacesListScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return _Map();
}
}
class _Map extends StatefulWidget {
#override
_MapState createState() => _MapState();
}
class _MapState extends State<_Map> {
final _titleController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('App name'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(
"Dialog title",
textAlign: TextAlign.center,
),
content: TextField(
decoration: InputDecoration(labelText: 'Title'),
controller: _titleController,
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text("No"),
),
FlatButton(
onPressed: () {
return showDialog(
context: context,
builder: (ctx) => StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
return AlertDialog(
title: Text(
"Second subdialog title",
textAlign: TextAlign.center,
),
content: Container(
width: 150,
height: 200,
decoration: BoxDecoration(
border:
Border.all(width: 1, color: Colors.grey),
),
child: Text('Here will be file'),
alignment: Alignment.center,
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text("No"),
),
FlatButton(
onPressed: () => Text('Here I will work with state'),
child: Text("Yes"),
),
],
);
}),
);
},
child: Text("Yes"),
),
],
),
);
},
),
],
),
body: Text('App body here'),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FloatingActionButton(
onPressed: () => Text('Here will be called dialog function'),
child: Icon(Icons.add, color: Colors.white),
backgroundColor: Colors.green,
heroTag: 'mapZoomIn',
),
],
),
);
}
}
Now how I can this part of code (calling dialog) separate to another widget and call then using created widget.
showDialog(
context: context,
builder: (ctx) => StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
return AlertDialog(
title: Text(
"Second subdialog title",
textAlign: TextAlign.center,
),
content: Container(
width: 150,
height: 200,
decoration: BoxDecoration(
border:
Border.all(width: 1, color: Colors.grey),
),
child: Text('Here will be file'),
alignment: Alignment.center,
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text("No"),
),
FlatButton(
onPressed: () => Text('Here I will work with state'),
child: Text("Yes"),
),
],
);
}),
);
Firstly, you can refactor your StatefulBuilder(builder: (BuildContext context, StateSetter setState) { return AlertDialog(... into one separate widget like MyFancyDialog extends StatefulWidget. Then just call showDialog(..., MyFancyWidget()) when you need it.
Secondly, if you do not want the first method, you can extract the whole showDialog(...) into a function like void showMyFancyDialog() { showDialog... }. Is it also OK, since in dart functions are first class.
Did you try to create a new Stateful widget class at the bottom of your code and try to call that widget? If you didn't try that,
Go to under of your code and create a new Stateful class. (Let's say that myDialog)
Add the code that you want to call, inside of your "myDialog" widget
Go for where you want to call that code and try to call that class.
Hope this works for you.
Create a separate file. Import material package.
then create "Widget myDialog(context){ // paste your showDialog code here }"
Now your myDialog widget has been ready for using anywhere on whole project just import and call it.
When you call it pass BuildContext in its argument.
Its work for me.
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,
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