I need to have the list of add able dropdown of states,district. They can be added. I could add the forms but I need to set the district data according to the states.I get the states from the api. When selecting the states from dropdown I get the districts according to states from api.Its working fine with the district dropdown of index 0 but I am getting the error There should be exactly one item with [DropdownButton]'s value: 1. in other index. How can I achieve this. I followed this https://stackoverflow.com/a/63188955/8023701. I have implemented as follows:
Widget _buildStates(BuildContext context, int i) {
return
Container(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
state,
style: TextStyle(
// fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.red),
),
SizedBox(
width: 20,
),
DropdownButtonFormField(
validator: (value) => validateDrops(value),
isExpanded: true,
hint: Text(state),
value: _selectedState[i],
onChanged: (newValue) {
setState(() {
print("Stae value");
print(newValue);
_selectedState[i]= newValue;
getMyDistricts(newValue, i);
});
},
items: statess.map((Data item) {
return new DropdownMenuItem(
child: new Text(
item.provinceNepali,
),
value: item.id.toString(),
);
}).toList(),
)
],
));
}
District widget:
Widget _buildDistrict(BuildContext context, int i) {
return Container(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
district,
style: TextStyle(
// fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.red),
),
SizedBox(
width: 20,
),
mydis != null
? DropdownButtonFormField(
validator: (value) => validateDrops(value),
isExpanded: true,
hint: Text(select),
value: _selectedDistrict[i],
onChanged: (newValue) {
setState(() {
_selectedDistrict[i] = newValue;
getMyMuni(newValue, i);
});
},
items: mydis.map((Datas item) {
return new DropdownMenuItem(
child: new Text(
item.municipalityEnglish,
),
value: item.id.toString(),
);
}).toList(),
)
: Container()
],
));
}
I am getting states from api as follows:
void getAvgProvince() async {
setState(() {
_isLoading = true;
});
ProvinceResponse joinResponse = await getProvince();
if (joinResponse != null) {
setState(() {
statess = joinResponse.data;
_isLoading = false;
});
} else {
setState(() {
_isLoading = false;
});
}
}
And district as follows:
void getMyDistricts(newValue, int i) async {
DistrictResponse joinResponse = await getDistrict(newValue);
if (joinResponse != null) {
setState(() {
mydis = joinResponse.data;
_isLoading = false;
});
} else {
setState(() {
_isLoading = false;
});
}
}
The DropdownButton error There should be exactly one item with [DropdownButton]'s value: 1 occurs because the initial selected value of the dropdown is null. What you can do here is add a placeholder value for the dropdown if it's yet to be active. You can follow a similar approach on this answer.
Related
I have a dropdown list populated with users, I want to get the user id and pass to a function whenever a user is selected from the list
An unhandled exception is occurring instead
the exception
E/flutter (28482): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: Null check operator used on a null value
The below snippet is where how I am fetching the users
User? sid;
List<User> users = [];
//fetch users
Future<List<User>>? getUsers() async {
var result = await client.get(usersUrl);
return userFromJson(result.body);
}
Future<void> fetchandShow() async {
final users = await getUsers();
setState(() {
this.users = users ?? [];
});
}
#override
void initState() {
super.initState();
fetchandShow();
}
below is the dropdownbutton where I am displaying the users
DropdownButtonFormField<User>(
hint: Text('Select user'),
decoration: InputDecoration(
border: InputBorder.none,
),
value: sid,
items: users
.map((item) => DropdownMenuItem(
value: item,
child: Text(
item.username,
style: TextStyle(fontSize: 20.0),
),
))
.toList(),
onChanged: (item) => setState(() {
sid!.id = item as String?;
print(sid!.id);
}),
),
below is where i want to pass the user id
ElevatedButton(
onPressed: () async {
await createNote(
_bodyController.text, int.parse(sid!.id.toString()));
Navigator.pop(context);
},
child: Text('submit'),
)
Here "timeZoneType" is the List of data and once the user select any option from the dropdown, we will get the index ("accountIndex") of the item.
Once we get the index of the item, we can just get that index item details by
"timeZoneType[index]"
var detailData = timeZoneType[index]
Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: 45,
child: DropdownButtonHideUnderline(
child: Padding(
padding: const EdgeInsets.only(left: 20.0, right: 1),
child: DropdownButton(
hint: Text("Timezone", style: Constants.editTextStyleLight),
value: _currentSelectedValue.value,
items: timeZoneType
.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: Constants.editTextStyle,
),
);
}).toList(),
onChanged: (value) {
setState(() {
print(value);
accountIndex = timeZoneType.indexOf(value.toString());
print(accountIndex);
});
}),
),
),
),
],
)
Your item is of User type, Handle onChanged as below:
onChanged: (item) => setState(() {
sid = item;
}),
I am trying to use the dropdown menu in my flutter app but getting an error.
Here is the code:
List<String> items = ["Item1", "Item2", "Item3", "Item4"];
String selectedItem = "Item1";
DropdownButton<String>(
items: items.map(
(txt) {
return DropdownMenuItem<String>(
child: Text(
"$txt"
),
);
}
).toList(),
value: selectedItem,
)
In some questions, I saw that we have to initially set a variable to the value present inside our list. I have exactly done that but still getting an error.
Error Message:
There should be exactly one item with [DropdownButton]'s value: Item1.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 850 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1'
What is the error here?
Kindly comment if more information is needed.
Here an example, the explanation in the code:
class _MyHomePageState extends State<MyHomePage> {
List<String> items = ["Item1", "Item2", "Item3", "Item4"];
String selectedItem = "Item1";
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
Flex(direction: Axis.vertical, children:[
DropdownButton<String>(
value: selectedItem,
onChanged: (_value) { // update the selectedItem value
setState(() {
selectedItem = _value!;
});
},
items: items
.map<DropdownMenuItem<String>>((String _value) => DropdownMenuItem<String>(
value: _value, // add this property an pass the _value to it
child: Text(_value,)
)).toList(),
),
])
],
),
);
}
}
If you are loading the list from an api that returns list, look at what i did to debug the error.
Created a reusable widget that handle future response
Widget rangeLists(selectedValue) {
return FutureBuilder(
future: YourFuture,//this should return Future<List>
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading...');
} else {
List<DropdownMenuItem<String>> categoriesItems = [
DropdownMenuItem(
child: Text(selectedValue),
value: selectedValue,
),
];
print('categoriesItems.last.value');
print(categoriesItems.last.value);
var snapshotAsMap = snapshot.data as List;
for (int i = 0; i < snapshotAsMap.length; i++) {
if (snapshotAsMap[i]['category'] != selectedValue) {
categoriesItems.add(
DropdownMenuItem(
child: Text(snapshotAsMap[i]['category']),
value: snapshotAsMap[i]['category'],
),
);
}
}
return Padding(
padding: const EdgeInsets.only(left: 18.0, right: 18, top: 10),
child: Container(
padding: EdgeInsets.only(left: 25, right: 25),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 1),
borderRadius: BorderRadius.circular(25)),
child: DropdownButton<String>(
items: categoriesItems,
icon: const Icon(
Icons.expand_more,
color: Colors.grey,
),
iconSize: 24,
elevation: 16,
isExpanded: true,
style: const TextStyle(color: Colors.grey),
underline: SizedBox(),
onChanged: (value) {
setState(() {
widget.selectedValue = value;
});
},
value: selectedValue,
hint: Text('My courses'),
),
),
);
}
})};
2.Usage
you can called it like this
String selectedValue="Select Here"
rangeLists(selectedValue)//call this as a widget in ur ui
It will handle all list from the backend u don't need to worry about the error any more
List<String> items = ["Item1", "Item2", "Item3", "Item4"];
String selectedItem = "";
DropdownButton<String>(
items: items.map(
(txt) {
return DropdownMenuItem<String>(
child: Text("$txt"),
);
}
).toList(),
value: selectedItem==""null?"":selectedItem,
)
I am trying to develop a survey form using Flutter and I have multiple dropdown fields in the form. I want to get the selected values from those dropdowns when I click the save button. But all I am getting is the value I initially set inside initState(). The code I am using is as below. Any help to get this sorted out is much appreciated.
class _EditSurveyState extends State<EditSurvey> {
String contactMethod;
String country;
List contactMethodList = ['phone', 'email', 'mail'];
List countryList = ['us', 'uk', 'germany'];
#override
void initState() {
super.initState();
contactMethod = surveryData['contact'];
country = surveryData['country'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
return Scaffold(
children: [
Expanded(
flex: screenWidth(context) < 1300 ? 10 : 8,
child: SafeArea(
child: Column(
children: [
createDropdownField("Contact", contactMethod, contactMethodList),
createDropdownField("Country", country, countryList),
Row(mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed: () async {
print(contactMethod + country);
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
)
),
child: Text(
"UPDATE",
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
)
),
],
),
]
)
)
)
]
)
);
}
Row createDropdownField(String labelText, String _valueChoose, List valueList) {
return Row (
children: [
SizedBox(height: 25,),
Align(
alignment: Alignment.centerLeft,
child: Text(
'$labelText',
),
),
DropdownButtonFormField(
value: _valueChoose,
hint: Text("$labelText"),
icon: Icon(Icons.arrow_drop_down),
isExpanded: true,
onChanged: (newValue){
setState(() {
_valueChoose = newValue;
});
},
items: valueList.map((valueItem){
return DropdownMenuItem(
value: valueItem,
child: Text(valueItem),
);
}).toList(),
),
],
);
}
}
I don't understand why you using intitstate if you want to initialize value to String you can do it while declaring, try removing initstate and
Declare a variable first where you will store new value from dropdown onchange
i.e
class _EditSurveyState extends State<EditSurvey> {
String _currentValue;
DropdownButtonFormField(
onChanged: (val) =>
setState(() => _currentValue = val as String),
value: _currentValue ,
items: YourList.map((item) {
return DropdownMenuItem(
value: item,
child: Text('$item Items'),
);
}).toList(),
),
The app I'm building uses a Cupertino Picker that shows a list of items to select, in this case the names of the US States. The first item defaults to the first item in the list ('ak'), when the button to select the item is pressed, the app errors out. This only happens with the first item, when the picker isn't scrolled. If the picker is scrolled and the user goes back to the first item, it works fine.
class StateSelectScreen extends StatefulWidget {
static const String id = 'state_select_screen';
#override
_StateSelectScreenState createState() => _StateSelectScreenState();
}
class _StateSelectScreenState extends State<StateSelectScreen> {
String selectedState = 'ak';
bool showSpinner = false;
DropdownButton<String> androidDropdown() {
List<DropdownMenuItem<String>> dropdownItems = [];
for (String state in statesList) {
var newItem = DropdownMenuItem(
child: Text(
USStates.getName(state).toUpperCase(),
textAlign: TextAlign.center,
),
value: state,
);
dropdownItems.add(newItem);
}
return DropdownButton<String>(
dropdownColor: Colors.black26,
autofocus: true,
focusColor: Colors.black26,
style: TextStyle(
fontSize: k30PointFont,
),
value: selectedState,
items: dropdownItems,
onChanged: (value) {
setState(() {
selectedState = value;
getStateData();
});
},
);
}
CupertinoPicker iOSPicker() {
List<Text> pickerItems = [];
for (String state in statesList) {
pickerItems.add(Text(USStates.getName(state.toUpperCase())));
}
return CupertinoPicker(
backgroundColor: kCupertinoPickerBackgroundColor,
itemExtent: kCupertinoPickerItemExtent,
onSelectedItemChanged: (selectedIndex) {
setState(() {
selectedState = USStates.getName(statesList[selectedIndex]);
getStateData();
});
},
children: pickerItems,
);
}
Map<String, dynamic> selectedStateData = {};
bool isWaiting = false;
void getStateData() async {
isWaiting = true;
try {
var stateData = await GetData().getStateData(selectedState);
isWaiting = false;
setState(() {
selectedStateData = stateData;
});
} catch (e) {
print(e);
}
}
#override
void initState() {
super.initState();
getStateData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(kAppBarTitle),
),
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Padding(
padding: EdgeInsets.only(top: kStateSelectScreenFlexEdgeInsetsTop, bottom: kStateSelectScreenFlexEdgeBottom),
child: Hero(
tag: kHeroTag,
child: Container(
height: 200.0,
child: Image.asset(kHeroImageAsset),
),
),
),
),
Container(
child: Column(
children: <Widget>[
Container(
height: kStateSelectScreenContainerHeight,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: kStateSelectScreenContainerPaddingBottom),
child: Platform.isIOS ? iOSPicker() : androidDropdown(),
),
BottomButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ResultsScreen(
covidData: selectedStateData,
location: selectedState,
);
},
),
);
},
buttonTitle: kCheckStateResultsButtonTitle,
),
SizedBox(
height: kHeight15,
),
BottomButton(
onPressed: () {
Navigator.pushNamed(context, MenuScreen.id);
},
buttonTitle: kMainMenuButtonTitle,
),
],
),
),
],
),
),
);
}
}
I have recreated your problem, but don't see any error.
https://codepen.io/flakerimi/pen/poRywLZ
You said when the button to select the item is pressed, the app errors out.
Which button ? I don't see any
I have added scrollController: FixedExtentScrollController(initialItem: 1), but I don't think thats the case.
return CupertinoPicker(
scrollController: FixedExtentScrollController(initialItem: 1),
backgroundColor: Colors.white,
itemExtent: 30,
onSelectedItemChanged: (selectedIndex) {
setState(() {
selectedState = statesList[selectedIndex];
print(selectedState);
});
},
children: pickerItems,
);
I'm trying to build an app in flutter in which during quiz, I'm using radio buttons. I want to highlight the correct answer and the answer selected by the user if the correct answer is not selected by the user.
If the correct answer is selected then I just want to select the user selected answer.
I cannot find any way to do it.
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.content.getQuestion(),
style: Constants.articleQuestionStyle,
),
Container(),
Column(
children: widget.content
.getOptions()
.map<Widget>((value) => _buildRadioBtn(value))
.toList()),
//
// {
// return Row(children: [
// Radio(
// value: value,
// groupValue: widget.content.getGuess(),
// onChanged: (val){
// print("value: ${value}");
// print("isChecked: ${widget.content.isChecked()}");
// return //_buildRadioBtn(val);
//// widget.content.isChecked()
//// ? null :
// _buildRadioBtn(val);//_handleValueChanged(val);
// },
// activeColor: (widget.content.getGuess() == widget.content.getCorrectAnswer())? Colors.orange: Colors.red,
// ),
//
// Text(
// value,
// style: Constants.articleBodyTextStyle,
// )
// ]);
// }
// ).toList()),
and
_buildRadioBtn(value) {
// bool isCorrect = widget.content.getCorrectAnswer().contains(value);
// bool isChosen = widget.content.getGuess().contains(value);
return Row(
children: <Widget>[
Radio(
value: widget.content.isChecked(),
groupValue: widget.content.getGuess(),
onChanged: (value){
if(!widget.content.isChecked()) {
// print("ffffff");
// widget.content.registerGuess(value);
// print("abc");
// setState(() {});
_handleValueChanged(value);
}
},
activeColor: (
widget.content.getGuess() == widget.content.getCorrectAnswer())? Colors.orange: Colors.red,
),
Text(
// "hello",
value,
style: Constants.articleBodyTextStyle,
)
],
);
}
}
The way I think it will work is to rebuild the radio button once the user selects the answer, but I cannot do so. Please help.
Method: 1
String question = 'Q 1', answer = 'A 3', defaultValue = 'nil';
List<String> options = ['A 1', 'A 2', 'A 3', 'A 4'], info = ['', '', '', ''];
List<Color> bgs = [Colors.white, Colors.white, Colors.white, Colors.white];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListTile(title: Text(question)),
ListView.builder(
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (cc, ii) {
return Card(
color: bgs[ii],
child: ListTile(
title: Text(options[ii]),
subtitle: Text(info[ii]),
leading: Radio(
value: options[ii],
groupValue: defaultValue,
onChanged: (String value) {
setState(() {
defaultValue = value;
});
},
),
),
);
}),
RaisedButton(
onPressed: () {
if (defaultValue == answer) {
setState(() {
int ind = options.indexOf(defaultValue);
bgs[ind] = Colors.green;
info[ind] = 'Correct Answer !';
});
} else {
setState(() {
int wrongInd = options.indexOf(defaultValue);
bgs[wrongInd] = Colors.redAccent;
info[wrongInd] = 'Wrong Answer !';
int correctInd = options.indexOf(answer);
bgs[correctInd] = Colors.green;
info[correctInd] = 'Correct Answer !';
});
}
},
child: Text('Submit'))
],
),
),
);
}
Method: 2
String question = 'Q 1', answer = 'A 3', defaultValue = 'nil';
List<String> options = ['A 1', 'A 2', 'A 3', 'A 4'], info = ['', '', '', ''],radioValues=[];
List<Color> bgs = [Colors.black, Colors.black, Colors.black, Colors.black];
#override
void initState(){
super.initState();
radioValues.addAll(options);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListTile(title: Text(question)),
ListView.builder(
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (cc, ii) {
return ListTile(
title: Text(options[ii],
style:TextStyle(color:bgs[ii])),
subtitle: Text(info[ii],
style:TextStyle(color:bgs[ii])),
leading: Radio(
value: radioValues[ii],
groupValue: defaultValue,
onChanged: (String value) {
setState(() {
defaultValue = value;
});
},
),
);
}),
RaisedButton(
onPressed: () {
if (defaultValue == answer) {
setState(() {
int ind = options.indexOf(defaultValue);
bgs[ind] = Colors.green;
info[ind] = 'Correct Answer !';
});
} else {
setState(() {
int wrongInd = options.indexOf(defaultValue);
bgs[wrongInd] = Colors.redAccent;
info[wrongInd] = 'Wrong Answer !';
int correctInd = options.indexOf(answer);
bgs[correctInd] = Colors.green;
info[correctInd] = 'Correct Answer !';
radioValues[wrongInd] = defaultValue;
radioValues[correctInd] = defaultValue;
});
}
},
child: Text('Submit'))
],
),
),
);
}
I suggest that you leave the select state of what the user picked. What you can do is change the colors or styling of the text of the items to reflect which the user picked vs which is the correct answer.