Reputation: 1175
I have written a pretty extensive form using DropdownButton
and TextField
widgets. The concept is that I have a StatefulWidget
, where the class of State<StatefulWidget>
contains 2 methods that return the widget I want to build. This way I can easily access and use the entered data and pass it along a function to compose an e-mail out of them.
However, when I select an item from the options, the framework throws an exception during the rebuild. I put in some log functions, and it shows that the setState()
method successfully saves the value to selectedValue
variable.
Widget buildMultiChoiceInputRow(var label, List<String> values) {
final List<String> options = values.toList();
selection = options.first;
final dropDownMenuOptions = options.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList();
return new Column(
children: <Widget>[
new Row(
children: <Widget>[
new Expanded(
child: new Container(
padding:
const EdgeInsets.only(left: 5.0, top: 2.0, right: 5.0),
child: new Text(label, style: commonInfoCardInfoTextBlack16Bold)),
),
],
),
new Row(
children: <Widget>[
new Expanded(
child: new Container(
padding: const EdgeInsets.only(left: 5.0, right: 5.0),
child: new DropdownButton(
value: selectedValue,
items: dropDownMenuOptions,
onChanged: (selection) {
setState(() {
selectedValue = selection;
switch (label) {
case labelVirtualAdoption:
tempAdoptionType =
composeMultiChoiceAnswer(label, selection);
print(selection);
print(selectedValue);
break;
case labelAskedAboutSpecies:
tempAskedAboutSpecies =
composeMultiChoiceAnswer(label, selection);
break;
case labelHouseOrFlat:
tempHouseOrFlat =
composeMultiChoiceAnswer(label, selection);
break;
....
default:
break;
}
});
}),
),
)
],
),
new Divider(color: Colors.transparent)
],
);
}
Here is the exception:
I/flutter (20998): The following assertion was thrown building AdoptionInput(dirty, state: AdoptionInputState#3cc80):
I/flutter (20998): 'package:flutter/src/material/dropdown.dart': Failed assertion: line 481 pos 15: 'value == null ||
I/flutter (20998): items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not true.
And here is the stack, showing that the exception is thrown during the rebuild:
I/flutter (20998): #2 new DropdownButton (package:flutter/src/material/dropdown.dart)
I/flutter (20998): #3 AdoptionInputState.buildMultiChoiceInputRow (package:osszefogasaszanhuzokert/adoptionPageUtilities.dart:443:28)
I/flutter (20998): #4 AdoptionInputState.build (package:osszefogasaszanhuzokert/adoptionPageUtilities.dart:639:11)
I/flutter (20998): #5 StatefulElement.build (package:flutter/src/widgets/framework.dart:3730:27)
I/flutter (20998): #6 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3642:15)
I/flutter (20998): #7 Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (20998): #8 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2242:33)
The problem seems really similar to a former bug in flutter, but if I try to initialize the selection
and selectedValue
in initState()
, the same exception will be thrown right as the form is built for the first time.
What am I missing here?
Upvotes: 25
Views: 52667
Reputation: 48
Although this question was asked four years ago and the Flutter SDK may have changed since then, if someone comes across this issue, one solution is to remove duplicate values in the DropdownMenuItem
. Alternatively, you can add key: ValueKey(item)
in your DropdownMenuItem widget. For instance, your DropdownButton code should look like this:
DropdownButton(
value: selectedValue,
items: dropDownMenuOptions,
key: ValueKey(selectedValue),
)
Also make sure that the initial value assigned to "value:"
for selectedValue
is present in the list passed to "items:
".
Upvotes: 0
Reputation: 24087
I was doing a value.toString() and the null was being converted to "null"!
Upvotes: 0
Reputation: 9
Use var to declare variable instead of String. Now you don't need to set the default value to null.
var dropdownvalue;
DropdownButton<String>(
value: dropdownvalue,
icon: Icon(Icons.keyboard_arrow_down),
iconSize: 28,
elevation: 20,
onChanged: (String newval){
setState((){
dropdownvalue = newval;
});
},
items: <String>["Registration","Verification", "ArenaRun"]
.map<DropdownMenuItem<String>>((String value){
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
Upvotes: 0
Reputation: 1261
here is the right way:
import 'package:flutter/material.dart';
class RegisterFragments extends StatefulWidget {
RegisterFragments({Key key, this.step}) : super(key: key);
final int step;
_RegisterFragmentsState createState() => _RegisterFragmentsState();
}
class _RegisterFragmentsState extends State<RegisterFragments> {
Map<String, bool> values = {"abc": false, "def": true, "ghi": false};
List<String> _do = ['One', 'Two', 'Free', 'Four'];
String _dropdownValue = 'One';
@override
Widget build(BuildContext context) {
switch (widget.step) {
case 0:
return buildDo();
break;
case 1:
return Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: values.length,
itemBuilder: (BuildContext context, int index) {
switch (widget.step) {
case 0:
return buildDo();
break;
case 1:
return buildService(context, index);
break;
default:
return Container();
break;
}
},
),
);
break;
default:
return Container();
break;
}
}
Widget buildService(BuildContext context, int index) {
String _key = values.keys.elementAt(index);
return Container(
child: Card(
child: CheckboxListTile(
title: Text(_key),
onChanged: (bool value) {
setState(() {
values[_key] = value;
});
},
value: values[_key],
),
),
);
}
Widget buildDo() {
return DropdownButton<String>(
isExpanded: true,
hint: Text("Service"),
items: _do.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
this._dropdownValue = newValue;
});
},
value: _dropdownValue,
);
}
}
list and dorpdown value
List<String> _do = ['One', 'Two', 'Free', 'Four'];
String _dropdownValue = 'One';
dropdown items
tems: _do.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>( **
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
this._dropdownValue = newValue;
});
},
value: _dropdownValue,
Upvotes: 0
Reputation: 726
A bit off time, but, the code worked when I passed null as the "value" but I was having this problem when I placed a value that was included on the "Items". The problem was that the "Items" had a duplicate value, so it seems that you should provide all different items in the list passed to the "Items".
Upvotes: 0
Reputation: 1547
Extending above answer with the second case where I was stuck.
Your "value" for DropdownButton should be set to 'null' or be one from the values list.
Your 'values' should also be different in every item value.
for example: Avoid this
items.add(DropdownMenuItem(
value: 1.toString(),
child: Text(1.toString()),
));
items.add(DropdownMenuItem(
value: 1.toString(),
child: Text(1.toString()),
));
Avoid repeating the values.
Upvotes: 14
Reputation: 656
Your "value" for DropdownButton should be set to 'null' or or be one from the values list.
DropdownButton(
value: null,
isDense: true,
onChanged: (String newValue) {
// somehow set here selected 'value' above whith
// newValue
// via setState or reactive.
},
items: ['yellow', 'brown', 'silver'].map((String value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
}).toList(),
),
So for my example DropdownButton value should be set to null or be 'yellow' or 'brown' or 'silver'.
Upvotes: 54