如何在颤动中实现下拉列表?

我有一个位置的列表,我想实现作为一个下拉列表在扑动。我对这门语言很陌生。这就是我所做的。

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的值为 null,但是我是这样初始化它的。

String _selectedLocation = 'Please choose a location';

382081 次浏览

试试这个

DropdownButton<String>(
items: <String>['A', 'B', 'C', 'D'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (_) {},
)
    

您需要在代码中添加 value: location来完成它。

items: _locations.map((String location) {
return new DropdownMenuItem<String>(
child: new Text(location),
value: location,
);
}).toList(),

你必须考虑到这一点(来自 DropdownButton 文档) :

”这些条目必须有不同的值,如果 value 不是 null,则必须是 成为他们中的一员”

所以基本上你有这个字符串列表

List<String> _locations = ['A', 'B', 'C', 'D'];

你在下拉值属性中的值是这样初始化的:

String _selectedLocation = 'Please choose a location';

试试这个清单:

List<String> _locations = ['Please choose a location', 'A', 'B', 'C', 'D'];

这应该可以:)

如果你不想添加这样的字符串(在列表上下文之外) ,你也可以使用类似这样的属性:

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(() {});
});

把值放在项目里面,然后它就会工作,

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 也遇到了类似的问题。我最终创建了一个插件: 扑动搜索面板。不是一个下拉式插件,但你可以显示的项目与搜索功能。

使用以下代码来使用小部件:

    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 的问题时,我就创建了它:

按钮。省道

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抛出的错误,但思想是一样的) :

失败的断言: 第560行 pos 15: ‘ item = = null | | items.isEmpty | | value = = null | | items.where ((DropdownMenuItem item) = > item.value = = value)。Length = = 1’: 不为真。

or有四个条件,其中至少一个必须满足:

  • 提供了项目(DropdownMenuItem小部件列表)。这样就消除了 items == null
  • 提供了非空列表。这消除了 items.isEmpty
  • 还给出了一个值(_selectedLocation)。这消除了 value == null。请注意,这是 DropdownButton的值,而不是 DropdownMenuItem的值。

因此只剩下最后一张支票了,它可以归结为:

遍历 DropdownMenuItem。找出所有 value等于 _selectedLocation的。然后,检查有多少项目符合它被发现。必须有一个具有此值的小部件。否则,抛出错误。

代码的呈现方式是,没有值为 _selectedLocationDropdownMenuItem小部件。相反,所有小部件的值都设置为 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)。如果您想让用户重新选择 Please choose a location选项,那么可能会很有用。
  • 选项2 。传递一些信息给 hint:参数,并将 selectedLocation设置为 null(以满足 value == null条件)。如果您不希望 Please choose a location仍然是一个选项,那么使用 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类来创建下拉列表:

...
...
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(),
),
),
);
...
...

请参阅此 颤动文档

当我用一个新的动态值替换默认值时,就发生了这种情况。但是,您的代码可能以某种方式依赖于该默认值。因此,请尝试保留一个常量,并将默认值存储在某处以备用。

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 需要成为项目列表的一部分;

对于任何有兴趣实现自定义 classDropDown的人,您可以按照以下步骤。

  1. 假设您有一个名为 Language的类,该类具有以下代码和一个返回 List<Language>static方法

    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'),
    ];
    
  2. 在任何你想实现 DropDown的地方,你都可以 importLanguage类首先像下面这样使用它

        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);
    },
    )
    

使用 StatefulWidgetsetState更新下拉列表。

  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

打开下拉菜单并选择值:

Select Value

将选定值反映到下拉菜单:

Value 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像弹出窗口一样出现。你可以像我一样自定义它(它会显示在同一个平台上,见下图) :

enter image description here

展开后:

enter image description here

请按照以下步骤: 首先,创建一个名为 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,
    )
    
  • 或者和你的班级:

    班级学生{ Int id; 字符串名称;

    A (this. id,this. name) ;

    //记住覆盖 @ 覆盖 String toString (){ 报税表名称; } }

打电话给:

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(),
)