【问题标题】:Flutter: There should be exactly one item with [DropdownButton]'s valueFlutter:应该只有一项具有 [DropdownButton] 的值
【发布时间】:2020-06-16 00:09:33
【问题描述】:

我正在尝试在 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从数据库中获取我的列表

【问题讨论】:

  • 你试过硬编码一个值吗?字符串值 = "一些值";在您的 FutureBuilder 之前并将其分配给 _value = value;
  • @van 是的,同样的问题。
  • 只是补充一下其他人已经提到的,不仅默认值需要匹配列表中的一个值,如果是字符串,大小写也必须匹配。如果您的列表中有 NIGERIA,并且这是默认值,则默认值也必须是 NIGERIA,而不是尼日利亚。

标签: flutter dart drop-down-menu dropdownbutton flutter-futurebuilder


【解决方案1】:

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

我的 DropdownButton 的代码:

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

错误

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

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

这是一个错误!

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

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

因此崩溃:

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

  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

【讨论】:

  • 请注意默认选择值应该存在于数组中。
  • 这解决了我截至 2021 年 9 月的问题
  • 如果数据是来自服务器的动态数据,它将无法工作。如果你有动态数据,你怎么能告诉我它是如何工作的?
  • @MichaelFernando 我认为这是一个有效的问题。您是否仅在加载来自服务器的数据后尝试初始化 UI?因为那样的话,您将有一个空的初始化程序或列表中的第一项。
  • 终于有作品了。因为我在小部件构建中设置了我的 func 下拉列表。因此,它总是在第一时间加载数据。所以,我在小部件构建之外更改了此功能的位置并正常工作。谢谢你解释我@sud007
【解决方案2】:

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

  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];
}

【讨论】:

  • 我正在寻找的确切答案。谢谢大佬。
  • 非常感谢,这就是我要找的。​​span>
【解决方案3】:

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

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

String dropdownValue = "Encajes";

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

【讨论】:

  • 默认值不起作用它仍然给出错误。
  • "断言失败:第 1411 行第 15 行:'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem item) { return item。 value == value; }).length == 1'" 得到与更改下拉项时相同的错误。
  • 您的列表是空的,这意味着,即使您正确设置了第一项,您的小部件似乎也会在您的列表有任何值之前尝试呈现。我会尝试像 List myList = [] 一样初始化我的列表。通过这种方式,您可以确保您的应用在获得成员之前不会崩溃。我没有看到你的代码,只是想猜测一下。
  • 确保您选择 dart 作为语言并且在自动删除之前的许多天
【解决方案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 中的任何字段都不匹配

【讨论】:

  • 也为我工作。谢谢
【解决方案5】:

所以我找到了一个解决方案

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

List<Tag> _tagList = [];

然后,在我的 initState 中,我分配 我从数据库获得的列表previous List

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

【讨论】:

  • 我在这里没有看到DropdownButton 的代码有任何问题,我也在这样做。但似乎对我不起作用。只是因为同样的错误而崩溃!如果我删除了value:_assignedString,那么这工作正常,但是 DropdownButton 不会显示选定的值。仍在寻找解决方案。
  • 我解决了我的问题。 @Abdelbaki 的问题也是出于同样的原因,但由于您正在获取异步数据,这可能是一个原因,但实际上是 value 属性获得了 null 或空值。
【解决方案6】:

只需让标签类从Equatable 扩展并将属性传递给道具。这对我来说是诀窍。

class Tag extends Equatable{
  String id;
  String name;

  Tag(this.id, this.name);

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

}

【讨论】:

    【解决方案7】:

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

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

    因此,请确保在 snapshot.data删除所有重复项,然后再将它们设置为 DropDownButton 的项目。。 p>

    【讨论】:

      【解决方案8】:

      我有同样的错误,我的默认值不在 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'...];
      

      现在当您在 DropdownButton 中传递 defaultvalue 时不会出现错误

      DropdownButton (
        item:[]
       onChanged: (String values){
         print(values);
      setState(() {
      defaultValue = values;
      });
      },
      value: defaultValue,
      )
      

      【讨论】:

        【解决方案9】:

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

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

        【讨论】:

          【解决方案10】:

          我使用了一个技巧。所选项目作为列表中的第一个索引项目。因此,每次更改项目时从列表中删除项目重新插入项目作为第一个项目列表 。请参考以下代码。在这里,我使用 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(),
                              ),
                            ),
                          ),
                        ),
                      ]
                  );
            }
          

          【讨论】:

            【解决方案11】:

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

            初始代码:

            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;
            

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

            【讨论】:

              【解决方案12】:

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

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

              【讨论】:

                【解决方案13】:
                          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;
                              });
                
                         
                

                【讨论】:

                【解决方案14】:

                我也遇到了同样的问题,解决办法是把DropdownButton的值填进去(value:(使用你设置的item中的值) 你不能使用任何你想要的值,但它应该是你为 DropdownMenuItem 设置的项目之一。

                【讨论】:

                • 这个问题有 13 个现有答案,包括一个接受了 58 个赞成的答案。您确定尚未提供您的答案吗?如果不是,为什么有人会更喜欢您的方法而不是提议的现有方法?您是否正在利用新功能?是否存在更适合您的方法的场景?另外,您的意思是“您可以使用您想要的任何值”吗?否则,该语句没有意义。
                猜你喜欢
                • 2021-08-29
                • 1970-01-01
                • 2021-05-01
                • 1970-01-01
                • 2023-01-17
                • 2020-07-18
                • 2021-12-03
                • 2023-01-05
                • 2020-08-26
                相关资源
                最近更新 更多