class CardWidget extends StatefulWidget {
final String title;
final Color color;
final IconData icon;
const CardWidget({
super.key,
required this.title,
required this.color,
required this.icon,
});
@override
State<CardWidget> createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
final List<String> items = [
'Key 1',
'Key 2',
'Key 3',
'Key 4',
];
String? selectedValue;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
width: 200,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
color: widget.color,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(
widget.icon,
color: Colors.white,
size: 32,
),
Center(
child: Text(
widget.title,
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.bold),
),
),
DropdownButtonHideUnderline(
child: DropdownButton2(
isExpanded: true,
hint: Row(
children: [
Expanded(
child: Center(
child: Text(
'Select a Key',
style: dropdownTitle,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
items: items
.map((item) => DropdownMenuItem<String>(
value: item,
child: Center(
child: Text(
item,
style: dropdownItems,
overflow: TextOverflow.ellipsis,
),
),
))
.toList(),
value: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value as String;
});
},
icon: const Icon(
Icons.arrow_forward_ios_outlined,
),
iconSize: 20,
iconEnabledColor: Color.fromARGB(255, 31, 10, 88),
iconDisabledColor: Colors.grey,
buttonHeight: 40,
buttonWidth: 135,
buttonPadding: const EdgeInsets.only(
left: 15,
right: 15,
),
buttonDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: Colors.black26,
),
color: Color.fromARGB(255, 255, 255, 255),
),
buttonElevation: 2,
itemHeight: 40,
itemPadding: const EdgeInsets.only(left: 14, right: 14),
dropdownMaxHeight: 200,
dropdownWidth: 200,
dropdownPadding: null,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
dropdownElevation: 8,
scrollbarRadius: const Radius.circular(10),
scrollbarThickness: 6,
scrollbarAlwaysShow: true,
offset: const Offset(-20, 0),
),
),
CardWidget I created.
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CardWidget(
title: 'Mute',
color: Color.fromARGB(255, 211, 38, 25),
icon: Icons.volume_off_outlined,
),
CardWidget(
title: 'Mute',
color: Color.fromARGB(255, 211, 38, 25),
icon: Icons.volume_off_outlined,
),
CardWidget(
title: 'Mute',
color: Color.fromARGB(255, 211, 38, 25),
icon: Icons.volume_off_outlined,
),
CardWidget(
title: 'Mute',
color: Color.fromARGB(255, 211, 38, 25),
icon: Icons.volume_off_outlined,
),
],
),
The homepage where I use CardWidget.
I can call my list and select the elements inside the list.
However, as seen in the image, it can be active even though 'key 1' is selected in all dropdowns. What I'm dealing with is for example 'key 1' is selected in the first dropdown, if 'key 1' is selected in the second dropdown it will just show it there. The second dropdown should not write 'key 1' under the menu, it should write in the last selected one. Leave the first one blank.
Removed selected Item from others dropdown selection: I am using Valunotifer to simplify the snippet and the logic is
onChanged: (value) {
final index =
cardsNotifier.value.indexWhere((element) => element.id == id);
for (int i = 0; i < cardsNotifier.value.length; i++) {
if (index != i && cardsNotifier.value[i].selectedItem == value) {
cardsNotifier.value[i].selectedItem = null;
}
}
cardsNotifier.value[index].selectedItem = value;
cardsNotifier.value = cardsNotifier.value.toList();
},
Test widget
class FA extends StatefulWidget {
const FA({super.key});
@override
State<FA> createState() => _FAState();
}
class CardHelper {
final int id;
String? selectedItem;
CardHelper({
required this.id,
this.selectedItem,
});
}
ValueNotifier<List<CardHelper>> cardsNotifier = ValueNotifier(List.generate(
4,
(index) => CardHelper(
id: index,
)));
class _FAState extends State<FA> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(children: [
for (int i = 0; i < cardsNotifier.value.length; i++)
CardWidget(
title: 'Mute',
id: cardsNotifier.value[i].id,
),
]),
);
}
}
class CardWidget extends StatelessWidget {
final String title;
final int id;
const CardWidget({
super.key,
required this.id,
required this.title,
});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
width: 200,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
itemBuilder(),
],
),
),
),
);
}
ValueListenableBuilder<List<CardHelper>> itemBuilder() {
return ValueListenableBuilder<List<CardHelper>>(
valueListenable: cardsNotifier,
builder: (context, data, child) => DropdownButtonHideUnderline(
child: DropdownButton2(
isExpanded: true,
hint: Row(
children: [
Expanded(
child: Center(
child: Text(
'Select a Key',
overflow: TextOverflow.ellipsis,
),
),
),
],
),
items: ['Key 1', 'Key 2', 'Key 3', 'Key 4']
.map((item) => DropdownMenuItem<String>(
value: item,
child: Center(
child: Text(
item,
overflow: TextOverflow.ellipsis,
),
),
))
.toList(),
value: data[id].selectedItem,
onChanged: (value) {
final index =
cardsNotifier.value.indexWhere((element) => element.id == id);
for (int i = 0; i < cardsNotifier.value.length; i++) {
if (index != i && cardsNotifier.value[i].selectedItem == value) {
cardsNotifier.value[i].selectedItem = null;
}
}
cardsNotifier.value[index].selectedItem = value;
cardsNotifier.value = cardsNotifier.value.toList();
},
icon: const Icon(
Icons.arrow_forward_ios_outlined,
),
iconSize: 20,
),
),
);
}
}
Remove selected item from dropdown
I am using a helper class for simplification
class CardHelper {
final int id;
final List<String> items;
final String? selectedItem;
CardHelper({
required this.id,
required this.items,
this.selectedItem,
});
}
And the CardWidget
widget take data as input and provide a callback with selected item.
class CardWidget extends StatefulWidget {
final List<String> items;
final Function(String?) selectedItemCallback;
.....
const CardWidget({
super.key,
required this.items,
And onChanged will be
items: widget.items
.map((item) => DropdownMenuItem<String>(
value: item,
child: Center(
child: Text(
item,
overflow: TextOverflow.ellipsis,
),
),
))
.toList(),
value: selectedValue,
onChanged: (value) {
widget.selectedItemCallback(value);
Now for the parent widget, we will create a list for cards
final cards = List.generate(
4,
(index) =>
CardHelper(id: index, items: ['Key 1', 'Key 2', 'Key 3', 'Key 4']));
And rest logic lies
for (int i = 0; i < cards.length; i++)
CardWidget(
title: 'Mute',
color: Color.fromARGB(255, 211, 38, 25),
icon: Icons.volume_off_outlined,
items: cards[i].items,
selectedItemCallback: (p0) {
for (int j = 0; j < cards.length; j++) {
if (i == j) continue;
cards[j].items.remove(p0);
}
setState(() {});
},
),
Play widget
void main(List<String> args) {
runApp(MaterialApp(home: FA()));
}
class FA extends StatefulWidget {
const FA({super.key});
@override
State<FA> createState() => _FAState();
}
class CardHelper {
final int id;
final List<String> items;
final String? selectedItem;
CardHelper({
required this.id,
required this.items,
this.selectedItem,
});
}
class _FAState extends State<FA> {
final cards = List.generate(
4,
(index) =>
CardHelper(id: index, items: ['Key 1', 'Key 2', 'Key 3', 'Key 4']));
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
for (int i = 0; i < cards.length; i++)
CardWidget(
title: 'Mute',
color: Color.fromARGB(255, 211, 38, 25),
icon: Icons.volume_off_outlined,
items: cards[i].items,
selectedItemCallback: (p0) {
for (int j = 0; j < cards.length; j++) {
if (i == j) continue;
cards[j].items.remove(p0);
}
setState(() {});
},
),
],
),
);
}
}
class CardWidget extends StatefulWidget {
final List<String> items;
final String title;
final Color color;
final IconData icon;
final Function(String?) selectedItemCallback;
const CardWidget({
super.key,
required this.items,
required this.title,
required this.color,
required this.icon,
required this.selectedItemCallback,
});
@override
State<CardWidget> createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
String? selectedValue;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
width: 200,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
color: widget.color,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(
widget.icon,
color: Colors.white,
size: 32,
),
Center(
child: Text(
widget.title,
),
),
DropdownButtonHideUnderline(
child: DropdownButton2(
isExpanded: true,
hint: Row(
children: [
Expanded(
child: Center(
child: Text(
'Select a Key',
overflow: TextOverflow.ellipsis,
),
),
),
],
),
items: widget.items
.map((item) => DropdownMenuItem<String>(
value: item,
child: Center(
child: Text(
item,
overflow: TextOverflow.ellipsis,
),
),
))
.toList(),
value: selectedValue,
onChanged: (value) {
widget.selectedItemCallback(value);
setState(() {
selectedValue = value as String;
});
},
icon: const Icon(
Icons.arrow_forward_ios_outlined,
),
iconSize: 20,
iconEnabledColor: Color.fromARGB(255, 31, 10, 88),
iconDisabledColor: Colors.grey,
buttonHeight: 40,
buttonWidth: 135,
buttonPadding: const EdgeInsets.only(
left: 15,
right: 15,
),
buttonDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: Colors.black26,
),
color: Color.fromARGB(255, 255, 255, 255),
),
buttonElevation: 2,
itemHeight: 40,
itemPadding: const EdgeInsets.only(left: 14, right: 14),
dropdownMaxHeight: 200,
dropdownWidth: 200,
dropdownPadding: null,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
dropdownElevation: 8,
scrollbarRadius: const Radius.circular(10),
scrollbarThickness: 6,
scrollbarAlwaysShow: true,
offset: const Offset(-20, 0),
),
),
],
),
),
),
);
}
}