【问题标题】:How to make dependent multilevel DropDown in flutter?如何在颤动中制作依赖的多级下拉菜单?
【发布时间】:2020-06-11 11:08:48
【问题描述】:

我正在尝试使相关的多级下拉列表首先包含州列表,第二个包含城市列表,所有从 API 获取的数据。最初,我加载州下拉菜单,当我选择州时,如果我选择城市,则加载该州的城市,城市选择成功,但是当我更改州值时,会发生错误。如果将在第一个下拉列表中进行更改,那么重新加载第二个下拉列表的正确方法是什么?

错误:应该只有一项具有 [DropdownButton] 的值:“城市”的实例。 检测到 0 个或 2 个或多个 [DropdownMenuItem] 具有相同的值

Future _state;
Future _city;

@override
  void initState() {
    super.initState();
    _state = _fetchStates();
  }
Future<List<StateModel>> _fetchStates() async {
    final String stateApi = "https://dummyurl/state.php";
    var response = await http.get(stateApi);

    if (response.statusCode == 200) {
      final items = json.decode(response.body).cast<Map<String, dynamic>>();

      List<StateModel> listOfUsers = items.map<StateModel>((json) {
        return StateModel.fromJson(json);
      }).toList();

      return listOfUsers;
    } else {
      throw Exception('Failed to load internet');
    }
  }
  Future<List<City>> _fetchCities(String id) async {
    final String cityApi = "https://dummyurl/city.php?stateid=$id";
    var response = await http.get(cityApi);

    if (response.statusCode == 200) {
      final items = json.decode(response.body).cast<Map<String, dynamic>>();
      print(items);
      List<City> listOfUsers = items.map<City>((json) {
        return City.fromJson(json);
      }).toList();

      return listOfUsers;
    } else {
      throw Exception('Failed to load internet');
    }
  }

状态下拉菜单

FutureBuilder<List<StateModel>>(
                                        future: _state,
                                        builder: (BuildContext context,
                                            AsyncSnapshot<List<StateModel>> snapshot) {
                                          if (!snapshot.hasData)
                                            return CupertinoActivityIndicator(animating: true,);
                                          return DropdownButtonFormField<StateModel>(
                                            isDense: true,
                                            decoration: spinnerDecoration('Select your State'),
                                            items: snapshot.data
                                                .map((countyState) => DropdownMenuItem<StateModel>(
                                              child: Text(countyState.billstate),
                                              value: countyState,
                                            ))
                                                .toList(),
                                            onChanged:(StateModel selectedState) {
                                              setState(() {
                                                stateModel = selectedState;
                                                _city = _fetchCities(stateModel.billstateid);
                                              });
                                            },
                                            value: stateModel,
                                          );
                                        }),

城市下拉菜单

FutureBuilder<List<City>>(
                                        future: _city,
                                        builder: (BuildContext context,
                                            AsyncSnapshot<List<City>> snapshot) {
                                          if (!snapshot.hasData)
                                            return CupertinoActivityIndicator(animating: true,);
                                          return DropdownButtonFormField<City>(
                                            isDense: true,
                                            decoration: spinnerDecoration('Select your City'),
                                            items: snapshot.data
                                                .map((countyState) => DropdownMenuItem<City>(
                                              child: Text(countyState.billcity)
                                                .toList(),
                                            onChanged: (City selectedValue) {
                                              setState(() {
                                                cityModel = selectedValue;
                                              });
                                            },
                                            value: cityModel,
                                          );
                                        }),
class StateModel {
  String billstateid;
  String billstate;
  String billcountryid;

  StateModel({this.billstateid, this.billstate, this.billcountryid});

  StateModel.fromJson(Map<String, dynamic> json) {
    billstateid = json['billstateid'];
    billstate = json['billstate'];
    billcountryid = json['billcountryid'];
  }
}
class City {
  String billcityid;
  String billcity;
  String billstateid;

  City({this.billcityid, this.billcity, this.billstateid});

  City.fromJson(Map<String, dynamic> json) {
    billcityid = json['billcityid'];
    billcity = json['billcity'];
    billstateid = json['billstateid'];
  }

【问题讨论】:

    标签: android ios flutter dart


    【解决方案1】:

    您必须在状态下拉列表的onChanged 回调中创建cityModel = null

    setState(() {
      cityModel = null;
      stateModel = selectedState;
      _city = _fetchCities(stateModel.billstateid);
    });
    

    应该只有一项具有 [DropdownButton] 的值: “城市”的实例。零个或 2 个或多个 [DropdownMenuItem] 检测到相同的值

    此处出现此错误,因为您传递的value 不在DropdownButtonFormFielditems 中(城市下拉菜单)。

    当您选择一个州时,您正在获取新的城市列表并将其传递给 CityDropDown 但忘记清除之前选择的城市(cityModel)。

    你也可以参考这个例子:DartPad

    【讨论】:

    • 非常感谢它正在工作,我想知道这是我在使用依赖下拉列表时遵循的正确方法
    • @Toyed 如果您想在选择或更改StateDropDown 之前显示加载指示器来代替CityDropDown,那么最好使用两个FutureBuilder。但我更喜欢显示加载对话框,而不是像我共享的 DartPad 中那样替换 CityDropDown
    • 好的..根据我遵循的这种方式,我面临一个问题,直到未获取新的状态数据它正在显示以前的状态数据。
    • @Toyed 这是因为您使用的是!snapshot.hasData 条件。尝试将其更改为snapshot.connectionState == ConnectionState.waiting
    • 是的,现在工作正常,你真是个天才。非常感谢
    【解决方案2】:

    在未获取新状态数据之前,我也面临一个问题。它正在显示以前的状态数据。我使用的方法是不同的。我没有使用未来的建设者。 这是我的代码:

                   Container(
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: <Widget>[
                          new Expanded(
                            child: new Container(
                              width: 450,
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.start,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  Text(
                                    "Source",
                                    style: TextStyle(
                                        fontSize: 15, fontWeight: FontWeight.bold),
                                  ),
                                  source1 != null ? DropdownButtonFormField<String>(
                                    isExpanded: true,
                                    validator: (value) => value == null ? 'field required' : null,
                                    hint: Text("Select Source"),
                                    items: source1.data.map((item) {
                                      // print("Item : $item");
                                      return DropdownMenuItem<String>(
                                        value: item.descr,
                                        child: Text(item.descr),
                                      );
                                    }).toList(),
                                    onChanged: (String cat) {
                                      setState(() {
                                        subsourseStr = null;
                                        sourceStr = cat;
                                        getSubSource2(sourceStr);
                                      });
                                    },
                                    value: sourceStr,
                                  ):SizedBox(height: 10),
                                ],
                              ),
                            ),
                          )
                        ],
                      ),
                    ),
    //
                    Container(
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                        crossAxisAlignment: CrossAxisAlignment.center,
                        children: <Widget>[
                          new Expanded(
                            child: new Container(
                              width: 450,
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.start,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  Text(
                                    "Sub Source",
                                    style: TextStyle(
                                        fontSize: 15, fontWeight: FontWeight.bold),
                                  ),
                                  subSource2 != null ? DropdownButtonFormField<String>(
                                    isExpanded: true,
                                    validator: (value) => value == null ? 'field required' : null,
                                    hint: Text("Select Sub Source"),
                                    items: subSource2.data.map((item) {
                                      return DropdownMenuItem<String>(
                                        value: item.descr,
                                        child: Text(item.descr),
                                      );
                                    }).toList(),
                                    onChanged: (String cat) {
                                      setState(() {
                                        subsourseStr = cat;
                                      });
                                    },
                                    value: subsourseStr,
                                  ):SizedBox(height: 10,),
                                ],
                              ),
                            ),
                          )
                        ],
                      ),
                    ),
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-22
      • 2014-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-08
      • 2016-10-13
      相关资源
      最近更新 更多