简体   繁体   中英

Flutter Error when changing DropDownButton Selection

Error:

Failed assertion: line 609 pos 15: 'items == null ||
        I/flutter (24295): items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) => item.value ==
        I/flutter (24295): value).length == 1': is not true.

I did some research and did not figure out what caused the problem. Here is my code:

class StatusList extends StatefulWidget {
  @override
  _StatusListState createState() => _StatusListState();
}

class _DispositionListState extends State<DispositionList> {

  var _currentSelectedValue = '';

  Future<RecordList> recordList;

  @override
  void initState() {
    recordList = getRecord();
    super.initState();
  }

  int i = 1;
  List<String> statusList = List<String>();

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<RecordList>(
        future: recordList,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            String current = snapshot.data.record[0].status.trim();
            statusList.add(snapshot.data.record[0].status.trim());

            while (i < snapshot.data.record.length) {
              if (snapshot.data.record[i].status.trim() != current) {
                statusList.add(snapshot.data.record[i].status.trim());
                current = snapshot.data.record[i].status.trim();
              }
              i++;
            }
            _currentSelectedValue = statusList[0]; //not set this will straight getting error

            return DropdownButton(
              items: statusList.map((String dropDownStringItem) {
                return DropdownMenuItem<String>(
                    value: dropDownStringItem,
                    child: SizedBox(
                      width: 200.0,
                      child: Text(
                        dropDownStringItem,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ));
              }).toList(),
              onChanged: (String valueSelected) {
                onDropDownSelected(valueSelected);
              },
              value: _currentSelectedValue,
            );
          } else if (snapshot.hasError) {
            return Text("${snapshot.error}");
          }
          return CircularProgressIndicator();
        });
  }

  void onDropDownSelected(String valueSelected) {
    setState(() {
      this._currentSelectedValue = valueSelected;
    });
  }
}

I tried compare the recordList and the valueSelected in onDropDownSelected return true. Problem caused when the setState is called even without any code inside. Anyone idea of what caused the problem based on these code?

Added code: Here is my getRecord()

Future<RecordList> getRecord() async {
  String url = 'some url';
  final response = await http.get(url, headers: {"Accept": "application/json"});

  if (response.statusCode == 200) {
    return RecordList.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed to load post');
  }
}

class Record {
  final String status;
  final String disposition;
  final int total;

  Record({this.status, this.disposition, this.total});

  factory Record.fromJson(Map<String, dynamic> json) {
    return Record(
        status: json['status'],
        disposition: json['disposition'],
        total: json['total']);
  }
}

class RecordList {
  final List<Record> record;

  RecordList({this.record});

  factory RecordList.fromJson(List<dynamic> parsedJson) {
    List<Record> record = new List<Record>();
    record = parsedJson.map((i) => Record.fromJson(i)).toList();
    return new RecordList(
      record: record,
    );
  }
}

It can be that the value property of DropdownButton has the value which is not one of item 's value. value property should be either null or one of the item's value.

Also, maybe you don't need a FutureBuilder here. You can do something like that:

class _DispositionListState extends State<DispositionList> {
  bool _isLoading = true;
  String _currentSelectedValue;
  List<String> statusList = List<String>();

  @override
  void initState() {
    super.initState();
    _loadStatusList();
  }

  _loadStatusList() async {
    final recordList = await getRecord();
    final list = recordList.record.map((r) { 
      return r.status.trim();
    }).toSet().toList();
    setState(() {
      statusList = list;
      _currentSelectedValue = list.first;
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return isLoading ? CircularProgressIndicator() : _buildDropdown();
  }

  Widget _buildDropdown() {
    return DropdownButton(
      items: statusList.map((dropDownStringItem) {
        return DropdownMenuItem<String>(
          value: dropDownStringItem,
          child: SizedBox(
            width: 200.0,
            child: Text(
              dropDownStringItem,
              overflow: TextOverflow.ellipsis,
            ),
          ),
        );
      }).toList(),
      onChanged: (valueSelected) {
        onDropDownSelected(valueSelected);
      },
      value: _currentSelectedValue,
    );
  }

  void onDropDownSelected(String valueSelected) {
    setState(() {
      this._currentSelectedValue = valueSelected;
    });
  }
}

Update: Added .toSet() to filter out duplicates from statusList

I think the problem is that the status list has duplicated items. I added .toSet() before .toList() to filter out duplicates. Set is a collection of objects in which each object can occur only once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM