38

我正在尝试在 Flutter中创建一个下拉按钮。从我的数据库中获取一个列表,然后我将列表传递给我的dropdownButton 一切工作,数据按预期显示,但是当我从中选择一个元素时,我收到此错误:

There should be exactly one item with [DropdownButton]'s value: Instance of 'Tag'. 
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 805 pos 15: 'items == null || items.isEmpty || value == null ||
          items.where((DropdownMenuItem<T> item) {
            return item.value == value;
          }).length == 1'

我尝试将DropdownButton 值设置为 null它可以工作,但是我看不到所选元素

这是我的代码:

FutureBuilder<List<Tag>>(
    future: _tagDatabaseHelper.getTagList(),
    builder: (BuildContext context, AsyncSnapshot<List<Tag>> snapshot) {
      if (!snapshot.hasData) {
        return Center(
          child: CircularProgressIndicator(),
        );
      }
      return ListView(
        children: <Widget>[
          SizedBox(
            height: MediaQuery.of(context).size.height * 0.2,
          ),
          Container(
            margin: EdgeInsets.symmetric(
                horizontal: MediaQuery.of(context).size.width * 0.07),
            child: Theme(
              data: ThemeData(canvasColor: Color(0xFF525A71)),
              child: DropdownButton<Tag>(
                value: _selectedTag,
                isExpanded: true,
                icon: Icon(
                  Icons.arrow_drop_down,
                  size: 24,
                ),
                hint: Text(
                  "Select tags",
                  style: TextStyle(color: Color(0xFF9F9F9F)),
                ),
                onChanged: (value) {
                  setState(() {
                    _selectedTag = value;
                  });
                },
                items: snapshot.data.map((Tag tag) {
                  return DropdownMenuItem<Tag>(
                    value: tag,
                    child: Text(
                      tag.tagTitle,
                      style: TextStyle(color: Colors.white),
                    ),
                  );
                }).toList(),
                value: _selectedTag,
              ),
            ),
          ),

我使用futureBuilder从数据库中获取我的列表。

4

14 回答 14

73

好吧,因为没有问题有完全相同的解决方案。我的代码也面临同样的问题。这是我解决这个问题的方法。

我的下拉按钮的代码:

DropdownButton(
   items: _salutations
         .map((String item) =>
             DropdownMenuItem<String>(child: Text(item), value: item))
         .toList(),
    onChanged: (String value) {
       setState(() {
         print("previous ${this._salutation}");
         print("selected $value");
         this._salutation = value;
            });
          },
     value: _salutation,
),

错误

在下面的代码片段中,我正在设置一个选择值的状态,它是 String 类型的。现在我的代码的问题是这个选择值的默认初始化。最初,我将变量初始化_salutation为:

String _salutation = ""; //Notice the empty String.

这是一个错误!

正如正确提到的错误消息,初始选择不应为空或为空。

'项目 == 空 || 项目.isEmpty || 值 == 空 ||

因此崩溃:

crash_message

解决方案
用一些默认值初始化值对象。请注意,该值应该是您的集合中包含的值之一。如果不是,那么预计会发生崩溃。

  String _salutation = "Mr."; //This is the selection value. It is also present in my array.
  final _salutations = ["Mr.", "Mrs.", "Master", "Mistress"];//This is the array for dropdown
于 2020-04-25T12:52:49.183 回答
23

如果尝试使用类实例设置下拉列表的值,也可能会出现此错误;

  var tag1 = Tag();
  var tag2 = Tag();
  print(tag1 == tag2); // prints false, dropwdown computes that value is not present among dropdown options

要解决此覆盖运算符 ==:

class Tag{
 String name = "tag";

  @override
  bool operator ==(Object other) => other is Tag && other.name == name;

  @override
  int get hashCode => name.hashCode;
}

或使用https://pub.dev/packages/equatable lib

class Tag extends Equatable{
 String name = "tag";

  @override
  List<Object> get props => [name];
}
于 2020-06-19T13:54:52.627 回答
10

我有同样的问题。解决方案很简单:您必须确保作为默认下拉值的字符串包含在您要在下拉菜单中使用的列表中。如果您想使用 api 中的列表,您应该确保知道该列表的至少一个值,以便您可以将其分配给作为默认下拉值的变量。

在这里,我想显示一个从 api 获得的列表。为了不出现错误,我将我的 defaultdropdownvalue 设置为名称“Encajes”,这是我的列表包含的现有类别之一。

String dropdownValue = "Encajes";

    items: categoriesString
    .map<DropdownMenuItem<String>>((String value) {
  return DropdownMenuItem<String>(
    value: value,
    child: Text(value),
  );
}).toList(),
于 2020-10-07T04:25:31.003 回答
4

我的下拉代码

child: DropdownButton(
      items: _currencies.map((String value) {
        return DropdownMenuItem<String>(
          child: Text(value),
          value: value,
        );
      }).toList(),
      value: 'Rupees',
      onChanged: (String newValueSelected) {
        // Your code to execute, when a menu item is selected from 
        dropdown
      },
))
var _currencies = ['Rupee','Dollar','Pound'];

我遇到了同样的错误,因为下拉代码块中的值与 _currencies 中的任何字段都不匹配

于 2020-05-29T07:18:08.713 回答
1

所以我找到了解决办法

创建了空列表来保存我的标签对象

List<Tag> _tagList = [];

然后,在我的initState中,我将从数据库中获得的列表分配给上一个列表

 @override
void initState() {
super.initState();
_tagDatabaseHelper.getTagList().then((foo) {
  setState(() {
    _tagList = foo;
  });
});

}

最后我的 DropdownButton 代码:

DropdownButton<Tag>(
            isExpanded: true,
            icon: Icon(
              Icons.arrow_drop_down,
              size: 24,
            ),
            hint: Text(
              "Select tags",
              style: TextStyle(color: Color(0xFF9F9F9F)),
            ),
            items: _tagList.map((foo) {
              return DropdownMenuItem(
                value: foo,
                child: Text(foo.tagTitle),
              );
            }).toList(),
            onChanged: (value) {
              setState(() {
                _selectedTag = value;
              });
            },
            value: _selectedTag,
          ),
于 2020-03-03T16:29:17.873 回答
1

我遇到了同样的问题,令人惊讶的是,我的项目列表中有重复项,这些项目是从远程数据库中获取的。

每次我从服务器获取数据时(当新的应用程序用户登录时),数据没有重复,但相同的数据被多次添加到列表中,因为我在同一设备上登录了多个用户。也许你的错误是类似的。

因此,在snapshot.data它们设置为DropDownButton.

于 2020-10-18T18:55:03.440 回答
1

我有同样的错误,我的默认值不存在于 listItems 在下拉按钮中映射为:

String defaultvalue = 'selectCategorie'

const List<String> Subcategories = ['category 1','category 2','category 3'...];

不得不改变这个: -

String defaultvalue = 'selectCategorie';

const List<String> Subcategories = ['selectCategorie','category 1','category 2','category 3'...];

现在,当您defaultvalue在 DropdownButton 中传递时,没有错误

DropdownButton (
  item:[]
 onChanged: (String values){
   print(values);
setState(() {
defaultValue = values;
});
},
value: defaultValue,
)
于 2021-10-22T09:29:54.463 回答
1

只需让标签类扩展Equatable并将属性传递给道具..这对我有用。

class Tag extends Equatable{
  String id;
  String name;

  Tag(this.id, this.name);

  @override
  List<Object> get props => [id,name];

}
于 2020-06-11T11:47:39.693 回答
0

我遇到了同样的问题,解决方案是填充 DropdownButton(value: (使用您设置的项目中的值) 的值,您不能使用您想要的任何值,但它应该是您设置的项目之一下拉菜单项。

于 2021-11-21T09:23:45.750 回答
0

我改变如下,它得到了解决:

初始代码:

List<GamesModel> users = <GamesModel>[
  new GamesModel(1,"Option1"),
  new GamesModel(2,"Option2"),

];
return users;

更改代码:

List<GamesModel> users = <GamesModel>[
      const GamesModel(1,"Option1"),
      const GamesModel(2,"Option2"),
];
return users;

如果有人想要我可以把整个代码

于 2021-06-17T14:52:55.500 回答
0
          DropdownButton<String>(
            iconEnabledColor: Colors.cyan.withOpacity(.6),
            isExpanded: true,
            itemHeight: 50,
            iconSize: 30,
            hint: Text("Choose Province"),
            items: _provinces
                .map((e) => DropdownMenuItem(
              child: Text(e),
              value: e,
            ))
                .toList(),
            value: _Province,
            onChanged: (String? value) async{
              final respnose=await FirebaseFirestore.instance.collection('city').where('provinceName',isEqualTo: value).get();
              _city=[];
              for(var item in respnose.docs){
                print(item.data());
                _city.add(item.data()['name']);
              }
              
              print(_Province);
              setState(() {
                _city=_city;
                _Province = value;
              });
            },
          ),
          SizedBox(height: 20,),

          DropdownButton<String>(
            iconEnabledColor: Colors.cyan.withOpacity(.6),
            isExpanded: true,
            itemHeight: 50,
            iconSize: 30,
            hint: Text("Choose City"),
            items:_city
                .map((e) => DropdownMenuItem(
              child: Text(e),
              value: e,
            ))
                .toList(),
            value: _City,
            onChanged: (String? value) async{
              setState(() {
                _town=[];
                _Town=null;
              });
              print(_town);
              final respnose=await FirebaseFirestore.instance.collection('town').where('cityName',isEqualTo: value).get();
              print(respnose.docs);


              for(var item in respnose.docs){
                print(item.data());
                _town.add(item.data()['name']);
              }
              print(_town);
              print(_City);
              setState(() {

                _City = value;
                _town=_town;
              });
            },
          ),
          SizedBox(height: 20,),
          
          if(true)
          DropdownButton<String>(
            iconEnabledColor: Colors.cyan.withOpacity(.6),
            isExpanded: true,
            itemHeight: 50,
            iconSize: 30,
            hint: Text("Choose Town"),
            items:_town
                .map((e) => DropdownMenuItem(
              child: Text(e),
              value: e,
            )
            )
                .toList(),
            value: _Town,
            onChanged: (String? value)async {
              print(_Town);
              setState(() {
                _Town = value;
              });

         
于 2021-06-29T07:47:11.467 回答
0

如果您忘记为下拉菜单项指定值,也会发生此错误。==== 工作 ====

<String>['A', 'B', 'C'].map<DropdownMenuItem<String>>((vehicle) {
        print("vehicle is $vehicle");
        print("vehicle is equal ${vehicle == x.value}");
        return DropdownMenuItem<String>(
          value: vehicle,
          child: Text(
            // vehicle.vehicleInfo!.vehicleType!,
            vehicle,
            style: TextStyle(
              color: Colors.grey[600],
            ),
          ),
        );
      }).toList(),

==== 不工作 ====

<String>['A', 'B', 'C'].map<DropdownMenuItem<String>>((vehicle) {
        return DropdownMenuItem<String>(
          child: Text(
            vehicle,
            style: TextStyle(
              color: Colors.grey[600],
            ),
          ),
        );
      }).toList(),
于 2021-06-29T18:17:24.000 回答
0

您可以使用三元运算符避免空值:

  Container(
             child:
              new DropdownButton<String>(
              value: dropdownValue ?? "1",
              icon: const Icon(Icons.arrow_downward),
              iconSize: 24,
              elevation: 16,
              style: const TextStyle(color: Colors.black, fontSize: 18),
              underline: Container(height: 2, color: Colors.white24, ),
              items: <String>['1', '2', '3', '5'].map((String value) {
              return new DropdownMenuItem<String>(
              value: value,
              child: new Text(value),
              );}).toList(),
              onChanged: (value) {
                  setState(() { dropdownValue=value;});
              },
          )),
于 2021-05-06T12:34:04.080 回答
0

我用了一个技巧。所选项目作为列表中的第一个索引项目。因此,每次更改项目时,从列表中删除该项目并将该项目重新插入列表中的第一个项目。请参考以下代码。在这里,我使用 Object 作为下拉项,而我将其作为提取函数的小部件。并且在调用 dropDownButton 函数之前

//项目列表如下

 List<LeaveType> items = [
 (id=1,name="Sick"),
 (id=2,name="Paid")
 ]

selectedLeave = null;

Row leaveTypeDropDown(StateSetter setCustomState, List<LeaveType> items) {
    if(selectedLeave != null){
      items.remove(selectedLeave);
      items.insert(0, selectedLeave);
    }
    return Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children : [
              text("Select Leave Type",textSize: 15),
              Padding(padding: const EdgeInsets.all(5)),
              Expanded(
                child: Container(
                  padding: const EdgeInsets.only(left: 10.0, right: 10.0),
                  decoration: BoxDecoration(
                    border: Border.all(color: Colors.black,width: 1),
                    borderRadius: const BorderRadius.all(Radius.circular(10.0)),
                  ),
                  child: DropdownButtonHideUnderline(
                    child: DropdownButton<LeaveType>(
                      isExpanded: true,
                      //initial value 
                      value: selectedLeave != null ? items[0] : null,
                      icon: const Icon(Icons.arrow_downward),
                      iconSize: 24,
                      elevation: 16,
                      hint: text("Leave Type"),
                      style: const TextStyle(
                          color: Colors.black
                      ),
                      onChanged: (LeaveType  value) {
                        setCustomState(() {
                          selectedLeave = value;
                          items.remove(selectedLeave);
                          items.insert(0, selectedLeave);
                        });
                      },
                      items: items
                          .map((leave) {
                        return  new DropdownMenuItem<LeaveType>(
                          value: leave,
                          child: text(leave.name),
                        );
                      }).toList(),
                    ),
                  ),
                ),
              ),
            ]
        );
  }
于 2021-06-12T08:13:56.550 回答