Flutter DropdownButton - populate items from rest webservice
Solution 1
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, ...
Solution 2
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();
Solution 3
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.
user1209216
Updated on December 09, 2022Comments
-
user1209216 over 1 year
My
fetchCities()
method returnsFuture<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?
-
user1209216 about 5 yearsWy not use
FutureBuilder
? I managed to obtain future cities list ininitState
(by calling myfetchCities
method, then passed retrieved list toFutureBuilder
and no it works. -
Victor Godoy about 5 years@user1209216 Follow this answer and check that
value:
fromDropdownMenuItem
must be some kind of id, something to compare you can't compare a Object like you are doing in the question. -
user1209216 about 5 yearsI actually see no problems with this approach. Future retrieved in
initState
, passed toFutureBuilder
. I just movedfetchCities
to initState