I am new to Flutter but I am trying to create a DropdownButtonFormField and it is not working. I am getting an error that says I have duplicate values. What is interesting is that I do not have a list with duplicate values. I have found a similar question on SO and the solution says to initiate the strings with a value and that the user was duplicating a list item, but I have a similar solution for another another list and it seems to work fine. I cannot figure out why it is not working here. Any help is greatly appreciated.
Error Message:
There should be exactly one item with [DropdownButton]'s value: 0.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 1411 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1'
The relevant error-causing widget was:
StreamBuilder<UserProfile>
ProfileForm Class:
final List<String> accountType = ['Educator', 'School Administrator', 'Parent'];
String _currentAccountType;
@override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
return StreamBuilder<UserProfile>(
stream: DatabaseService(uid: user.uid).userProfileData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserProfile userProfileData = snapshot.data;
return Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
DropdownButtonFormField(
decoration: textInputDecoration,
value: _currentAccountType ?? userProfileData.accountType,
items: accountType.map((accountType) {
return DropdownMenuItem(
value: accountType,
child: Text(accountType),
);
}).toList(),
onChanged: (val) {
setState(() {
_currentAccountType = val;
});
},
),
Database Class
class DatabaseService {
final String uid;
DatabaseService({this.uid});
final CollectionReference userProfileCollection =
Firestore.instance.collection('user_profile');
Future updateUserProfile(
String accountType,
String birthDate,
String courseName,
String dateJoined,
String email,
String firstName,
String lastName,
String schoolName,
String title) async {
return await userProfileCollection.document(uid).setData({
'accountType': accountType,
'birthDate': birthDate,
'courseName': courseName,
'dateJoined': dateJoined,
'email': email,
'firstName': firstName,
'lastName': lastName,
'schoolName': schoolName,
'title': title,
});
}
//User Profile from snapshot
List<Profile> _userProfileListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Profile(
accountType: doc.data['accountType'] ?? '',
birthDate: doc.data['birthDate'] ?? '',
courseName: doc.data['courseName'] ?? '',
dateJoined: doc.data['dateJoined'] ?? '',
email: doc.data['email'] ?? '',
firstName: doc.data['firstName'] ?? '',
lastName: doc.data['lastName'] ?? '',
schoolName: doc.data['schoolName'] ?? '',
title: doc.data['title'] ?? '',
);
}).toList();
}
UserProfile _userProfileFromSnapshot(DocumentSnapshot snapshot) {
return UserProfile(
uid: uid,
accountType: snapshot.data['accountType'],
birthDate: snapshot.data['birthDate'],
courseName: snapshot.data['courseName'],
dateJoined: snapshot.data['dateJoined'],
email: snapshot.data['email'],
firstName: snapshot.data['firstName'],
lastName: snapshot.data['lastName'],
schoolName: snapshot.data['schoolName'],
title: snapshot.data['title'],
);
}
Stream<List<Profile>> get userProfile {
return userProfileCollection.snapshots().map(_userProfileListFromSnapshot);
}
Stream<UserProfile> get userProfileData {
return userProfileCollection
.document(uid)
.snapshots()
.map(_userProfileFromSnapshot);
}
}
userProfileData.accountType is '0', not 'Educator' or 'School Administrator' or 'Parent'.
Success: value must in items.value
final List<String> accountType = ['Educator', 'School Administrator', 'Parent'];
DropdownButtonFormField(
decoration: textInputDecoration,
value: accountType[0],
items: accountType.map((accountType) {
return DropdownMenuItem(
value: accountType,
child: Text(accountType),
);
}).toList(),
onChanged: (val) {
setState(() {
_currentAccountType = val;
});
},
),
Failed: There should be exactly one item with [DropdownButton]'s value: hahaha
final List<String> accountType = ['Educator', 'School Administrator', 'Parent'];
DropdownButtonFormField(
decoration: textInputDecoration,
value: 'hahaha',
items: accountType.map((accountType) {
return DropdownMenuItem(
value: accountType,
child: Text(accountType),
);
}).toList(),
onChanged: (val) {
setState(() {
_currentAccountType = val;
});
},
),
It seems that there is some clash when using 'hint:' and 'value:' simultaneously. My approach was to add a global _selected in the Widget's State:
bool _selected;
and then in the DropdownButton itself:
value: _selected ? _userChoice: null,
In this way you actually use one of the item values once they are set.
Here is an entire example taken from some traning code (excuse the silliness ;)
class FavoriteCity extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _FavoriteCityState();
}
}
class _FavoriteCityState extends State<FavoriteCity> {
String nameCity = '';
String _loveLevel = '';
bool _selected = false;
var _howMuchLoved = ['A little', 'So so', 'Quite a bit', 'A lot', 'Greatly'];
@override
Widget build(BuildContext context) {
print('Widget Built');
return Scaffold(
appBar: AppBar(
title: Text('Favorite city app'),
elevation: 8.0,
),
body: Container(
margin: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
TextField(
onSubmitted: (String userInput) {
setState(() {
print('State rebuilt');
nameCity = userInput;
});
},
),
DropdownButton<String>(
hint: Text('How much do you love the city?'),
items: _howMuchLoved.map((String myMenuItem) {
return DropdownMenuItem<String>(
value: myMenuItem,
child: Text(myMenuItem),
);
}).toList(),
onChanged: (String valueSelectedByUser) {
_dropDownItemSelected(valueSelectedByUser);
},
value: _selected ? _loveLevel : null,
isDense: true,
),
Padding(
padding: EdgeInsets.all(20.0),
child: Text(
'Your favorite city is $nameCity ! \n ... and you love it $_loveLevel',
style: TextStyle(
fontSize: 20.0,
fontStyle: FontStyle.italic,
),
),
),
],
),
),
);
}
void _dropDownItemSelected(String valueSelectedByUser) {
setState(() {
this._loveLevel = valueSelectedByUser;
_selected = true;
});
}
In my case, below change resolved the error:
Error scenario:
var itemList=['Alpha','Beta','Cat'];
var itemSelected='Zebra';
Working scenario:
var itemList=['Alpha','Beta','Cat'];
var itemSelected='Cat'; //Can be Alpha, Beta or Cat but not any other value
Dropdown widget code:
DropdownButton<String>(
items: itemList.map((String singleItem){
return DropdownMenuItem<String>(
value: singleItem,
child:Text(singleItem)
);
}).toList(),
onChanged: (String itemChosen){
setState(() {
this.itemSelected=itemChosen;
});
},
value:itemSelected ,
),
Having the 'itemSelected' variable value same as one of the elements in the list resolved the problem for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With