Reputation: 804
I have a code where I have multiple dropdown boxes based on the length of a list. The code is working well, for now let's say it's generating two dropdown boxes with there options. The issue is that when ever I'm selecting an option from the first dropdown box it's affecting the second dropdown value and vice versa whenever I choose an option from the second dropdown it's affecting the first dropdown value. the issue is I know that I'm using one variable to both of the dropdown boxes values, But I don't know how to generate dynamic variables based on the dropdown boxes name or any other ways of generating a variable that gives different variables for the dropdown boxes. I tried to use a list variable to hold the values based on the dropdown boxes but it didn't work too. So I'm lost right now I don't know how to solve it. Here's the code:
class DropdownAttributes extends StatefulWidget {
final List<Attributes> attributes;
final List<AllAttributes> allAttributes;
const DropdownAttributes({Key key, this.attributes, this.allAttributes})
: super(key: key);
State createState() => DropdownAttributesState();
}
class DropdownAttributesState extends State<DropdownAttributes> {
int selectedAttributes;
@override
Widget build(BuildContext context) {
//We make an integer list first to hold all the attribute id's in the product
List<int> attributeIds = [];
//we push the attribute id's to the integer list "attributeIds"
widget.attributes.map((Attributes attributes) {
attributeIds.add(attributes.attributeId);
}).toList();
//we remove all the duplicates so that when we loop into it we don't duplicate the results
List<int> removedDuplicates =
LinkedHashSet<int>.from(attributeIds).toList();
return Column(
children: widget.allAttributes.map((AllAttributes allAttributes) {
bool attributeCheck;
//we check if removedDuplicates contains any of the id's that in the allAttributes.id
if (removedDuplicates.contains(allAttributes.id)) {
attributeCheck = true;
} else {
attributeCheck = false;
}
//we are in the loop, if we any attribute.attribute.id equals allAttributes.id return a dropdown with it's options
if (attributeCheck) {
return Row(
children: <Widget>[
new Container(
alignment: Alignment(-1.0, -1.0),
child: Padding(
padding: const EdgeInsets.only(bottom: 10.0, right: 10.0),
child: Text(
allAttributes.name + ':',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w600),
),
)),
DropdownButton<int>(
hint: Text("Select a " + allAttributes.name),
value: allAttributes.id,
onChanged: (int Value) {
setState(() {
selectedAttributes = Value;
});
print(selectedAttributes);
},
items: widget.attributes.map((Attributes attributes) {
bool listCheck;
if (attributes.attributeId == allAttributes.id) {
listCheck = true;
} else {
listCheck = false;
}
if(listCheck){
return DropdownMenuItem<int>(
value: attributes.id,
child: Row(
children: <Widget>[
SizedBox(
width: 10,
),
Text(
attributes.value,
style: TextStyle(color: Colors.black),
),
],
),
);
} else{
return DropdownMenuItem<int>(
value: attributes.id,
child: Container(),
);
}
}).toList(),
),
],
);
} else {
return Container();
}
}).toList());
}
}
UPDATE: Just to make the question and the issue more clear, The dropdown's don't depend on each other, each of them are independent dropdown's but they are been generated in a for loop. and right now in my code all of the dropdown's that are generated are changing one variable int selectedAttributes; instead of changing each of them their own variables. which I can't figure it out how to generate variables for each of them that I can access them later. I can only access only one variable which it's int selectedAttributes; but it's been affected by all the dropdown's that are generated and I don't want that.
UPDATE WITH ANSWER: The below code is final working code I just wanted to add it for any one in future that faces the same problem, the below code is based on @João Soares answer with some changes to work with my condition.
class DropdownAttributes extends StatefulWidget {
final List<Attributes> attributes;
final List<AllAttributes> allAttributes;
const DropdownAttributes({Key key, this.attributes, this.allAttributes})
: super(key: key);
State createState() => DropdownAttributesState();
}
class DropdownAttributesState extends State<DropdownAttributes> {
int selectedAttributes;
List<int> dropdownValues = [];
@override
void initState() {
widget.allAttributes.map((AllAttributes allAttributes) {
dropdownValues = List.generate(
widget.allAttributes.length, (value) => allAttributes.id);
}).toList();
super.initState();
}
@override
Widget build(BuildContext context) {
//We make an integer list first to hold all the attribute id's in the product
List<int> attributeIds = [];
//we push the attribute id's to the integer list "attributeIds"
widget.attributes.map((Attributes attributes) {
attributeIds.add(attributes.attributeId);
}).toList();
//we remove all the duplicates so that when we loop into it we don't duplicate the results
List<int> removedDuplicates =
LinkedHashSet<int>.from(attributeIds).toList();
return Column(
children: widget.allAttributes.map((AllAttributes allAttributes) {
dropdownValues.add(allAttributes.id);
bool attributeCheck;
//we check if removedDuplicates contains any of the id's that in the allAttributes.id
if (removedDuplicates.contains(allAttributes.id)) {
attributeCheck = true;
} else {
attributeCheck = false;
}
//we are in the loop, if we any attribute.attribute.id equals allAttributes.id return a dropdown with it's options
if (attributeCheck) {
return Row(
children: <Widget>[
new Container(
alignment: Alignment(-1.0, -1.0),
child: Padding(
padding: const EdgeInsets.only(bottom: 10.0, right: 10.0),
child: Text(
allAttributes.name + ':',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.w600),
),
)),
DropdownButton<int>(
hint: Text("Select a " + allAttributes.name),
value: dropdownValues[allAttributes.id],
onChanged: (value) =>
onDropDownChange(allAttributes.id, value),
items: widget.attributes.map((Attributes attributes) {
bool listCheck;
if (attributes.attributeId == allAttributes.id) {
listCheck = true;
} else {
listCheck = false;
}
if (listCheck) {
return DropdownMenuItem<int>(
value: attributes.id,
child: Row(
children: <Widget>[
SizedBox(
width: 10,
),
Text(
attributes.value,
style: TextStyle(color: Colors.black),
),
],
),
);
} else {
return DropdownMenuItem<int>(
value: attributes.id,
child: Container(),
);
}
}).toList(),
),
],
);
} else {
return Container();
}
}).toList());
}
void onDropDownChange(dropDownIndex, value) {
setState(() {
dropdownValues[dropDownIndex] = value;
});
print('onDropDownChange: $dropDownIndex -> $value');
}
}
Upvotes: 1
Views: 3144
Reputation: 9625
Creating a dynamic generator of DropDownButton
s can be done with the below sample code. But to have a DropDownButton
dynamically know which other DropDownButton
s need to be changed you would have to also pass a map of those connections between DropDownButton
s.
class Attribute {
int value;
String name;
Attribute({
@required this.value,
@required this.name
});
}
class MultiDropDown61061194 extends StatefulWidget {
final List<List<Attribute>> lists;
MultiDropDown61061194({this.lists});
@override
_MultiDropDown61061194State createState() => _MultiDropDown61061194State();
}
class _MultiDropDown61061194State extends State<MultiDropDown61061194> {
List<int> dropdownValues = [];
@override
void initState() {
dropdownValues = List.generate(widget.lists.length, (value) => 1);
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 200,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: List.generate(widget.lists.length, (index) {
return Container(
child: DropdownButton(
isExpanded: true,
onChanged: (value) => onDropDownChange(index, value),
value: dropdownValues[index],
items: widget.lists[index].map((list) {
return DropdownMenuItem(
child: Text('${list.name}'),
value: list.value,
);
}).toList(),
),
);
})
),
),
);
}
void onDropDownChange(dropDownIndex, value){
setState(() {
dropdownValues[dropDownIndex] = value;
});
print('onDropDownChange: $dropDownIndex -> $value');
}
}
Calling the class with the lists:
MultiDropDown61061194(
lists: [
[
Attribute(
value: 1,
name: 'A'
),
Attribute(
value: 2,
name: 'B'
),
Attribute(
value: 3,
name: 'C'
),
],
[
Attribute(
value: 1,
name: 'Alpha'
),
Attribute(
value: 2,
name: 'Beta'
),
Attribute(
value: 3,
name: 'Charlie'
),
],
],
),
Upvotes: 2