如何使依赖的多级DropDown颤振?

时间:2020-02-27 12:25:28

标签: android ios flutter dart

我试图使依赖的多级下拉列表首先包含州列表,其次包含城市列表,所有这些数据都是从API获取的。最初,我加载状态下拉列表,当我选择状态时,然后加载该状态的城市(如果我选择城市,则成功选择了城市),但是当我更改状态值时,就会发生错误。如果将在第一个下拉列表中进行更改,重新加载第二个下拉列表的正确方法是什么?

错误:[DropdownButton]的值应该恰好是一项:“城市”的实例。 检测到零个或两个或两个以上具有相同值的[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'];
  }

2 个答案:

答案 0 :(得分:7)

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

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

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

此处发生此错误,因为您传递的value不在items的{​​{1}}(城市下拉列表)中。

选择州时,您正在获取城市列表的新列表并将其传递给CityDropDown,但是却忘记了清除先前选择的城市(DropdownButtonFormField)。

您也可以参考以下示例:DartPad

答案 1 :(得分:0)

在未获取新状态数据之前,我也遇到了问题。它显示了以前的状态数据。我使用的方法是不同的。我不使用将来的Builders。 这是我的代码:

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