问题
I am trying to ask the user to select the type of item in the first dropdown and then select from its corresponding available colours in the second dropdown. However, when after I have selected a colour (i.e. white) and now want to switch to another item that does not have this colour, an error is thrown:
"There should be exactly one item with [DropdownButton]'s value: white. \nEither zero or 2 or more
[DropdownMenuItem]s were detected with the same value"
Please help, I have already tried to setState at various places to update the values but this error still occurs.
The following is my code snippet:
StreamBuilder<QuerySnapshot>(
stream: mainItemsSnapshots,
builder: (context, snapshot) {
if (snapshot.hasError) return Text("Error");
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
{
List<DropdownMenuItem> dropdownMenuItems =
snapshot.data.documents
.map((DocumentSnapshot mainItem) {
return new DropdownMenuItem<String>(
value: mainItem.documentID,
child: Text(mainItem.documentID),
);
}).toList();
return DropdownButtonFormField<String>(
items: dropdownMenuItems,
onChanged: (String value) {
if (value != tempMainItemType) {
setState(() {
tempMainItemType = value;
tempItemColorsList.clear();
tempItemColorsList = [];
tempMainItemColor = null;
});
}
if (tempItemColorsList.isEmpty && value != null) {
tempItemColorsList = snapshot.data.documents
.where((element) => element.documentID == value)
.first
.data["colors"]
.keys
.map((color) => color.toString())
.toList()
.cast<String>();
}
setState((){});
},
onSaved: (String value) {
_order.mainItemType = value;
},
value: tempMainItemType,
);
}
}
},
),
// Main color
if (tempItemColorsList?.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: spacingGeneral),
child: textFieldLabel(context, "Main color"),
),
if (tempItemColorsList?.isNotEmpty)
DropdownButtonFormField(
items: tempItemColorsList.map((String color) {
return new DropdownMenuItem<String>(
value: color,
child: Text(color),
);
}).toList(),
onSaved: (String value) {
_order.mainColor = value;
},
value: tempMainItemColor,
onChanged: (String value) {
setState(() {
tempMainItemColor = value;
});
},
),
回答1:
This maybe too late, but you can create a Map<String, List<String>>
where the keys are the items of the first dropdown list, and the value will be the items of second dropdown list.
Here, I created a state that stores the selected item of the first dropdown list. Then I used it to map the items of the second dropdown list.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SampleDD(),
);
}
}
class SampleDD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DoubleDropdown(
items: <String, List<String>>{
'dark colors': ['black', 'gray', 'brown'],
'light colors': ['white', 'yellow', 'green'],
},
onChangedFirst: (val) => print('Selected first: $val'),
onChangedSecond: (val) => print('Selected second: $val'),
),
),
);
}
}
class DoubleDropdown extends StatefulWidget {
DoubleDropdown({
@required this.items,
@required this.onChangedFirst,
@required this.onChangedSecond,
});
final Map<String, List<String>> items;
final ValueChanged<String> onChangedFirst;
final ValueChanged<String> onChangedSecond;
@override
_DoubleDropdownState createState() => _DoubleDropdownState();
}
class _DoubleDropdownState extends State<DoubleDropdown> {
String selectedKey;
@override
void initState() {
super.initState();
selectedKey = widget.items.keys.first;
}
@override
Widget build(BuildContext context) {
return Column(
children: [
_buildFirstDropdown(),
_buildSecondDowndown(),
],
);
}
Widget _buildFirstDropdown() => DropdownButtonFormField<String>(
items: widget.items.keys.map((e) {
return DropdownMenuItem<String>(
child: Text(e),
value: e,
);
}).toList(),
onChanged: (val) {
setState(() => selectedKey = val);
widget.onChangedFirst(val);
},
value: selectedKey,
);
Widget _buildSecondDowndown() => DropdownButtonFormField<String>(
items: widget.items[selectedKey].map((e) {
return DropdownMenuItem<String>(
child: Text(e),
value: e,
);
}).toList(),
onChanged: widget.onChangedSecond,
value: widget.items[selectedKey].first,
);
}
来源:https://stackoverflow.com/questions/61572284/how-to-resolve-flutter-dropdownbuttonformfield-dynamic-selection-checking-for-dr