简体   繁体   中英

Selection of Item in DropdownButton causes Flutter to throw error

I am currently trying to retrieve data (tags) from a REST API and use the data to populate a dropdown menu which I can successfully do but upon selection of the item, I get the following error which according to this would mean that the "selected value is not member of the values list":

items == null || value == null || items.where((DropdownMenuItem item) => item.value == value).length == 1': is not true.

This occurs after the dropdown menu shows my selected item. However, this is error should not be occurring as I've done the necessary logging to check that the data is indeed assigned to the list in question. Could anyone help me resolve this issue? I have isolated it to down to it originating in the setState() method in onChanged of DropdownButton but can't seem to understand why that should be causing an issue. Any help would be deeply appreciated!

My code is as follows:

class _TodosByTagsHomePageState extends State<TodosByTagsHomePage> {
    Tag selectedTag;

    final Logger log = new Logger('TodosByTags');

    @override
    Widget build(BuildContext context) {
      return Scaffold(
          appBar: AppBar(
            title: Text("Second Screen"),
          ),
          body: ListView(
              children: <Widget>[
                FutureBuilder<List<Tag>> (
                    future: fetchTags(),
                    builder: (context, snapshot) {
                      if (snapshot.hasData) {
                        log.info("Tags are present");
                        _tagsList = snapshot.data;
                        return DropdownButton<Tag>(
                          value: selectedTag,
                          items: _tagsList.map((value) {
                            return new DropdownMenuItem<Tag>(
                              value: value,
                              child: Text(value.tagName),
                            );
                          }).toList(),
                          hint: Text("Select tag"),
                          onChanged: (Tag chosenTag) {
                            setState(() {
                              log.info("In set state");
                              selectedTag = chosenTag;
                              Scaffold.of(context).showSnackBar(new SnackBar(content: Text(selectedTag.tagName)));
                            });
                          },
                        ) ;
                      } else if (snapshot.hasError) {
                        return Text("${snapshot.error}");
                      }

                      return Container(width: 0.0, height: 0.0);
                    }),
              ])
      );
    }

// Async method to retrieve data from REST API
      Future<List<Tag>> fetchTags() async {
        final response =
        await http.get(REST_API_URL);

        if (response.statusCode == 200) {
          // If the call to the server was successful, parse the JSON
          var result = compute(parseData, response.body);
          return result;
        } else {
          // If that call was not successful, throw an error.
          throw Exception('Failed to load post');
        }
      }

      static List<Tag> parseData(String response) {
        final parsed = json.decode(response);

        return (parsed["data"] as List).map<Tag>((json) =>
        new Tag.fromJson(json)).toList();
      }

      List<Tag> _tagsList = new List<Tag>();

    }

// Model for Tag
    class Tag {
      final String tagName;
      final String id;
      final int v;

      Tag({this.id, this.tagName, this.v});

      factory Tag.fromJson(Map<String, dynamic> json) {
        return new Tag(
          id: json['_id'],
          tagName: json['tagName'],
          v: json['__v'],
        );
      }
    }

update your code like this I think issues that when calling setState in FutureBuilder that call fetchTags() move fetchTags() to initState() for once call

class _TodosByTagsHomePageState extends State<TodosByTagsHomePage> {
      Tag selectedTag;
      Future<List<Tag>> _tags;
      @override
      void initState() {
         _tags = fetchTags();
        super.initState();
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text("Second Screen"),
            ),
            body: ListView(children: <Widget>[
              FutureBuilder<List<Tag>>(
                  future: _tags,
                  builder: (context, snapshot) {
                    if (snapshot.hasData) {
                      return DropdownButton<Tag>(
                        value: selectedTag,
                        items: snapshot.data.map((value) {
                          print(value);
                          return  DropdownMenuItem<Tag>(
                            value: value,
                            child: Text(value.tagName),
                          );
                        }).toList(),
                        hint: Text("Select tag"),
                        onChanged: (Tag chosenTag) {
                          setState(() {
                            selectedTag = chosenTag;
                          });
                        },
                      );
                    } else if (snapshot.hasError) {
                      return Text("${snapshot.error}");
                    }

                    return Container(width: 0.0, height: 0.0);
                  }),
            ]));
      }

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