如何在颤动中实现下拉列表?
How to implement drop down list in flutter?
我有一个位置列表,我想在 Flutter 中将其实现为下拉列表。我对这门语言很陌生。这是我所做的。
new DropdownButton(
value: _selectedLocation,
onChanged: (String newValue) {
setState(() {
_selectedLocation = newValue;
});
},
items: _locations.map((String location) {
return new DropdownMenuItem<String>(
child: new Text(location),
);
}).toList(),
这是我的物品清单:
List<String> _locations = ['A', 'B', 'C', 'D'];
我收到以下错误。
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.
我假设 _selectedLocation
的值变为空。但我是这样初始化的。
String _selectedLocation = 'Please choose a location';
试试这个
DropdownButton<String>(
items: <String>['A', 'B', 'C', 'D'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
)
您需要在代码中添加 value: location
才能使用它。检查 this。
items: _locations.map((String location) {
return new DropdownMenuItem<String>(
child: new Text(location),
value: location,
);
}).toList(),
你必须考虑到这一点(来自 DropdownButton 文档):
"The items must have distinct values and if value isn't null it must
be among them."
所以基本上你有这个字符串列表
List<String> _locations = ['A', 'B', 'C', 'D'];
并且您在下拉列表值 属性 中的值初始化如下:
String _selectedLocation = 'Please choose a location';
试试这个列表:
List<String> _locations = ['Please choose a location', 'A', 'B', 'C', 'D'];
应该可以:)
另请查看 "hint" 属性 如果您不想添加这样的字符串(在列表上下文之外),您可以使用以下内容:
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(() {});
});
将值放在 items.then 内即可,
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(() {});
})
当我试图在下拉列表中显示动态字符串列表时,我遇到了与 DropDownButton 类似的问题。我最终创建了一个插件:flutter_search_panel。不是下拉插件,但您可以显示具有搜索功能的项目。
使用以下代码来使用小部件:
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);
},
),
假设我们正在创建一个货币下拉列表:
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;
});
}
在正文部分添加以下代码:
new Row(children: <Widget>[
new Text("Currency: "),
new Container(
padding: new EdgeInsets.all(16.0),
),
new DropdownButton(
value: _currentCurrency,
items: _dropDownMenuCurrencyItems,
onChanged: changedDropDownItem,
)
])
您收到的错误是由于请求空对象的 属性。您的项目必须为空,因此在要求比较其价值时,您会收到该错误。检查您正在获取数据,或者您的列表是对象列表而不是简单的字符串。
当我 运行 进入这个想要一个不太通用的 DropdownStringButton 的问题时,我刚刚创建了它:
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,
);
}
如需解决方案,请滚动至答案末尾。
首先我们来分析一下报错是什么意思(我引用了Flutter 1.2抛出的报错,但是思路是一样的):
Failed assertion: line 560 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.
有四个or
条件。必须至少满足其中一项:
- 项目(
DropdownMenuItem
小部件列表)已提供。这消除了 items == null
.
- 提供了非空列表。这消除了
items.isEmpty
.
- 还给出了一个值(
_selectedLocation
)。这消除了 value == null
。请注意,这是 DropdownButton
的值,而不是 DropdownMenuItem
的值。
因此只剩下最后一张支票了。归结为:
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.
按照代码的显示方式,没有 DropdownMenuItem
小部件的值为 _selectedLocation
。相反,所有小部件的值都设置为 null
。由于 null != _selectedLocation
,最后一个条件失败。通过将 _selectedLocation
设置为 null
来验证这一点 - 应用程序应该 运行.
要解决此问题,我们首先需要为每个 DropdownMenuItem
设置一个值(以便可以将某些内容传递给 onChanged
回调):
return DropdownMenuItem(
child: new Text(location),
value: location,
);
应用程序仍然会失败。这是因为您的列表仍然不包含 _selectedLocation
的值。您可以通过两种方式使应用程序运行:
- 选项 1。添加另一个具有值的小部件(以满足
items.where((DropdownMenuItem<T> item) => item.value == value).length == 1
)。如果您想让用户重新选择 select Please choose a location
选项,可能会有用。
- 选项 2。将一些东西传递给
hint:
参数并将 selectedLocation
设置为 null
(以满足 value == null
条件)。如果您不想 Please choose a location
保留一个选项,这很有用。
查看下面的代码,了解如何操作:
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(),
),
),
),
);
}
}
您可以使用 DropDownButton
class 来创建下拉列表:
...
...
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(),
),
),
);
...
...
请参考这个flutter web page
当我用新的动态值替换默认值时,我遇到过这种情况。但是,您的代码可能以某种方式依赖于该默认值。因此,请尝试将默认值存储在某处以备后备。
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...
改变
List<String> _locations = ['A', 'B', 'C', 'D'];
到
List<String> _locations = [_selectedLocation, 'A', 'B', 'C', 'D'];
_selectedLocation 需要成为您的项目列表的一部分;
任何有兴趣实施自定义 DropDown
的人 class
您可以按照以下步骤操作。
假设您有一个名为 Language
的 class,具有以下代码和一个 static
方法,其中 returns 和 List<Language>
class Language {
final int id;
final String name;
final String languageCode;
const Language(this.id, this.name, this.languageCode);
}
const List<Language> getLanguages = <Language>[
Language(1, 'English', 'en'),
Language(2, 'فارسی', 'fa'),
Language(3, 'پشتو', 'ps'),
];
任何你想实现的地方 DropDown
你可以 import
Language
class 首先使用它如下
DropdownButton(
underline: SizedBox(),
icon: Icon(
Icons.language,
color: Colors.white,
),
items: getLanguages.map((Language lang) {
return new DropdownMenuItem<String>(
value: lang.languageCode,
child: new Text(lang.name),
);
}).toList(),
onChanged: (val) {
print(val);
},
)
使用 StatefulWidget
和 setState
更新下拉菜单。
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;
},
);
},
);
}
下拉列表的初始状态:
打开下拉菜单和 select 值:
将 selected 值反映到下拉列表中:
使用此代码。
class PlayerPreferences extends StatefulWidget {
final int numPlayers;
PlayerPreferences({this.numPlayers});
@override
_PlayerPreferencesState createState() => _PlayerPreferencesState();
}
class _PlayerPreferencesState extends State<PlayerPreferences> {
int dropDownValue = 0;
@override
Widget build(BuildContext context) {
return Container(
child: DropdownButton(
value: dropDownValue,
onChanged: (int newVal){
setState(() {
dropDownValue = newVal;
});
},
items: [
DropdownMenuItem(
value: 0,
child: Text('Yellow'),
),
DropdownMenuItem(
value: 1,
child: Text('Red'),
),
DropdownMenuItem(
value: 2,
child: Text('Blue'),
),
DropdownMenuItem(
value: 3,
child: Text('Green'),
),
],
),
);
}
}
在正文中我们称之为
class ModeSelection extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: PlayerPreferences(),
) ,
),
);
}
}
如果您不希望 Drop list
像弹出窗口一样显示。你可以像我一样自定义它(它会像在同一个平面上一样显示,见下图):
展开后:
请按照以下步骤操作:
首先,创建一个名为 drop_list_model.dart
:
的 dart 文件
import 'package:flutter/material.dart';
class DropListModel {
DropListModel(this.listOptionItems);
final List<OptionItem> listOptionItems;
}
class OptionItem {
final String id;
final String title;
OptionItem({@required this.id, @required this.title});
}
接下来,创建文件 file select_drop_list.dart
:
import 'package:flutter/material.dart';
import 'package:time_keeping/model/drop_list_model.dart';
import 'package:time_keeping/widgets/src/core_internal.dart';
class SelectDropList extends StatefulWidget {
final OptionItem itemSelected;
final DropListModel dropListModel;
final Function(OptionItem optionItem) onOptionSelected;
SelectDropList(this.itemSelected, this.dropListModel, this.onOptionSelected);
@override
_SelectDropListState createState() => _SelectDropListState(itemSelected, dropListModel);
}
class _SelectDropListState extends State<SelectDropList> with SingleTickerProviderStateMixin {
OptionItem optionItemSelected;
final DropListModel dropListModel;
AnimationController expandController;
Animation<double> animation;
bool isShow = false;
_SelectDropListState(this.optionItemSelected, this.dropListModel);
@override
void initState() {
super.initState();
expandController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 350)
);
animation = CurvedAnimation(
parent: expandController,
curve: Curves.fastOutSlowIn,
);
_runExpandCheck();
}
void _runExpandCheck() {
if(isShow) {
expandController.forward();
} else {
expandController.reverse();
}
}
@override
void dispose() {
expandController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 17),
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 10,
color: Colors.black26,
offset: Offset(0, 2))
],
),
child: new Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.card_travel, color: Color(0xFF307DF1),),
SizedBox(width: 10,),
Expanded(
child: GestureDetector(
onTap: () {
this.isShow = !this.isShow;
_runExpandCheck();
setState(() {
});
},
child: Text(optionItemSelected.title, style: TextStyle(
color: Color(0xFF307DF1),
fontSize: 16),),
)
),
Align(
alignment: Alignment(1, 0),
child: Icon(
isShow ? Icons.arrow_drop_down : Icons.arrow_right,
color: Color(0xFF307DF1),
size: 15,
),
),
],
),
),
SizeTransition(
axisAlignment: 1.0,
sizeFactor: animation,
child: Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.only(bottom: 10),
decoration: new BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 4,
color: Colors.black26,
offset: Offset(0, 4))
],
),
child: _buildDropListOptions(dropListModel.listOptionItems, context)
)
),
// Divider(color: Colors.grey.shade300, height: 1,)
],
),
);
}
Column _buildDropListOptions(List<OptionItem> items, BuildContext context) {
return Column(
children: items.map((item) => _buildSubMenu(item, context)).toList(),
);
}
Widget _buildSubMenu(OptionItem item, BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 26.0, top: 5, bottom: 5),
child: GestureDetector(
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: const EdgeInsets.only(top: 20),
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey[200], width: 1)),
),
child: Text(item.title,
style: TextStyle(
color: Color(0xFF307DF1),
fontWeight: FontWeight.w400,
fontSize: 14),
maxLines: 3,
textAlign: TextAlign.start,
overflow: TextOverflow.ellipsis),
),
),
],
),
onTap: () {
this.optionItemSelected = item;
isShow = false;
expandController.reverse();
widget.onOptionSelected(item);
},
),
);
}
}
初始化值:
DropListModel dropListModel = DropListModel([OptionItem(id: "1", title: "Option 1"), OptionItem(id: "2", title: "Option 2")]);
OptionItem optionItemSelected = OptionItem(id: null, title: "Chọn quyền truy cập");
终于用上了:
SelectDropList(
this.optionItemSelected,
this.dropListModel,
(optionItem){
optionItemSelected = optionItem;
setState(() {
});
},
)
制作您的自定义小部件:
import 'package:flutter/material.dart';
/// Usage:
/// CustomDropdown<String>(
// items: ['A', 'B', 'C'],
// onChanged: (val) => _selectedValue = val,
// center: true,
// ),
/// --> Remember: f.toString() at line 105 is @override String toString() in your class
// @override
// String toString() {
// return name;
// }
class CustomDropdown<T> extends StatefulWidget {
CustomDropdown({
Key key,
@required this.items,
@required this.onChanged,
this.onInit,
this.padding = const EdgeInsets.only(top: 10.0),
this.height = 40,
this.center = false,
this.itemText,
}) : super(key: key);
/// list item
List<T> items;
/// onChanged
void Function(T value) onChanged;
/// onInit
void Function(T value) onInit;
///padding
EdgeInsetsGeometry padding;
/// container height
double height;
/// center
bool center;
String Function(String text) itemText;
@override
_CustomDropdownState<T> createState() => _CustomDropdownState();
}
class _CustomDropdownState<T> extends State<CustomDropdown<T>> {
/// current selected value
T _selectedValue;
@override
void initState() {
super.initState();
_initValue();
}
@override
Widget build(BuildContext context) {
return _buildBody();
}
/// set default selected value when init
_initValue() {
_selectedValue = widget.items[0];
if (widget.onInit != null) widget.onInit(_selectedValue);
}
_buildBody() {
Color borderLine = Color(0xffc0c0c0);
return Padding(
padding: widget.padding,
child: Row(
mainAxisAlignment: (widget.center)
? MainAxisAlignment.center
: MainAxisAlignment.start,
children: <Widget>[
new Container(
height: widget.height,
padding: EdgeInsets.only(left: 10.0),
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: BorderSide(
width: 0.8, style: BorderStyle.solid, color: borderLine),
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
),
child: new DropdownButtonHideUnderline(
child: new DropdownButton<T>(
value: _selectedValue,
onChanged: (T newValue) {
setState(() {
_selectedValue = newValue;
widget.onChanged(newValue);
});
},
items: widget.items.map((T f) {
return new DropdownMenuItem<T>(
value: f,
child: new Text(
(widget.itemText != null)
? widget.itemText(f.toString())
: f.toString(),
style: new TextStyle(color: Colors.black),
),
);
}).toList(),
),
),
),
],
),
);
}
}
之后,简单调用:
CustomDropdown<String>(
items: ['A', 'B', 'C'],
onChanged: (val) => _selectedValue = val,
center: true,
)
或者用你的class:
class学生{
内部编号;
字符串名称;
A(this.id,this.name);
//记得覆盖
@覆盖
字符串 toString() {
return 姓名;
}
}
并调用:
CustomDropdown<Student>(
items: studentList,
onChanged: (val) => _selectedValue = val,
center: true,
),
这是我发现最有用的代码。
它为您提供所需的一切。 (ctrl+c , ctrl+v 可以)
List<String> location = ['One', 'Two', 'Three', 'Four'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fuel Entry')),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: [
DropdownButton<String>(
hint: Text('Select a vehicle '),
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: location.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
),
),
),
);
很简单
DropdownButton<String>(
isExpanded: true,
value: selectedLocation,
icon: const Icon(Icons.arrow_circle_down),
iconSize: 20,
elevation: 16,
underline: Container(),
onChanged: (String newValue) {
setState(() {
selectedLocation = newValue;
});
},
items: List.generate(
_locations.length,
(index) => DropdownMenuItem(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_locations[index]
),
),
value: _locations[index],
),
),
),
),
下面给出的完整代码示例
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _locations = ['A', 'B', 'C', 'D'];
String selectedLocation = 'A';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: DropdownButton<String>(
isExpanded: true,
value: selectedLocation,
icon: const Icon(Icons.arrow_circle_down),
iconSize: 20,
elevation: 16,
underline: Container(),
onChanged: (String newValue) {
setState(() {
selectedLocation = newValue;
});
},
items: List.generate(
_locations.length,
(index) => DropdownMenuItem(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_locations[index]
),
),
value: _locations[index],
),
),
),
),
);
}
}
这个有选择前显示的提示文本
DropdownButton<String>(
focusColor: Colors.white,
value: _chosenValue,
//elevation: 5,
style: TextStyle(color: Colors.white),
iconEnabledColor: Colors.black,
items:classes.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(color: Colors.black),
),
);
}).toList(),
hint: Text(
"All Classes",
style: TextStyle(
color: Colors.black, fontSize: 14, fontWeight: FontWeight.w500),
),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
);
DropdownButton<int>(
value: 6, //selected
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Theme.of(context).accentColor),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (int? newValue) {},
items: <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(value.toString()),
);
}).toList(),
)
我有一个位置列表,我想在 Flutter 中将其实现为下拉列表。我对这门语言很陌生。这是我所做的。
new DropdownButton(
value: _selectedLocation,
onChanged: (String newValue) {
setState(() {
_selectedLocation = newValue;
});
},
items: _locations.map((String location) {
return new DropdownMenuItem<String>(
child: new Text(location),
);
}).toList(),
这是我的物品清单:
List<String> _locations = ['A', 'B', 'C', 'D'];
我收到以下错误。
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.
我假设 _selectedLocation
的值变为空。但我是这样初始化的。
String _selectedLocation = 'Please choose a location';
试试这个
DropdownButton<String>(
items: <String>['A', 'B', 'C', 'D'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
)
您需要在代码中添加 value: location
才能使用它。检查 this。
items: _locations.map((String location) {
return new DropdownMenuItem<String>(
child: new Text(location),
value: location,
);
}).toList(),
你必须考虑到这一点(来自 DropdownButton 文档):
"The items must have distinct values and if value isn't null it must be among them."
所以基本上你有这个字符串列表
List<String> _locations = ['A', 'B', 'C', 'D'];
并且您在下拉列表值 属性 中的值初始化如下:
String _selectedLocation = 'Please choose a location';
试试这个列表:
List<String> _locations = ['Please choose a location', 'A', 'B', 'C', 'D'];
应该可以:)
另请查看 "hint" 属性 如果您不想添加这样的字符串(在列表上下文之外),您可以使用以下内容:
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(() {});
});
将值放在 items.then 内即可,
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(() {});
})
当我试图在下拉列表中显示动态字符串列表时,我遇到了与 DropDownButton 类似的问题。我最终创建了一个插件:flutter_search_panel。不是下拉插件,但您可以显示具有搜索功能的项目。
使用以下代码来使用小部件:
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);
},
),
假设我们正在创建一个货币下拉列表:
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;
});
}
在正文部分添加以下代码:
new Row(children: <Widget>[
new Text("Currency: "),
new Container(
padding: new EdgeInsets.all(16.0),
),
new DropdownButton(
value: _currentCurrency,
items: _dropDownMenuCurrencyItems,
onChanged: changedDropDownItem,
)
])
您收到的错误是由于请求空对象的 属性。您的项目必须为空,因此在要求比较其价值时,您会收到该错误。检查您正在获取数据,或者您的列表是对象列表而不是简单的字符串。
当我 运行 进入这个想要一个不太通用的 DropdownStringButton 的问题时,我刚刚创建了它:
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,
);
}
如需解决方案,请滚动至答案末尾。
首先我们来分析一下报错是什么意思(我引用了Flutter 1.2抛出的报错,但是思路是一样的):
Failed assertion: line 560 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.
有四个or
条件。必须至少满足其中一项:
- 项目(
DropdownMenuItem
小部件列表)已提供。这消除了items == null
. - 提供了非空列表。这消除了
items.isEmpty
. - 还给出了一个值(
_selectedLocation
)。这消除了value == null
。请注意,这是DropdownButton
的值,而不是DropdownMenuItem
的值。
因此只剩下最后一张支票了。归结为:
Iterate through
DropdownMenuItem
's. Find all that have avalue
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.
按照代码的显示方式,没有 DropdownMenuItem
小部件的值为 _selectedLocation
。相反,所有小部件的值都设置为 null
。由于 null != _selectedLocation
,最后一个条件失败。通过将 _selectedLocation
设置为 null
来验证这一点 - 应用程序应该 运行.
要解决此问题,我们首先需要为每个 DropdownMenuItem
设置一个值(以便可以将某些内容传递给 onChanged
回调):
return DropdownMenuItem(
child: new Text(location),
value: location,
);
应用程序仍然会失败。这是因为您的列表仍然不包含 _selectedLocation
的值。您可以通过两种方式使应用程序运行:
- 选项 1。添加另一个具有值的小部件(以满足
items.where((DropdownMenuItem<T> item) => item.value == value).length == 1
)。如果您想让用户重新选择 selectPlease choose a location
选项,可能会有用。 - 选项 2。将一些东西传递给
hint:
参数并将selectedLocation
设置为null
(以满足value == null
条件)。如果您不想Please choose a location
保留一个选项,这很有用。
查看下面的代码,了解如何操作:
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(),
),
),
),
);
}
}
您可以使用 DropDownButton
class 来创建下拉列表:
...
...
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(),
),
),
);
...
...
请参考这个flutter web page
当我用新的动态值替换默认值时,我遇到过这种情况。但是,您的代码可能以某种方式依赖于该默认值。因此,请尝试将默认值存储在某处以备后备。
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...
改变
List<String> _locations = ['A', 'B', 'C', 'D'];
到
List<String> _locations = [_selectedLocation, 'A', 'B', 'C', 'D'];
_selectedLocation 需要成为您的项目列表的一部分;
任何有兴趣实施自定义 DropDown
的人 class
您可以按照以下步骤操作。
假设您有一个名为
Language
的 class,具有以下代码和一个static
方法,其中 returns 和List<Language>
class Language { final int id; final String name; final String languageCode; const Language(this.id, this.name, this.languageCode); } const List<Language> getLanguages = <Language>[ Language(1, 'English', 'en'), Language(2, 'فارسی', 'fa'), Language(3, 'پشتو', 'ps'), ];
任何你想实现的地方
DropDown
你可以import
Language
class 首先使用它如下DropdownButton( underline: SizedBox(), icon: Icon( Icons.language, color: Colors.white, ), items: getLanguages.map((Language lang) { return new DropdownMenuItem<String>( value: lang.languageCode, child: new Text(lang.name), ); }).toList(), onChanged: (val) { print(val); }, )
使用 StatefulWidget
和 setState
更新下拉菜单。
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;
},
);
},
);
}
下拉列表的初始状态:
打开下拉菜单和 select 值:
将 selected 值反映到下拉列表中:
使用此代码。
class PlayerPreferences extends StatefulWidget {
final int numPlayers;
PlayerPreferences({this.numPlayers});
@override
_PlayerPreferencesState createState() => _PlayerPreferencesState();
}
class _PlayerPreferencesState extends State<PlayerPreferences> {
int dropDownValue = 0;
@override
Widget build(BuildContext context) {
return Container(
child: DropdownButton(
value: dropDownValue,
onChanged: (int newVal){
setState(() {
dropDownValue = newVal;
});
},
items: [
DropdownMenuItem(
value: 0,
child: Text('Yellow'),
),
DropdownMenuItem(
value: 1,
child: Text('Red'),
),
DropdownMenuItem(
value: 2,
child: Text('Blue'),
),
DropdownMenuItem(
value: 3,
child: Text('Green'),
),
],
),
);
}
}
在正文中我们称之为
class ModeSelection extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: PlayerPreferences(),
) ,
),
);
}
}
如果您不希望 Drop list
像弹出窗口一样显示。你可以像我一样自定义它(它会像在同一个平面上一样显示,见下图):
展开后:
请按照以下步骤操作:
首先,创建一个名为 drop_list_model.dart
:
import 'package:flutter/material.dart';
class DropListModel {
DropListModel(this.listOptionItems);
final List<OptionItem> listOptionItems;
}
class OptionItem {
final String id;
final String title;
OptionItem({@required this.id, @required this.title});
}
接下来,创建文件 file select_drop_list.dart
:
import 'package:flutter/material.dart';
import 'package:time_keeping/model/drop_list_model.dart';
import 'package:time_keeping/widgets/src/core_internal.dart';
class SelectDropList extends StatefulWidget {
final OptionItem itemSelected;
final DropListModel dropListModel;
final Function(OptionItem optionItem) onOptionSelected;
SelectDropList(this.itemSelected, this.dropListModel, this.onOptionSelected);
@override
_SelectDropListState createState() => _SelectDropListState(itemSelected, dropListModel);
}
class _SelectDropListState extends State<SelectDropList> with SingleTickerProviderStateMixin {
OptionItem optionItemSelected;
final DropListModel dropListModel;
AnimationController expandController;
Animation<double> animation;
bool isShow = false;
_SelectDropListState(this.optionItemSelected, this.dropListModel);
@override
void initState() {
super.initState();
expandController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 350)
);
animation = CurvedAnimation(
parent: expandController,
curve: Curves.fastOutSlowIn,
);
_runExpandCheck();
}
void _runExpandCheck() {
if(isShow) {
expandController.forward();
} else {
expandController.reverse();
}
}
@override
void dispose() {
expandController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 17),
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 10,
color: Colors.black26,
offset: Offset(0, 2))
],
),
child: new Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.card_travel, color: Color(0xFF307DF1),),
SizedBox(width: 10,),
Expanded(
child: GestureDetector(
onTap: () {
this.isShow = !this.isShow;
_runExpandCheck();
setState(() {
});
},
child: Text(optionItemSelected.title, style: TextStyle(
color: Color(0xFF307DF1),
fontSize: 16),),
)
),
Align(
alignment: Alignment(1, 0),
child: Icon(
isShow ? Icons.arrow_drop_down : Icons.arrow_right,
color: Color(0xFF307DF1),
size: 15,
),
),
],
),
),
SizeTransition(
axisAlignment: 1.0,
sizeFactor: animation,
child: Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.only(bottom: 10),
decoration: new BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 4,
color: Colors.black26,
offset: Offset(0, 4))
],
),
child: _buildDropListOptions(dropListModel.listOptionItems, context)
)
),
// Divider(color: Colors.grey.shade300, height: 1,)
],
),
);
}
Column _buildDropListOptions(List<OptionItem> items, BuildContext context) {
return Column(
children: items.map((item) => _buildSubMenu(item, context)).toList(),
);
}
Widget _buildSubMenu(OptionItem item, BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 26.0, top: 5, bottom: 5),
child: GestureDetector(
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: const EdgeInsets.only(top: 20),
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey[200], width: 1)),
),
child: Text(item.title,
style: TextStyle(
color: Color(0xFF307DF1),
fontWeight: FontWeight.w400,
fontSize: 14),
maxLines: 3,
textAlign: TextAlign.start,
overflow: TextOverflow.ellipsis),
),
),
],
),
onTap: () {
this.optionItemSelected = item;
isShow = false;
expandController.reverse();
widget.onOptionSelected(item);
},
),
);
}
}
初始化值:
DropListModel dropListModel = DropListModel([OptionItem(id: "1", title: "Option 1"), OptionItem(id: "2", title: "Option 2")]);
OptionItem optionItemSelected = OptionItem(id: null, title: "Chọn quyền truy cập");
终于用上了:
SelectDropList(
this.optionItemSelected,
this.dropListModel,
(optionItem){
optionItemSelected = optionItem;
setState(() {
});
},
)
制作您的自定义小部件:
import 'package:flutter/material.dart';
/// Usage:
/// CustomDropdown<String>(
// items: ['A', 'B', 'C'],
// onChanged: (val) => _selectedValue = val,
// center: true,
// ),
/// --> Remember: f.toString() at line 105 is @override String toString() in your class
// @override
// String toString() {
// return name;
// }
class CustomDropdown<T> extends StatefulWidget {
CustomDropdown({
Key key,
@required this.items,
@required this.onChanged,
this.onInit,
this.padding = const EdgeInsets.only(top: 10.0),
this.height = 40,
this.center = false,
this.itemText,
}) : super(key: key);
/// list item
List<T> items;
/// onChanged
void Function(T value) onChanged;
/// onInit
void Function(T value) onInit;
///padding
EdgeInsetsGeometry padding;
/// container height
double height;
/// center
bool center;
String Function(String text) itemText;
@override
_CustomDropdownState<T> createState() => _CustomDropdownState();
}
class _CustomDropdownState<T> extends State<CustomDropdown<T>> {
/// current selected value
T _selectedValue;
@override
void initState() {
super.initState();
_initValue();
}
@override
Widget build(BuildContext context) {
return _buildBody();
}
/// set default selected value when init
_initValue() {
_selectedValue = widget.items[0];
if (widget.onInit != null) widget.onInit(_selectedValue);
}
_buildBody() {
Color borderLine = Color(0xffc0c0c0);
return Padding(
padding: widget.padding,
child: Row(
mainAxisAlignment: (widget.center)
? MainAxisAlignment.center
: MainAxisAlignment.start,
children: <Widget>[
new Container(
height: widget.height,
padding: EdgeInsets.only(left: 10.0),
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: BorderSide(
width: 0.8, style: BorderStyle.solid, color: borderLine),
borderRadius: BorderRadius.all(Radius.circular(5.0)),
),
),
child: new DropdownButtonHideUnderline(
child: new DropdownButton<T>(
value: _selectedValue,
onChanged: (T newValue) {
setState(() {
_selectedValue = newValue;
widget.onChanged(newValue);
});
},
items: widget.items.map((T f) {
return new DropdownMenuItem<T>(
value: f,
child: new Text(
(widget.itemText != null)
? widget.itemText(f.toString())
: f.toString(),
style: new TextStyle(color: Colors.black),
),
);
}).toList(),
),
),
),
],
),
);
}
}
之后,简单调用:
CustomDropdown<String>( items: ['A', 'B', 'C'], onChanged: (val) => _selectedValue = val, center: true, )
或者用你的class:
class学生{ 内部编号; 字符串名称;
A(this.id,this.name);
//记得覆盖 @覆盖 字符串 toString() { return 姓名; } }
并调用:
CustomDropdown<Student>(
items: studentList,
onChanged: (val) => _selectedValue = val,
center: true,
),
这是我发现最有用的代码。 它为您提供所需的一切。 (ctrl+c , ctrl+v 可以)
List<String> location = ['One', 'Two', 'Three', 'Four'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fuel Entry')),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Column(
children: [
DropdownButton<String>(
hint: Text('Select a vehicle '),
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: location.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
),
),
),
);
很简单
DropdownButton<String>(
isExpanded: true,
value: selectedLocation,
icon: const Icon(Icons.arrow_circle_down),
iconSize: 20,
elevation: 16,
underline: Container(),
onChanged: (String newValue) {
setState(() {
selectedLocation = newValue;
});
},
items: List.generate(
_locations.length,
(index) => DropdownMenuItem(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_locations[index]
),
),
value: _locations[index],
),
),
),
),
下面给出的完整代码示例
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _locations = ['A', 'B', 'C', 'D'];
String selectedLocation = 'A';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: DropdownButton<String>(
isExpanded: true,
value: selectedLocation,
icon: const Icon(Icons.arrow_circle_down),
iconSize: 20,
elevation: 16,
underline: Container(),
onChanged: (String newValue) {
setState(() {
selectedLocation = newValue;
});
},
items: List.generate(
_locations.length,
(index) => DropdownMenuItem(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
_locations[index]
),
),
value: _locations[index],
),
),
),
),
);
}
}
这个有选择前显示的提示文本
DropdownButton<String>(
focusColor: Colors.white,
value: _chosenValue,
//elevation: 5,
style: TextStyle(color: Colors.white),
iconEnabledColor: Colors.black,
items:classes.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(color: Colors.black),
),
);
}).toList(),
hint: Text(
"All Classes",
style: TextStyle(
color: Colors.black, fontSize: 14, fontWeight: FontWeight.w500),
),
onChanged: (String value) {
setState(() {
_chosenValue = value;
});
},
);
DropdownButton<int>(
value: 6, //selected
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Theme.of(context).accentColor),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (int? newValue) {},
items: <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
.map<DropdownMenuItem<int>>((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(value.toString()),
);
}).toList(),
)