可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a list of locations that i want to implement as a dropdown list in Flutter. Im pretty new to the language. Here's what i have done.
new DropdownButton(
value: _selectedLocation,
onChanged: (String newValue) {
setState(() {
_selectedLocation = newValue;
});
},
items: _locations.map((String location) {
return new DropdownMenuItem<String>(
child: new Text(location),
);
}).toList(),
This is my list of items:
List<String> _locations = ['A', 'B', 'C', 'D'];
And I am getting the following error.
Another exception was thrown: 'package:flutter/src/material/dropdown.dart': Failed assertion: line 468 pos 15: 'value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not true.
I assume the value of _selectedLocation
is getting null. But i am initialising it like so.
String _selectedLocation = 'Please choose a location';
回答1:
Try this
new DropdownButton<String>(
items: <String>['A', 'B', 'C', 'D'].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (_) {},
)
回答2:
For the solution, scroll to the end of the answer.
First of all, let's investigate what the error says (I have cited the error that's thrown with Flutter 1.2, but the idea is the same):
Failed assertion: line 560 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.
There are four or
conditions. At least one of them must be fulfilled:
- Items (a list of
DropdownMenuItem
widgets) were provided. This eliminates items == null
.
- Non-empty list was provided. This eliminates
items.isEmpty
.
- A value (
_selectedLocation
) was also given. This eliminates value == null
. Note that this is DropdownButton
's value, not DropdownMenuItem
's value.
Hence only the last check is left. It boils down to something like:
Iterate through DropdownMenuItem
's. Find all that have a value
that's equal to _selectedLocation
. Then, check how many items matching it were found. There must be exactly one widget that has this value. Otherwise, throw an error.
The way code is presented, there is not a DropdownMenuItem
widget that has a value of _selectedLocation
. Instead, all the widgets have their value set to null
. Since null != _selectedLocation
, last condition fails. Verify this by setting _selectedLocation
to null
- the app should run.
To fix the issue, we first need to set a value on each DropdownMenuItem
(so that something could be passed to onChanged
callback):
return DropdownMenuItem(
child: new Text(location),
value: location,
);
The app will still fail. This is because your list still does not contain _selectedLocation
's value. You can make the app work in two ways:
- Option 1. Add another widget that has the value (to satisfy
items.where((DropdownMenuItem<T> item) => item.value == value).length == 1
). Might be useful if you want to let the user re-select Please choose a location
option.
- Option 2. Pass something to
hint:
paremter and set selectedLocation
to null
(to satisfy value == null
condition). Useful if you don't want Please choose a location
to remain an option.
See the code below that shows how to do it:
import 'package:flutter/material.dart';
void main() {
runApp(Example());
}
class Example extends StatefulWidget {
@override
State<StatefulWidget> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
// List<String> _locations = ['Please choose a location', 'A', 'B', 'C', 'D']; // Option 1
// String _selectedLocation = 'Please choose a location'; // Option 1
List<String> _locations = ['A', 'B', 'C', 'D']; // Option 2
String _selectedLocation; // Option 2
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: DropdownButton(
hint: Text('Please choose a location'), // Not necessary for Option 1
value: _selectedLocation,
onChanged: (newValue) {
setState(() {
_selectedLocation = newValue;
});
},
items: _locations.map((location) {
return DropdownMenuItem(
child: new Text(location),
value: location,
);
}).toList(),
),
),
),
);
}
}
回答3:
you have to take this into account (from DropdownButton docs):
"The items must have distinct values and if value isn't null it must
be among them."
So basically you have this list of strings
List<String> _locations = ['A', 'B', 'C', 'D'];
And your value in Dropdown value property is initialised like this:
String _selectedLocation = 'Please choose a location';
Just try with this list:
List<String> _locations = ['Please choose a location', 'A', 'B', 'C', 'D'];
That should work :)
Also check out the "hint" property if you don't want to add a String like that (out of the list context), you could go with something like this:
DropdownButton<int>(
items: locations.map((String val) {
return new DropdownMenuItem<String>(
value: val,
child: new Text(val),
);
}).toList(),
hint: Text("Please choose a location"),
onChanged: (newVal) {
_selectedLocation = newVal;
this.setState(() {});
});
回答4:
You need to add value: location
in your code to work it. Check this out.
items: _locations.map((String location) {
return new DropdownMenuItem<String>(
child: new Text(location),
value: location,
);
}).toList(),
回答5:
place the value inside the items.then it will work,
new DropdownButton<String>(
items:_dropitems.map((String val){
return DropdownMenuItem<String>(
value: val,
child: new Text(val),
);
}).toList(),
hint:Text(_SelectdType),
onChanged:(String val){
_SelectdType= val;
setState(() {});
})
回答6:
You can use DropDownButton
class in order to create drop down list :
...
...
String dropdownValue = 'One';
...
...
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButton<String>(
value: dropdownValue,
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
...
...
please refer to this flutter web page
回答7:
For anyone interested to implement a DropDownButton
with a custom class
you can follow the bellow steps.
Suppose you have a class called Language
with the following code and a static
method which returns a List<Language>
class Language {
final int id;
final String name;
final String languageCode;
Language(this.id, this.name, this.languageCode);
static List<Language> getLanguages() {
return <Language>[
Language(1, 'English', 'en'),
Language(2, 'فارسی', 'fa'),
Language(3, 'پشتو', 'ps'),
];
}
}
Anywhere you want to implement a DropDownButton
you can import
the Language
class first use it as follow,
Language _selectedLanguage; // <- define a local property
DropdownButton(
underline: SizedBox(),
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(horizontal: 5),
border: OutlineInputBorder(),
hintText: 'Select Languge',
),
items: Language.getLanguages().map((Language lang) {
return DropdownMenuItem<Language>(
value: lang,
child: Text(lang.name),
);
}).toList(),
onChanged: (Language val) {
_selectedLanguage = val;
},
)
回答8:
It had happened to me when I replace the default value with a new dynamic value. But, somehow your code may be dependent on that default value. So try keeping a constant with default value stored somewhere to fallback.
const defVal = 'abcd';
String dynVal = defVal;
// dropdown list whose value is dynVal that keeps changing with onchanged
// when rebuilding or setState((){})
dynVal = defVal;
// rebuilding here...
回答9:
Change
List<String> _locations = ['A', 'B', 'C', 'D'];
To
List<String> _locations = [_selectedLocation, 'A', 'B', 'C', 'D'];
_selectedLocation needs to be part of your item List;
回答10:
I was facing a similar issue with the DropDownButton when i was trying to display a dynamic list of strings in the dropdown. I ended up creating a plugin : flutter_search_panel. Not a dropdown plugin, but you can display the items with the search functionality.
Use the following code for using the widget :
FlutterSearchPanel(
padding: EdgeInsets.all(10.0),
selected: 'a',
title: 'Demo Search Page',
data: ['This', 'is', 'a', 'test', 'array'],
icon: new Icon(Icons.label, color: Colors.black),
color: Colors.white,
textStyle: new TextStyle(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 20.0, decorationStyle: TextDecorationStyle.dotted),
onChanged: (value) {
print(value);
},
),
回答11:
Let say we are creating a drop down list of currency:
List _currency = ["INR", "USD", "SGD", "EUR", "PND"];
List<DropdownMenuItem<String>> _dropDownMenuCurrencyItems;
String _currentCurrency;
List<DropdownMenuItem<String>> getDropDownMenuCurrencyItems() {
List<DropdownMenuItem<String>> items = new List();
for (String currency in _currency) {
items.add(
new DropdownMenuItem(value: currency, child: new Text(currency)));
}
return items;
}
void changedDropDownItem(String selectedCurrency) {
setState(() {
_currentCurrency = selectedCurrency;
});
}
Add below code in body part:
new Row(children: <Widget>[
new Text("Currency: "),
new Container(
padding: new EdgeInsets.all(16.0),
),
new DropdownButton(
value: _currentCurrency,
items: _dropDownMenuCurrencyItems,
onChanged: changedDropDownItem,
)
])
回答12:
The error you are getting is due to ask for a property of a null object. Your item must be null so when asking for its value to be compared you are getting that error. Check that you are getting data or your list is a list of objects and not simple strings.
回答13:
When I ran into this issue of wanting a less generic DropdownStringButton, I just created it:
dropdown_string_button.dart
import 'package:flutter/material.dart';
// Subclass of DropdownButton based on String only values.
// Yes, I know Flutter discourages subclassing, but this seems to be
// a reasonable exception where a commonly used specialization can be
// made more easily usable.
//
// Usage:
// DropdownStringButton(items: ['A', 'B', 'C'], value: 'A', onChanged: (string) {})
//
class DropdownStringButton extends DropdownButton<String> {
DropdownStringButton({
Key key, @required List<String> items, value, hint, disabledHint,
@required onChanged, elevation = 8, style, iconSize = 24.0, isDense = false,
isExpanded = false, }) :
assert(items == null || value == null || items.where((String item) => item == value).length == 1),
super(
key: key,
items: items.map((String item) {
return DropdownMenuItem<String>(child: Text(item), value: item);
}).toList(),
value: value, hint: hint, disabledHint: disabledHint, onChanged: onChanged,
elevation: elevation, style: style, iconSize: iconSize, isDense: isDense,
isExpanded: isExpanded,
);
}
回答14:
Use StatefulWidget
and setState
to update dropdown.
String _dropDownValue;
@override
Widget build(BuildContext context) {
return DropdownButton(
hint: _dropDownValue == null
? Text('Dropdown')
: Text(
_dropDownValue,
style: TextStyle(color: Colors.blue),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(color: Colors.blue),
items: ['One', 'Two', 'Three'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
_dropDownValue = val;
},
);
},
);
}
initial state of dropdown:
Open dropdown and select value:
Reflect selected value to dropdown: