Reputation: 45
I am new to flutter and i am facing problems with populating dropdown from json. I have been following a solution i found on internet but am now stuck with the exception below. I cant seem to figure out how to set the initial value. My idea though is to have the initial value set as PLEASE SELECT. Assistance is appreciated:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following assertion was thrown building FutureBuilder<List<Country>>(dirty, state:
_FutureBuilderState<List<Country>>#6f8a4):
There should be exactly one item with [DropdownButton]'s value: Instance of 'Country'.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 894 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1'
My code looks like below:
import 'dart:convert';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
home: Location(),
));
}
Future<List<Country>> fetchCountries() async {
final responseJson = json.decode("""
{
"data": [
{
"Country_str_code": "AL",
"Region_str_code": "Eur",
"Country_str_name": "Albania"
},
{
"Country_str_code": "DZ",
"Region_str_code": "Afr",
"Country_str_name": "Algeria"
}
]
}
""");
print(responseJson['data']);
return (responseJson['data'] as List)
.map((country) => Country.fromJson(country))
.toList();
}
class Country {
final String countryCode;
final String countryName;
Country({required this.countryCode, required this.countryName});
factory Country.fromJson(Map<String, dynamic> json) {
return Country(
countryCode: json['Country_str_code'],
countryName: json['Country_str_name'],
);
}
}
class Location extends StatefulWidget {
@override
State<StatefulWidget> createState() => LocationState();
}
class LocationState extends State<Location> {
late Future<List<Country>> futureCountry;
Country _selected = Country(countryCode: "AL", countryName: "Albania");
@override
void initState() {
super.initState();
futureCountry = fetchCountries();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Registrate'),
backgroundColor: Colors.blue[600],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: FutureBuilder<List<Country>>(
future: futureCountry,
builder: (context, snapshot) {
if (snapshot.hasData) {
return DropdownButton(
value: _selected,
icon: Icon(Icons.arrow_drop_down),
iconSize: 30,
elevation: 16,
style: TextStyle(color: Colors.black),
onChanged: (newValue) {
setState(() {
_selected = newValue as Country;
});
},
items: snapshot.data
?.map<DropdownMenuItem<Country>>((Country value) {
return DropdownMenuItem<Country>(
value: value,
child: Text(value.countryName),
);
}).toList(),
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
SizedBox(
width: double.maxFinite,
child: ElevatedButton(
onPressed: () {},
child: Text('Continue'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.red),
foregroundColor:
MaterialStateProperty.all<Color>(Colors.white),
)),
),
],
),
);
}
}
Upvotes: 1
Views: 135
Reputation: 853
You are creating a new Country
and setting it to _selected
and that country is also found within your futureCountry
list creating duplicates in the list. Instead, you should set _selected
to null and set a new value (Country) when Dropdown value changes.
class Location extends StatefulWidget {
@override
State<StatefulWidget> createState() => LocationState();
}
class LocationState extends State<Location> {
late Future<List<Country>> futureCountry;
Country? _selected; // make this variable to nullable
@override
void initState() {
super.initState();
futureCountry = fetchCountries();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Registrate'),
backgroundColor: Colors.blue[600],
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Center(
child: FutureBuilder<List<Country>>(
future: futureCountry,
builder: (context, snapshot) {
if (snapshot.hasData) {
return DropdownButton(
hint: const Text("PLEASE SELECT"), // Add a hint here
value: _selected, // If value is null hint will be displayed.
icon: Icon(Icons.arrow_drop_down),
iconSize: 30,
elevation: 16,
style: TextStyle(color: Colors.black),
onChanged: (newValue) {
setState(() {
_selected = newValue as Country;
});
},
items: snapshot.data
?.map<DropdownMenuItem<Country>>((Country value) {
return DropdownMenuItem<Country>(
value: value,
child: Text(value.countryName),
);
}).toList(),
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
),
SizedBox(
width: double.maxFinite,
child: ElevatedButton(
onPressed: () {},
child: Text('Continue'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.red),
foregroundColor:
MaterialStateProperty.all<Color>(Colors.white),
)),
),
],
),
);
} }
Upvotes: 2