Flutter中下拉选择不显示

15

我正在从JSON中获取项目并在下拉菜单中显示它们。当人们从下拉列表中选择一个项目时,我可以得到选择,但所选项目没有改变。

例如,在列表中有(东京,巴黎,纽约)。默认选择是东京。当人们选择巴黎时,我会收到通知,但下拉菜单中的选择不会改变。

这是我的代码:

new DropdownButton(
  value: cities.elementAt(0),
  hint: new Text("Ville"),
  items: cities.map((String value) {
    return new DropdownMenuItem(
      value: value,
      child: new Row(
        children: <Widget>[
          new Icon(
            Icons.location_city,
            color: Colors.deepOrange,
          ),
          new Text(value)
        ],
      ),
    );
  }).toList(),
  onChanged: (String value) {
    getTravelCity(value);
  },
),

当用户选择一个项目时,仍然显示默认值。


https://dev59.com/D1UK5IYBdhLWcg3wgQHd?rq=1 - Kirill Shashov
我尝试过了,但仍然存在同样的问题。 - Pondikpa Tchabao
10个回答

26

请确保您没有在下面示例中的小部件内声明selectedValue,以下示例完全适用于我。

输入图像描述

dartpad上查看工作代码以进行测试。

var currentSelectedValue;
const deviceTypes = ["Mac", "Windows", "Mobile"];

 Widget typeFieldWidget() {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 20),
      child: FormField<String>(
        builder: (FormFieldState<String> state) {
          return InputDecorator(
            decoration: InputDecoration(
                border: OutlineInputBorder(
                    borderRadius: BorderRadius.circular(5.0))),
            child: DropdownButtonHideUnderline(
              child: DropdownButton<String>(
                hint: Text("Select Device"),
                value: currentSelectedValue,
                isDense: true,
                onChanged: (newValue) {
                  setState(() {
                    currentSelectedValue = newValue;
                  });
                  print(currentSelectedValue);
                },
                items: deviceTypes.map((String value) {
                  return DropdownMenuItem<String>(
                    value: value,
                    child: Text(value),
                  );
                }).toList(),
              ),
            ),
          );
        },
      ),
    );
  }

替代解决方案

如果您正在使用无状态小部件中的DropDownButton,您可以通过将其包装在ValueListenableBuilder中来重新构建dropDown Widget。

class MyWidget extends StatelessWidget {
  String? currentSelectedValue;

   final ValueNotifier<List<String>> _listNotifier =
      ValueNotifier<List<String>>(["Mac", "Windows", "Mobile"]);

  Widget typeFieldWidget() {
    return ValueListenableBuilder(
      valueListenable: _listNotifier,
      builder: (BuildContext context, List<String> list, Widget? child) {
        return Container(
          padding: EdgeInsets.symmetric(horizontal: 20),
          child: FormField<String>(
            builder: (FormFieldState<String> state) {
              return InputDecorator(
                decoration: InputDecoration(
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(5.0))),
                child: DropdownButtonHideUnderline(
                  child: DropdownButton<String>(
                    hint: Text("Select Device"),
                    value: currentSelectedValue,
                    isDense: true,
                    onChanged: (newValue) {
                        currentSelectedValue = newValue;
                      _listNotifier.notifyListeners();
                    },
                    items: list.map((String value) {
                      return DropdownMenuItem<String>(
                        value: value,
                        child: Text(value),
                      );
                    }).toList(),
                  ),
                ),
              );
            },
          ),
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
      child: typeFieldWidget(),
    ));
  }
}


如果您使用SetState,那么这将起作用。但是对于我的情况,我需要一个StatelessWidget - Dani
2
不进行setState是不可能在屏幕上呈现新内容的,这就是Flutter的工作原理,它需要通过调用setState重新绘制小部件,您可以更改内存中的变量值,但要在屏幕上呈现更新后的值,您需要调用setState重新绘制用户界面。 - Mahesh Jamdade
1
如果您尝试了上述代码,则会起作用。如果您想添加新元素,只需将该元素添加到列表中,例如上面的代码中的deviceTypes,这样新元素就会反映在下拉列表中,每当下拉列表在屏幕上呈现时,它都会获取新列表。 - Mahesh Jamdade
1
我已更新答案,并附上了可运行的代码Dartpad链接,请查看。 - Mahesh Jamdade
1
最重要的注意事项:“确保您不是在 Widget 内部声明 selectedValue”! - Giddy Naya
显示剩余3条评论

11
This is the solution: "StatefulBuilder()"

       Future<void> AgregarContacto() async {
             String dropdownValue = 'Exento';
             return showDialog<void>(
           context: context,
           barrierDismissible: true, // user must tap button!
           builder: (BuildContext context) {
                 return StatefulBuilder(
                   builder: (BuildContext context, StateSetter setState) {
                   return AlertDialog(
                   title: Text('Agregar cliente', textAlign: TextAlign.center,),
                   content:
                     SingleChildScrollView(
                       child: ListBody(
                         children: <Widget>[
                           Center(
                             child: Material(
                               type: MaterialType.transparency,
                               child: DropdownButton<String>(
                                 items: <String>[
                                   'Responsable inscripto',
                                   'Monotributista',
                                   'Exento',
                                   'Consumidor final',
                                   'No responsable'
                                 ]
                                     .map<DropdownMenuItem<String>>((
                                     String value) {
                                   return DropdownMenuItem<String>(
                                     value: value,
                                     child: Text(value),
                                   ); //DropMenuItem
                                 }).toList(),
                                 value: dropdownValue,
                                 onChanged: (String newValue) {
                                   setState(() {
                                     dropdownValue = newValue;
                                     print("new${newValue}");
                                   }); //setState
                                 },
                                 //OnChange
                                 isExpanded: false,
                                 hint: Text('Responsable inscripto',
                                   style: TextStyle(color: Colors.black),),
                               ),
                             ), //Material
                           ), //Center
                         ],
                       ),
                     ),
                   actions: <Widget>[
                         FlatButton(
                           child: Text('Regret'),
                           onPressed: () {
                             Navigator.of(context).pop();
                          },
                       ),
                    ],
                 );
            }
            );
           },
         );
       }

这正是我一直在寻找的。我有一个非常长的无状态屏幕,希望在那个有状态的屏幕中只有一个下拉菜单。这篇文章给了我这个功能。太棒了。我测试过了,它的效果非常好。 - spatik

3
这是一个在Flutter的Github上报告的bug。
正确的实现方式是:
class _DropdownButtonBugState extends State<DropdownButtonBug> {

  final List<String> _items = ['One', 'Two', 'Three', 'Four'].toList();

  String _selection;

  @override
  void initState() {
    _selection = _items.first;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final dropdownMenuOptions = _items
      .map((String item) =>
        new DropdownMenuItem<String>(value: item, child: new Text(item))
      )
      .toList();

    return new Scaffold(
      body: new DropdownButton<String>(
        value: _selection,
        items: dropdownMenuOptions,
        onChanged: (s) {
          setState(() {
            _selection = s;
          });
        }
      )
    );
  }
}

2

如果选择后的值没有更新,Dropdown Button可以包装在StatefulBuilder()小部件中。


0
因为您需要使用“setState((){});”方法更新“value:”参数,并且新值必须与传递给“onChanged:”的值具有相同的类型。我建议这样做,因为您可以动态更改图标。
1)在文件顶部声明一个新类;
class city {
  final String name;
  final IconData icon;

  const city({
    this.name,
    this.icon,
  });
}

2) 将此添加到您的小部件状态中:

class yourState extend<myWidget>{

List<city> cities = [
    new city(name: "tokio", icon: Icons.location_city),
    new city(name: "paris", icon: Icons.location_city),
    new city(name: "new york", icon: Icons.location_city),
  ];

int index = 0;

Widget drop() {
    return DropdownButton(
        value: cities[index],
        hint: new Text("Ville"),
        items: cities.map((city value) {
          return new DropdownMenuItem(
            value: value,
            child: new Row(
              children: <Widget>[
                new Icon(
                  value.icon,
                  color: Colors.deepOrange,
                ),
                new Text(value.name)
              ],
            ),
          );
        }).toList(),
        onChanged: (city value) {
          setState(() {
            index = cities.indexOf(value);
            print(index);
          });
          //getTravelCity(value.name);
        });
  }

}

现在在您想要DropDown的位置调用“drop()”。再见!


DropdownButtonvalue属性已经更新了,现在不需要将实际的字符串值赋值给它,只需使用索引即可。 - Bing Gan

0
我在最近的应用程序中使用了这段代码,它可以正常工作。也许你可以将它与你的代码进行比较。
return DropdownButton<String>(
    style: Theme.of(context).textTheme.title,
    items: _persons.map((String val) {
      return new DropdownMenuItem<String>(
        value: val,
        child: new Container(
          child: new Card(
            child: new Row(children: <Widget>[
              new Icon(Icons.person),
              new Text(val),
            ]),
          ),
        ),
      );
    }).toList(),
    hint: new Card(
        child: new Row(children: <Widget>[
      new Icon(Icons.person),
      new Text("check"),
    ])),
    value: _selectedPerson,
    onChanged: (newVal) {
      _selectedPerson = newVal;
      setState(() {});
    });

0

你在下拉菜单中硬编码了第一个城市的

new DropdownButton(
            value: cities.elementAt(0), //this one
...

你需要有一些状态变量来保存所选的城市。
class YourStatefulWidgetState extends State<YourStatefulWidget> {
  @override
  initState() {
   super.initState();
   selectedCity = cities.elementAt(0)
  }

 @override
 Widget build(BuildContext context) {
   ...
   new DropdownButton(
            value: selectedCity,
            hint: new Text("Ville"),
            items: cities.map((String value) {
              return new DropdownMenuItem(value: value,
                child: new Row(
                  children: <Widget>[
                    new Icon(
                      Icons.location_city, color: Colors.deepOrange,),
                    new Text(value)
                  ],
                ),);
            }).toList(),
            onChanged: (String value) {
              setState(() {
                selectedCity = getTravelCity(value);
              });
            },),
     ...

我忘了告诉你下拉菜单在AlertDialog中。 - Pondikpa Tchabao
@PondikpaTchabao 你有自己的对话框小部件吗?我认为这段代码在你的情况下不起作用,因为 setState 调用更新的是构建对话框的小部件,而不是对话框本身。你应该创建自己的 'DropdownAlertDialog' Stateful 小部件。 - Kirill Shashov

0

我尝试了所有选项,包括所有Flutter问题。 但是经过2个小时的搜索,我找到了我的Bloc实现小部件的解决方案。

 onChanged: (Object value) {
     selectedCity = getTravelCity(value);
     setState((){}; });
        );

你可以在这里感受到不同之处。由于我正在使用一些状态来填充BlocBuilder下的列表,它不会允许你重新构建下拉菜单。所以在设置了dropdownValue之后,请调用setState方法。这将允许你反映出该值。

谢谢!


0

将一个变量作为下拉菜单的默认值,然后在您的 onChanged 中,设置变量等于value; 并进行 setState。


0

对于未来遇到此问题的人,请使用变量将值设置为DropdownButton,当onChanged被调用时,使用setState更新变量

Expanded(child: DropdownButton<String>(
          items: cities.map((String value) {
            return DropdownMenuItem<String>(
              value: value,
              child: Text(value),
            );
          }).toList(),
          value: city,
          onChanged: (String val) {
            _onDropDownChanged(val);
          },
        ),)

通过使用state更新city变量

void _onDropDownChanged(String val) {
    setState(() {
      this.city = val;
    });
  }

如需更多信息,请查看下面的链接

https://github.com/FazalHussain/Flutter-Demo/blob/trip_cost_app/lib/main.dart


这对我起作用,直到我使用了这个。 - user3697484

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接