6
votes

My fetchCities() method returns Future<List<City>> and it loads data from rest webservice. Code to populate items:

 Widget buildCitiesSelector(){

    return new Center(
        child: FutureBuilder(
            future: fetchCities() ,
            builder: (context, snapshot){
              if (snapshot.hasData) {
                return new DropdownButton <City>(
                    hint: Text('Wybierz miasto'),
                  items: snapshot.data.map<DropdownMenuItem<City>>((City value) {
                    return DropdownMenuItem<City>(
                      value: value,
                      child: Text(value.name),
                    );
                  }).toList(),
                  onChanged: (value) {
                      setState(() {_selectedCity = value;});
                  },
                  value: _selectedCity,
                );
              }
              else{
                return CircularProgressIndicator();
              }
            }

        )

    );
  }

Result: items are correctly displayed in selector. However, when selecting any particular item, I'm getting exception:

I/flutter (13910): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ I/flutter (13910): The following assertion was thrown building FutureBuilder>(dirty, state: I/flutter (13910): _FutureBuilderState>#dacd9): I/flutter (13910): 'package:flutter/src/material/dropdown.dart': Failed assertion: line 560 pos 15: 'items == null || I/flutter (13910): items.isEmpty || value == null || items.where((DropdownMenuItem item) => item.value == I/flutter (13910): value).length == 1': is not true.

How to properly select item? Any ideas what's wrong?

4
I think your problem is very similar to mine: stackoverflow.com/questions/61720324/…Bliv_Dev

4 Answers

5
votes

You should not use FutureBuilder for this situation. Rather fetch the data in initState() and then cause a rebuild using setState() to update the view.

If fetchCities() creates a new Future every time it is called, then build() will invoke that fetch every time the UI is rebuilt (which can be quite often)

https://docs.flutter.io/flutter/widgets/FutureBuilder-class.html

The future must have been obtained earlier, e.g. during State.initState, ...

2
votes
      child: FutureBuilder(
            future: Webservice().load(Country.countries) ,
            builder: (context, snapshot){
              if(snapshot.hasError)
                return Text(snapshot.error);

              if (snapshot.hasData) {
                return  DropdownButtonFormField(
                  decoration: new InputDecoration(icon: Icon(Icons.language)), //, color: Colors.white10
                  value: selectedCountry,
                  items: snapshot.data.map<DropdownMenuItem<Country>>((Country country) {

                       return  DropdownMenuItem<Country>(

                                value: country,
                                child: Text(country.name, style: TextStyle(color: Color.fromRGBO(58, 66, 46, .9))),

                              );
                  })
                          .toList(),


                  onChanged: (Country newValue) {
                      setState(() => selectedCountry = newValue);
                      // selectedCountry = newValue;
                      print(newValue.id);
                      print(newValue.name);
                  },
               );

              }
              return CircularProgressIndicator();
1
votes

I had the same issue today, and after some digging, I found a mistake in my code: the value in the DropdownButton wasn't in the items list.

I assumed (wrongly) that the dropdown would handle the "empty" value - but that is not the case.

0
votes

You can declare a Future and init in initState and in FutureBuilder use this future.

City cityModel;
Future _future;

@override
  void initState() {
    _future = fetchCities();
    super.initState();
  }

body: FutureBuilder<List<City>>(
          future: _future,