Related
I am trying to get the Text value of the selected container in a GridView. I am able to print the selected index. But I need to get the text value of the selected container so that I can save it in sharedPreferences and later get it and store it on the server.
Please assist. Your help will be gladly appreciated.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class QuickSurveyScreen extends StatefulWidget {
const QuickSurveyScreen({super.key});
#override
State<QuickSurveyScreen> createState() => _QuickSurveyScreenState();
}
class _QuickSurveyScreenState extends State<QuickSurveyScreen> {
int selectedIndex = -1;
List<String> gasUsage = [
"Heater",
"Stove",
"Geyser",
"Fireplace",
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(top: 120.0, right: 30, left: 30),
child: Column(
children: const [
Text(
"What do you use \nLP Gas for?",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 35, fontWeight: FontWeight.bold),
),
Padding(
padding: EdgeInsets.symmetric(
vertical: 16.0,
horizontal: 20,
),
child: Text(
"Please choose one or more options from below",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
),
),
),
],
),
))
];
},
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
Expanded(
child: GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
),
physics: const NeverScrollableScrollPhysics(),
itemCount: gasUsage.length,
itemBuilder: (BuildContext context, int index) {
return gasUsageContainer("", gasUsage[index], index);
}),
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 65.0),
backgroundColor: const Color(0xFFF0A202),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(26.0),
),
textStyle: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
child: const Text('Continue'),
),
],
),
),
),
),
);
}
gasUsageContainer(String image, String name, int index) {
return GestureDetector(
onTap: () {
setState(() {
if (selectedIndex == index) {
selectedIndex = -1;
} else {
selectedIndex = index;
}
if (kDebugMode) {
print(selectedIndex);
}
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: selectedIndex == index
? Colors.amber.shade50
: Colors.grey.shade200,
border: Border.all(
color: selectedIndex == index
? Colors.amber
: Colors.blue.withOpacity(0),
width: 2.0,
),
borderRadius: BorderRadius.circular(22.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: selectedIndex == index
? Colors.amber.shade800
: Colors.black,
),
)
]),
),
);
}
}
You can get it from List with index. Checkout the change I did for onTap in gasUsageContainer
onTap: () {
setState(() {
String? selected;
if (selectedIndex == index) {
selectedIndex = -1;
selected = null;
} else {
selectedIndex = index;
selected = gasUsage[index];//Here you get the value
}
if (kDebugMode) {
print('selectedIndex:$selectedIndex, selected: $selected');
}
});
}
Edited
class _QuickSurveyScreenState extends State<QuickSurveyScreen> {
final selectedItems = <String>[];
...
gasUsageContainer(String image, String name, int index) {
String item = gasUsage[index];
return GestureDetector(
onTap: () {
setState(() {
if (selectedItems.contains(item)) {
selectedItems.remove(item);
} else {
selectedItems.add(item);
}
if (kDebugMode) {
print('selectedItems: $selectedItems');
}
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: selectedItems.contains(item)
? Colors.amber.shade50
: Colors.grey.shade200,
border: Border.all(
color: selectedItems.contains(item)
? Colors.amber
: Colors.blue.withOpacity(0),
width: 2.0,
),
borderRadius: BorderRadius.circular(22.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: selectedItems.contains(item)
? Colors.amber.shade800
: Colors.black,
),
)
]),
),
);
}
}
I am working on dropdownmenu items where in the drop-down menu item there are several checkboxes but any of the checkboxes on hover don't give on tap cursor permission.
This is a very strange thing I found out as I have already used the checkbox before but this type of error I didn't receive.
I think maybe the problem is in dropdownmenu.
I have also included the video for better understanding of my problem.
my code :-
Container(
width: 160,
//margin: const EdgeInsets.only(top: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5), color: Colors.white),
child: ListTileTheme(
contentPadding: EdgeInsets.all(0),
dense: true,
horizontalTitleGap: 0.0,
minLeadingWidth: 0,
child: ExpansionTile(
iconColor: primaryBackgroundLightGrey,
title: Text(
listOFSelectedItem.isEmpty
? "Project type"
: listOFSelectedItem[0],
style: t5O40),
children: <Widget>[
Container(
height: 10,
color: primaryBackgroundLightGrey,
),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.listOFStrings.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Container(
height: 10,
),
Container(
margin: const EdgeInsets.only(bottom: 8.0),
child: _ViewItem(
item: widget.listOFStrings[index],
selected: (val) {
selectedText = val;
if (listOFSelectedItem.contains(val)) {
listOFSelectedItem.remove(val);
} else {
listOFSelectedItem.add(val);
}
widget.selectedList(listOFSelectedItem);
setState(() {});
},
itemSelected: listOFSelectedItem
.contains(widget.listOFStrings[index])),
),
],
);
},
),
],
),
),
),
class _ViewItem extends StatelessWidget {
String item;
bool itemSelected;
final Function(String) selected;
_ViewItem(
{required this.item, required this.itemSelected, required this.selected});
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Padding(
padding: EdgeInsets.only(
left: size.width * .015,
),
child: Row(
children: [
SizedBox(
height: 2,
width: 2,
child: Checkbox(
value: itemSelected,
onChanged: (val) {
selected(item);
},
hoverColor: Colors.transparent,
checkColor: Colors.white,
activeColor: Colors.grey),
),
SizedBox(
width: size.width * .010,
),
Text(item, style: t3O60),
],
),
);
}
}
You can adapt the example to your own code
dropdownBuilder: _customDropDownExample,
popupItemBuilder: _customPopupItemBuilderExample,
Widget _customDropDownExample(
BuildContext context, UserModel? item, String itemDesignation) {
if (item == null) {
return Container();
}
return Container(
child: (item.avatar == null)
? ListTile(
contentPadding: EdgeInsets.all(0),
leading: CircleAvatar(),
title: Text("No item selected"),
)
: ListTile(
contentPadding: EdgeInsets.all(0),
leading: CircleAvatar(
// this does not work - throws 404 error
// backgroundImage: NetworkImage(item.avatar ?? ''),
),
title: Text(item.name),
subtitle: Text(
item.createdAt.toString(),
),
),
);
After that
Widget _customPopupItemBuilderExample(
BuildContext context, UserModel item, bool isSelected) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 8),
decoration: !isSelected
? null
: BoxDecoration(
border: Border.all(color: Theme.of(context).primaryColor),
borderRadius: BorderRadius.circular(5),
color: Colors.white,
),
child: ListTile(
selected: isSelected,
title: Text(item.name),
subtitle: Text(item.createdAt.toString()),
leading: CircleAvatar(
// this does not work - throws 404 error
// backgroundImage: NetworkImage(item.avatar ?? ''),
),
),
);
I am using this package https://pub.dev/packages/dropdown_button2
Multiselect Dropdown with Checkboxes
final List<String> items = [
'Item1',
'Item2',
'Item3',
'Item4',
];
List<String> selectedItems = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButtonHideUnderline(
child: DropdownButton2(
isExpanded: true,
hint: Align(
alignment: AlignmentDirectional.center,
child: Text(
'Select Items',
style: TextStyle(
fontSize: 14,
color: Theme.of(context).hintColor,
),
),
),
items: items.map((item) {
return DropdownMenuItem<String>(
value: item,
//disable default onTap to avoid closing menu when selecting an item
enabled: false,
child: StatefulBuilder(
builder: (context, menuSetState) {
final _isSelected = selectedItems.contains(item);
return InkWell(
onTap: () {
_isSelected
? selectedItems.remove(item)
: selectedItems.add(item);
//This rebuilds the StatefulWidget to update the button's text
setState(() {});
//This rebuilds the dropdownMenu Widget to update the check mark
menuSetState(() {});
},
child: Container(
height: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
_isSelected
? const Icon(Icons.check_box_outlined)
: const Icon(Icons.check_box_outline_blank),
const SizedBox(width: 16),
Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
],
),
),
);
},
),
);
}).toList(),
//Use last selected item as the current value so if we've limited menu height, it scroll to last item.
value: selectedItems.isEmpty ? null : selectedItems.last,
onChanged: (value) {},
buttonHeight: 40,
buttonWidth: 140,
itemHeight: 40,
itemPadding: EdgeInsets.zero,
selectedItemBuilder: (context) {
return items.map(
(item) {
return Container(
alignment: AlignmentDirectional.center,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
selectedItems.join(', '),
style: const TextStyle(
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
),
);
},
).toList();
},
),
),
),
);
}
I have created a container. His child is a text.
When I tap on the container/text, it display a modal and a Picker.
Then, the user can select a value. Press the confirm button and my text widget should change to display the value selected by the user.
But in my case, it is not updating the value of the text widget. I have used that in the past and it was working very well. But here it is not and I do not see why. I am on this since 8:00. A little help would be appreciated. Many thanks.
int valuePickerUnitSelected =0;
String unitCount = '';
int unitCountInt;
String goal = "";
List <String> unitForHabits = ['Count', 'Minute(s)','Hour(s)','Gramme(s)', 'Pound(s)'];
class AddingHabitDetails extends StatefulWidget {
const AddingHabitDetails({Key key}) : super(key: key);
#override
_AddingHabitDetailsState createState() => _AddingHabitDetailsState();
}
class _AddingHabitDetailsState extends State<AddingHabitDetails> {
BuildContext get ctx => null;
#override
Widget build(BuildContext context) {
return Scaffold(
//drawer: new MyMenu(), //TODO a remettre
appBar: new AppBar(
title: new Text('Habits'),
),
body: Column(
children: [
titleZone('Name'),
textFieldHabits('Habit name', context),
titleZone('Goals'),
FlatButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (unitCount.length < 2 )...[
Container(
width: 65,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue,
),
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(20))),
child:
Center(child: Text(
'Time', style: TextStyle(color: Colors.black,))))
]
else
...[
Container(
width: 65,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue,),
color: Colors.blue,
borderRadius: BorderRadius.all(
Radius.circular(20))),
child: Center(
child: Text(
unitCount, style: TextStyle(color: Colors.black,))))
],
],
),
onPressed: () {
setState(() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ShowPickerUnite(unitForHabits);
});
},
);
},
),
//ChipGoalV3(ctx),// ChipGoal(),
textFieldHabits('Goals', context),
titleZone('Frequency'),
textFieldHabits('Frequency', context),
titleZone('Time Range'),
titleZone('Reminder'),
titleZone('Habits Term'),
],
),
);
}
}
//########################################################################
class ShowPickerUnite extends StatefulWidget {
List<String> myListUnit;
ShowPickerUnite(this.myListUnit, {Key key}) : super(key: key);
#override
_ShowPickerUniteState createState() => _ShowPickerUniteState(
myListUnit);
}
class _ShowPickerUniteState extends State<ShowPickerUnite> {
List <String> myListUnit;
_ShowPickerUniteState(this.myListUnit);
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
decoration: BoxDecoration(
color: Color(0xffffffff),
border: Border(
bottom: BorderSide(
color: Color(0xffffffff),
width: 0.0,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CupertinoButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 5.0,
),
),
DefaultTextStyle(
style: TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.bold),
child: Text('Select what you want'),
),
// Text('Energy Needed', style: TextStyle(fontSize: 12.0, color: Colors.black),
// ),
CupertinoButton(
child: Text('Confirm'),
onPressed: () {
setState(() {
unitCount = unitForHabits[valuePickerUnitSelected];
print(unitCount);
});
Navigator.of(context).pop();
},
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 5.0,
),
),
],
),
),
Container(
//width: 360,
height: 200,
decoration:BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
child: CupertinoPicker(
backgroundColor: Colors.white ,
useMagnifier: true,
magnification: 1.3,
scrollController: FixedExtentScrollController(initialItem: 0),
itemExtent: 25,
children: [
for (String name in myListUnit)
Center(
child:Text(name)),
],
onSelectedItemChanged: (value) {
setState(() {
valuePickerUnitSelected = value;
// taskEnergy = myListEnergy[valuePickerEnergySelected];
// taskNewValue ['task_Energy'] = taskEnergy;
});
}))
]);
}
}
Widget inputNameHabit (){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text ('Name Habits',style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold),),
);
}
Widget titleZone (String _titleName){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text ( _titleName,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold),),
],
),
);
}
Widget textFieldHabits (String item,context){
return TextField(
decoration: InputDecoration(
hintText: item,
filled: true,
fillColor: Colors.grey[300],
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(50)
),
),
onTap: (){
Navigator.push(context,
MaterialPageRoute(
builder: (context) => HabitGoalUnitSelection(), //TODO MODIFIER route selon source
),
);
},);
}
Wait for the dialog to finish and then call setState to update the UI.
Modify this way
onPressed: () async {
await showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ShowPickerUnite(unitForHabits);
});
setState(() {});
I have asked this question previously but I am not receiving much help. I've created a GridView using GridView.count; however, I cannot get an indiviual container to change colors. The entire row changes if I click on any container within the row. I want to be able to change an individual container's color when it is tapped on, as well as have check mark appear on the top right corner of the container when selected.
(1) My Layout
(2) An Example of what I would like to happen
I'm very new to Flutter, so my code is not very optimal. I've tried making a list model as well but I have not had any luck with that. I'm attaching a portion of my code to show what I've done so far. Any help would be great :)
Widget build(BuildContext) {
double _height = MediaQuery.of(context).size.height;
final data = ModalRoute.of(context)!.settings;
String retrieveString;
if (data.arguments == null) {
retrieveString = "empty";
} else {
retrieveString = data.arguments as String;
}
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff31708c),
body: Padding(
padding: EdgeInsets.only(
left: 30,
right: 30,
top: _height * 0.2),
child: Column(
children: <Widget>[
Text('Hi $retrieveString! What all would you like to focus on?',
style: GoogleFonts.montserrat(
color: Colors.white70,
fontSize: 19,
fontWeight: FontWeight.w600
),
textAlign: TextAlign.center,),
const SizedBox(height: 9),
Text("You can pick all that apply:",
style: GoogleFonts.montserrat(
color: Colors.white70,
fontSize: 14.5,
fontWeight: FontWeight.w600
),),
const SizedBox(height: 9,),
Column(children: [
GridView.count(
primary: true,
shrinkWrap: true,
padding: const EdgeInsets.all(10),
childAspectRatio: 1.15,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
_ContainerColor = _ContainerColor == Colors.white
? Color(0xffa1d0e6)
: Colors.white;
});
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: const Color.fromARGB(255, 20, 83, 106),
width: 2.5),
color: _ContainerColor
),
child: Column(
children: [
const Align(alignment: Alignment.topCenter,
child: Icon(MyFlutterApp.relationships,
color: Color(0xff31708c),
size: 45,),
),
const SizedBox(height: 4,),
Text('Maintaining healthy relationships',
style: GoogleFonts.montserrat(
fontSize: 14,
fontWeight: FontWeight.w500,
color: const Color(0xff31708c)
),
textAlign: TextAlign.center,)
],
),
),
),
From my understanding, you have to do allow users to have multi-select, because of the line
You can pick all that apply:
So here is a custom stateful widget that helps you to do multi-select, you can have your own widget child inside the gridview.
class CustomPage extends StatefulWidget {
const CustomPage({Key? key}) : super(key: key);
#override
State<CustomPage> createState() => _CustomPageState();
}
class _CustomPageState extends State<CustomPage> {
String retrieveString = "";
List selectedIndex = [];
List dataItems = ['India', 'USA', 'Germany'];
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
final data = ModalRoute.of(context)!.settings;
if (data.arguments == null) {
retrieveString = "empty";
} else {
retrieveString = data.arguments as String;
}
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xff31708c),
body: Padding(
padding: EdgeInsets.only(left: 30, right: 30, top: height * 0.2),
child: Column(children: <Widget>[
Text('Hi $retrieveString! What all would you like to focus on?'),
const SizedBox(height: 10),
const Text("You can pick all that apply:"),
const SizedBox(height: 10,),
Expanded(
child: GridView.builder(
scrollDirection: Axis.vertical,
primary: true,
shrinkWrap: true,
padding: const EdgeInsets.all(10),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 1.15,
crossAxisCount: 2,
crossAxisSpacing: 25,
mainAxisSpacing: 25),
itemCount: dataItems.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
setState(() {
if (selectedIndex.contains(index)) {
selectedIndex.remove(index);
} else {
selectedIndex.add(index);
}
});
},
child: Stack(
alignment: Alignment.topRight,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(
color:
const Color.fromARGB(255, 20, 83, 106),
width: 2.5),
color: selectedIndex.contains(index)
? const Color(0xffa1d0e6)
: Colors.white),
child: Center(
child: Text(dataItems[index]),
),
),
selectedIndex.contains(index)
? Container(
padding: const EdgeInsets.all(10),
child: const CircleAvatar(
child: Icon(Icons.check_outlined),
),
)
: Container()
],
),
);
},
),
)
])));
}
}
Hope it resolves your issue.
I've created your app as a test version. All you need to do is inject your widget in the //put your widget here!!!!! section. Also for testing this right now as a demo, you can paste the code on dartpad and select "New" -> "Flutter" -> Paste Code: https://dartpad.dev
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowtappedModeBanner: false,
home: Scaffold(
body: Center(
child: CustomCheckboxGrid(),
),
),
);
}
}
class CustomCheckboxGrid extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _CustomCheckboxGridState();
}
}
class _CustomCheckboxGridState extends State<CustomCheckboxGrid> {
int tapped_index = 0;
List card_names = [
'Maintaining healthy relationships',
'Being happier and more content in life',
'Work life balance',
'Personal Growth',
'Stress',
'Mental health',
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.builder(
padding: const EdgeInsets.all(16),
itemCount: card_names.length,
itemBuilder: (BuildContext context, int index) {
return buildCard(index);
},
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
),
);
}
Widget buildCard(int index) {
bool tapped = index == tapped_index;
String current_name = card_names[index];
return GestureDetector(
onTap: () {
setState(() {
print("Tapped index: ${index}");
tapped_index = index;
});
},
child: Stack(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(14),
child:
//put your widget here!!!!!
//-----------------------------------
Card(
color: tapped ? Colors.orange : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Container(
child: Center(child: Text(current_name)),
),
),
//-----------------------------------
),
Positioned(
top: 14,
right: 14,
child: Offstage(
offstage: !tapped,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(width: 2),
shape: BoxShape.circle),
child: Icon(
Icons.check,
color: Colors.green,
),
),
),
),
],
),
);
}
}
enter image description hereAs per image I want popup in app-bar in flutter
Try the below code and you will store the SVG image in image directory
actions[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 5),
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(width: 2, color: Colors.blue)),
child: SvgPicture.asset(
"images/ic_more.svg",
height: 30,
color: Colors.white,
),
),
onTapDown: (details) {
_showPopUpMenu(details.globalPosition);
})
)
]
popUpMenu:
_showPopUpMenu(Offset offset) async {
final screenSize = MediaQuery.of(context).size;
double left = offset.dx;
double top = offset.dy;
double right = screenSize.width - offset.dx;
double bottom = screenSize.height - offset.dy;
await showMenu<MenuItemType>(
context: context,
position: RelativeRect.fromLTRB(left, top, right, bottom),
items: MenuItemType.values
.map((MenuItemType menuItemType) =>
PopupMenuItem<MenuItemType>(
value: menuItemType,
child: Text(getMenuItemString(menuItemType)),
))
.toList(),
).then((MenuItemType item) {
if (item == MenuItemType.EDIT) {
// here set your route
}
});
}
And your enum data for popup menu
import 'package:flutter/foundation.dart';
enum MenuItemType {
EDIT,
DUPLICATE
}
getMenuItemString(MenuItemType menuItemType) {
switch (menuItemType) {
case MenuItemType.EDIT:
return "Edit";
case MenuItemType.DUPLICATE:
return "Duplicate";
}
}
Please refer to below code
Using custom_pop_up_menu: ^1.2.2
https://pub.dev/packages/custom_pop_up_menu
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<ChatModel> messages;
List<ItemModel> menuItems;
CustomPopupMenuController _controller = CustomPopupMenuController();
#override
void initState() {
menuItems = [
ItemModel('Chat', Icons.chat_bubble),
ItemModel('Add', Icons.group_add),
ItemModel('View', Icons.settings_overscan),
];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('CustomPopupMenu'),
actions: <Widget>[
CustomPopupMenu(
child: Container(
child: Icon(
Icons.more_horiz,
color: Colors.white,
size: 24.0,
),
padding: EdgeInsets.symmetric(
horizontal: 30.0,
vertical: 20.0,
),
),
menuBuilder: () => ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Container(
color: Colors.white,
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: menuItems
.map(
(item) => GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: _controller.hideMenu,
child: Container(
height: 40,
padding: EdgeInsets.symmetric(horizontal: 20),
child: Row(
children: <Widget>[
Icon(
item.icon,
size: 15,
color: Colors.black,
),
Expanded(
child: Container(
margin: EdgeInsets.only(left: 10),
padding:
EdgeInsets.symmetric(vertical: 10),
child: Text(
item.title,
style: TextStyle(
color: Colors.black,
fontSize: 12,
),
),
),
),
],
),
),
),
)
.toList(),
),
),
),
),
pressType: PressType.singleClick,
verticalMargin: -10,
controller: _controller,
barrierColor: Colors.black54,
horizontalMargin: 0.0,
arrowColor: Colors.white,
showArrow: true,
),
],
),
body: Container(
child: Center(
child: Text(
"Pop up menu",
),
),
),
);
}
}
Solution Using PopupmenuButton
Widget popMenus({
List<Map<String, dynamic>> options,
BuildContext context,
}) {
return PopupMenuButton(
iconSize: 24.0,
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
icon: Icon(
Icons.more_horiz_rounded,
color: Colors.black,
size: 24.0,
),
offset: Offset(0, 10),
itemBuilder: (BuildContext bc) {
return options
.map(
(selectedOption) => PopupMenuItem(
height: 12.0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
selectedOption['menu'] ?? "",
style: TextStyle(
fontSize: ScreenUtil().setSp(14.0),
fontWeight: FontWeight.w400,
fontStyle: FontStyle.normal,
color: Colors.blue,
),
),
(options.length == (options.indexOf(selectedOption) + 1))
? SizedBox(
width: 0.0,
height: 0.0,
)
: Padding(
padding: EdgeInsets.symmetric(
vertical: 8.0,
),
child: Divider(
color: Colors.grey,
height: ScreenUtil().setHeight(1.0),
),
),
],
),
value: selectedOption,
),
)
.toList();
},
onSelected: (value) async {},
);
}
class PopUpmenusScreen extends StatefulWidget {
const PopUpmenusScreen({Key key}) : super(key: key);
#override
_PopUpmenusScreenState createState() => _PopUpmenusScreenState();
}
class _PopUpmenusScreenState extends State<PopUpmenusScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Examples"),
actions: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0,),
child: popMenus(
context: context,
options: [
{
"menu": "option 1" ?? '',
"menu_id": 1,
},
{
"menu": "option 2" ?? "",
"menu_id": 2,
},
{
"menu": "option 3" ?? "",
"menu_id": 3,
},
{
"menu": "option 4" ?? "",
"menu_id": 4,
},
],
),
)
],
),
);
}
}
Solution 2:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Examples"),
actions: [
IconButton(
icon: Icon(
Icons.more_horiz,
color: Colors.black,
size: 20.0,
),
onPressed: () {},
)
],
),
);
}
You can do that easily using DropdownButton2 which is customizable Flutter's core DropdownButton.
It has customButton parameter which will replace the normal Button with Image, Icon or any widget you want. You can customize everything and design what you need by using many options described with the package. Also, you can change the position of the dropdown menu by using the offset parameter.
Here's an example of using DropdownButton2 as a Popup Menu with Icon:
class CustomButtonTest extends StatefulWidget {
const CustomButtonTest({Key? key}) : super(key: key);
#override
State<CustomButtonTest> createState() => _CustomButtonTestState();
}
class _CustomButtonTestState extends State<CustomButtonTest> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButtonHideUnderline(
child: DropdownButton2(
customButton: const Icon(
Icons.list,
size: 46,
color: Colors.red,
),
customItemsIndexes: const [3],
customItemsHeight: 8,
items: [
...MenuItems.firstItems.map(
(item) =>
DropdownMenuItem<MenuItem>(
value: item,
child: MenuItems.buildItem(item),
),
),
const DropdownMenuItem<Divider>(enabled: false, child: Divider()),
...MenuItems.secondItems.map(
(item) =>
DropdownMenuItem<MenuItem>(
value: item,
child: MenuItems.buildItem(item),
),
),
],
onChanged: (value) {
MenuItems.onChanged(context, value as MenuItem);
},
itemHeight: 48,
itemWidth: 160,
itemPadding: const EdgeInsets.only(left: 16, right: 16),
dropdownPadding: const EdgeInsets.symmetric(vertical: 6),
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Colors.redAccent,
),
dropdownElevation: 8,
offset: const Offset(0, 8),
),
),
),
);
}
}
class MenuItem {
final String text;
final IconData icon;
const MenuItem({
required this.text,
required this.icon,
});
}
class MenuItems {
static const List<MenuItem> firstItems = [home, share, settings];
static const List<MenuItem> secondItems = [logout];
static const home = MenuItem(text: 'Home', icon: Icons.home);
static const share = MenuItem(text: 'Share', icon: Icons.share);
static const settings = MenuItem(text: 'Settings', icon: Icons.settings);
static const logout = MenuItem(text: 'Log Out', icon: Icons.logout);
static Widget buildItem(MenuItem item) {
return Row(
children: [
Icon(
item.icon,
color: Colors.white,
size: 22
),
const SizedBox(
width: 10,
),
Text(
item.text,
style: const TextStyle(
color: Colors.white,
),
),
],
);
}
static onChanged(BuildContext context, MenuItem item) {
switch (item) {
case MenuItems.home:
//Do something
break;
case MenuItems.settings:
//Do something
break;
case MenuItems.share:
//Do something
break;
case MenuItems.logout:
//Do something
break;
}
}
}