Cannot Select Checkbox List Tile Flutter - flutter

I've created a map of values to make checkboxes. The map consists of String and bools and when the value of the bool is changed, the check box value should change.
EDIT 1:
My ListView Checkbox
CheckboxListTile(
title: new Text(
key,
style: textHeaderStyle,
),
value: _selectedOrders.contains(undeliveryOrders[key]),
activeColor: Colors.pink,
checkColor: Colors.white,
onChanged: (bool value) {
setState(() {
if(value){
_selectedOrders.add(undeliveryOrders[key]);
undeliveryOrders[key] = value;
}else{
setState(() {
_selectedOrders.remove(undeliveryOrders[key]);
});
}
});
},
)
Creating the Map:
void _formatOrders(availableOrders) {
for (int i = 0; i < availableOrders.length; i++) {
var tempOrder = '${availableOrders[i].customer.uniqueInfo.name} , ${availableOrders[i].address}';
undeliveryOrders['$tempOrder'] = false;
}
print('$undeliveryOrders');
print('$numbers');
}
Selected Order Method
var _selectedOrders = [];
getItems() {
undeliveryOrders.forEach((key, value) {
if (value == false) {
_selectedOrders.add(key);
}
});
print(_selectedOrders);
_selectedOrders.clear();
}

I think you might be over complicating things every value does not have to be mapped to a boolean the way I do it is I add the value that gets checked to an array then check if that item is in that array if it is its true if not its false. You just have to remember to remove the item if the checkbox is unchecked here is some sample code for you.
List<String> items = ['Item 1', 'Item 2', 'Item 3'];
List<String> isChecked = [];
//Initialized outside build
ListView(
children: <Widget>[
...items
.map(
(item) => CheckboxListTile(
subtitle: Text('This is a subtitle'),
secondary: Text('This is Secondary text'),
title: Text(item),
value: isChecked.contains(item),
onChanged: (bool value) {
if (value) {
setState(() {
isChecked.add(item);
});
} else {
setState(() {
isChecked.remove(item);
});
}
},
),
)
.toList()
],
),

In my case, the setState() is not responding, try to use StatefulBuilder().
For Example:
...
bool isSelected = false;
StatefulBuilder(
builder: (context, _setState) {
return ListView(
children: [
CheckboxListTile(
value: isSelected,
onChanged: (bool? value) {
_setState(() => isSelected = value);
},
),
],
);
},
);
...

You can try with this code bellow ?
onChanged: (bool value) {
setState(() {
undeliveryOrders[key] = value ?? false;
});
},

Related

How to get DropdownMenuItem from a list

I have tried to use a function and for in loop to loop through a list and then create dropdownMenuItems. I am getting this error 'There should be exactly one item with [DropdownButton]'s value Either zero or 2 or more [DropdownMenuItem]s were detected with the same value'. I have look through similar solutions stating that the default value should be one of the values of the list which is not my case
Below is the list
const List<String> currenciesList = [
'AUD',
'BRL',
'CAD',
'CNY',
'EUR',
'GBP',
'HKD',
'IDR',
'ILS',
'INR',
'JPY',
'MXN',
'NOK',
'NZD',
'PLN',
'RON',
'RUB',
'SEK',
'SGD',
'USD',
'ZAR'
];
and the loop
String selectedCurrency = 'USD';
List<DropdownMenuItem<String>> dropdownItems = [];
List<DropdownMenuItem<String>> getDropDownItems() {
for (String currency in currenciesList) {
var newItem = DropdownMenuItem(
child: Text(currency),
value: currency,
);
dropdownItems.add(newItem);
}
return dropdownItems;
}
lastly the dropdownbutton
child: DropdownButton<String>(
value: selectedCurrency,
items: getDropDownItems(),
onChanged: (value) {
setState(() {
selectedCurrency = value!;
});
},
Please help me understand what i must have done wrong
Your code-snippet
DropdownButton<String>(
value: selectedCurrency,
items: getDropDownItems(), ///<= adding items on every state-build
onChanged: (value) {
setState(() {
selectedCurrency = value!;
});
},
On state changes means after you click on DropdownMenuItem you are calling again getDropDownItems(), in this case our it will add DropdownMenuItem again to dropdownItems, and so the DropdownButton having duplicate values, and you are getting errors.
Use initState to call it once, or just initialize the dropdownMenuItem.
Here is the Solution Widget
class _ItemSectionState extends State<ItemSection> {
List<DropdownMenuItem<String>> dropdownItems = []; //* you can make nullable if you want, I'm doing it to force having String.
String selectedCurrency = 'USD';
#override
void initState() {
super.initState();
dropdownItems = List.generate(
currenciesList.length,
(index) => DropdownMenuItem(
value: currenciesList[index],
child: Text(
currenciesList[index],
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
DropdownButton<String>(
items: dropdownItems,
value: selectedCurrency,
onChanged: (value) {
setState(() {
selectedCurrency = value!;
});
},
),
],
),
);
}
}
The error occurs due to duplicate values..
When you try to change the value of the drop down, the getDropDownItems function is rerun and the logic in there just duplicates the values for the dropdown.
a quick fix would be to simply map over the currenciesList as opposed to writing a function to add the widgets to a List as shown below:
String selectedCurrency = 'USD';
...
child: DropdownButton<String>(
value: selectedCurrency,
items: currenciesList.map((currency) => DropdownMenuItem(
child: Text(currency),
value: currency,
),
),
onChanged: (value) {
setState(() {
selectedCurrency = value!;
});
},
...

StatefulWidget - FLutter

I need to edit this code, in a way to define only one variable widget which can be able to change on every state to a different widget type.
I need to be able to make a dynamic form no matter what the question and its type is, the way i handle it is somehow complex and not efficient.
so is there any idea on how to change the same variable for different widget on every setState()
`Column(
children: <Widget>[
questionText,
textCounter > 0 ? textField : SizedBox(),
selectCounter > 0 ? selectField : SizedBox()
],
)),`FutureBuilder(
future: fetchQuestions(),
builder: (context, snapshot) {
if (snapshot.hasData) {
for (var i = 0; i < snapshot.data.length; i++) {
var temp = snapshot.data[i]['question_value'].toString();
var type = snapshot.data[i]['question_type'].toString();
questionsList.add(temp);
typeList.add(type);
}
return Align(
alignment: Alignment.bottomRight,
child: RaisedButton(
onPressed: () {
changeQuest(questionsList, typeList,
snapshot.data.length, snapshot.data);
},
child: Text('next'),
),
);
} else
return Center(child: CircularProgressIndicator());
},
),
changeQuest(List questions, List type, length, data) {
setState(() {
textCounter = 0;
selectCounter = 0;
integerCounter = 0;
if (counter < length) {
questionText = Text(questions[counter]);
if (type[counter] == 'Integer') {
textCounter++;
textField = TextFormField(
decoration: new InputDecoration(labelText: "Enter your number"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
WhitelistingTextInputFormatter.digitsOnly
], // Only numbers can be entered
);
} else if (type[counter] == 'Text') {
textCounter++;
textField = TextFormField(
decoration: new InputDecoration(labelText: "Enter a text"),
keyboardType: TextInputType.text,
);
} else if (type[counter] == 'Select') {
selectCounter++;
for (var i = 0; i < data[counter]['answers'].length; i++) {
answersList
.add(data[counter]['answers'][i]['answer_value'].toString());
}
dropDownValue = answersList[0];
selectField = DropdownButton<String>(
value: dropDownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (value) {
setState(() {
dropDownValue = value;
});
},
items: answersList
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
print (dropDownValue);
}
}
counter++;
});
}
as #proversion said in the comments, you can check in the widget tree, if a condition returns true or false.
Before you enter the child you could check with an inline if-statement like so:
questionType == 'dropdown' ? (Widget for True) : (Widget for False)
Or if you have to do a complex check, I would do this in the build Method before the return of the widget and set a boolean value there, which represents your check result.
Then you can use this value (example: isTrue) in the widget tree like isTure ? (Widget for True) : (Widget for False).
Here is a sample code, that should work.
import 'package:flutter/material.dart';
class WidgetWithDifferentChildren extends StatefulWidget {
#override
_WidgetWithDifferentChildrenState createState() =>
_WidgetWithDifferentChildrenState();
}
class _WidgetWithDifferentChildrenState
extends State<WidgetWithDifferentChildren> {
String questionType = '';
String dropdownValue = 'SelectItem';
String textValue = '';
TextEditingController txtCtrl = TextEditingController();
#override
void dispose() {
// TODO: implement dispose when using TextEditingController
txtCtrl.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: questionType == ''
? Text('no Question Type')
: questionType == 'dropdown'
? DropdownButton<String>(
value: dropdownValue,
onChanged: (String newValue) {
// Do something with the new Value
print('New DropDown value = $newValue');
setState(() {
dropdownValue = newValue;
});
},
items: <String>[
'SelectItem',
'Item 1',
'Item 2',
'Item 3',
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
)
: questionType == 'textfield'
? TextFormField(
controller: txtCtrl,
onChanged: (value) {
// Do something with the new Value
print('New TextField value = $value');
setState(() {
textValue = value;
});
},
)
: Text('Question Type does not match'),
);
}
}
UPDATE
acc. to your provided code, you may want to check the following. I created a separate class which will return the right widget for the question. Just pass the type and additional the dropDownList to the function.
General I would suggest to store the questions and the corresponding answers in the same array, this would be a easy call of the function like getInputWidget(type:question[i].type, dropDownList:question[i].dropDownList).
Source Code for above example
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class WidgetWithDifferentChildren extends StatefulWidget {
#override
_WidgetWithDifferentChildrenState createState() =>
_WidgetWithDifferentChildrenState();
}
class _WidgetWithDifferentChildrenState
extends State<WidgetWithDifferentChildren> {
String questionType = '';
String inputValue = '';
List<String> answers = [];
int questionID = 0;
TextEditingController txtCtrl = TextEditingController();
List<Map<String, String>> questionList = [
{'question_value': 'text question ', 'question_type': 'text'},
{'question_value': 'number question ', 'question_type': 'number'},
{'question_value': 'select question ', 'question_type': 'select'},
{'question_value': 'last question ', 'question_type': 'text'},
];
List<String> dropDownList = [
'Select an Item',
'Answer A',
'Answer B',
'Answer C',
];
#override
void dispose() {
// TODO: implement dispose when using TextEditingController
txtCtrl.dispose();
super.dispose();
}
Widget getInputWidget({#required String type, List<String> dropDownList}) {
Widget inputW;
if (type == 'number' || type == 'text') {
inputW = TextFormField(
controller: txtCtrl,
decoration: new InputDecoration(labelText: "Enter a $type"),
keyboardType:
type == 'text' ? TextInputType.text : TextInputType.number,
inputFormatters: <TextInputFormatter>[
type == 'text'
? LengthLimitingTextInputFormatter(50)
: WhitelistingTextInputFormatter.digitsOnly
], // Only numbers can be entered
onChanged: (value) {
setState(() {
inputValue = value;
});
},
);
} else if (type == 'select') {
if (inputValue.length == 0) {
// set the input Value for the first time
inputValue = dropDownList[0];
}
inputW = DropdownButton<String>(
value: inputValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (value) {
setState(() {
inputValue = value;
});
},
items: dropDownList.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
return inputW;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
RaisedButton(
onPressed: () {
setState(() {
answers.add(inputValue);
inputValue = '';
txtCtrl.clear();
questionID = questionID + 1;
});
// unfocus to close the Keyboard
// conrtibution to: https://flutterigniter.com/dismiss-keyboard-form-lose-focus/
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
},
child: Text('next'),
),
getInputWidget(
type: questionList[questionID]['question_type'],
dropDownList: dropDownList),
Divider(thickness: 2),
Text('You enter: $inputValue'),
Divider(thickness: 2),
Text('Your answers are:'),
Flexible(
child: ListView.builder(
itemCount: answers.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('$index. ${answers[index]}'),
);
}),
),
],
),
),
);
}
}

Flutter - I have two issue with RadioListTile

I have a problem with RadioListTile it doesn't work when I make the change that does not change unless I leave it and return to it I find it changed.
The second problem is I want to save the option when the application is run for the first time, it is the first choice.
if someone has answered, please?
this is my code:
int radioValue = 0;
int tafIbnkatheer = 1;
int tafBaghawy = 2;
int tafQurtubi = 3;
int tafSaadi = 4;
int tafTabari = 5;
var showTaf;
void handleRadioValueChanged(int val) {
TranslateRepository2 translateRepository2 = new TranslateRepository2();
TranslateRepository translateRepository = new TranslateRepository();
TranslateRepository3 translateRepository3 = new TranslateRepository3();
TranslateRepository4 translateRepository4 = new TranslateRepository4();
TranslateRepository5 translateRepository5 = new TranslateRepository5();
setState(() {
radioValue = val;
switch (PrefService.getInt('$radioValue')) {
case 1:
setState(() {
showTaf = translateRepository2;
});
break;
case 2:
setState(() {
showTaf = translateRepository;
});
break;
case 3:
setState(() {
showTaf = translateRepository3;
});
break;
case 4:
setState(() {
showTaf = translateRepository4;
});
break;
case 5:
setState(() {
showTaf = translateRepository5;
});
break;
}
print(showTaf);
});
}
#override
void initState() {
setState(() {
radioValue = 1;
});
super.initState();
}
...
RadioListTile(
value: tafIbnkatheer,
groupValue: radioValue,
title: Text("Radio 1"),
subtitle: Text("Radio 1 Subtitle"),
onChanged: (val) {
setState(() {
handleRadioValueChanged(val);
});
},
activeColor: Colors.red,
selected: true,
),
RadioListTile(
value: tafBaghawy,
groupValue: radioValue,
title: Text("Radio 2"),
subtitle: Text("Radio 2 Subtitle"),
onChanged: (val) {
setState(() {
handleRadioValueChanged(val);
});
},
activeColor: Colors.red,
selected: false,
),
RadioListTile(
value: tafQurtubi,
groupValue: radioValue,
title: Text("Radio 3"),
subtitle: Text("Radio 3 Subtitle"),
onChanged: (val) {
setState(() {
handleRadioValueChanged(val);
});
},
activeColor: Colors.red,
selected: false,
),
RadioListTile(
value: tafSaadi,
groupValue: radioValue,
title: Text("Radio 4"),
subtitle: Text("Radio 4 Subtitle"),
onChanged: (val) {
setState(() {
handleRadioValueChanged(val);
});
},
activeColor: Colors.red,
selected: false,
),
RadioListTile(
value: tafTabari,
groupValue: radioValue,
title: Text("Radio 5"),
subtitle: Text("Radio 5 Subtitle"),
onChanged: (val) {
setState(() {
handleRadioValueChanged(val);
});
},
activeColor: Colors.red,
selected: false,
),
...
To make a Radio List Tile selected, as the documentation says, you can use checked. In fact
checked → bool
Whether this radio button is checked
selected → bool
Whether to render icons and text in the activeColor.

List of Radio button in flutter

For my project i display a list of football game with the choice for the user of (3 radios buttons with value "1" "N" or "2").
Example : i need list of football game like that :
Arsenal-Tottenham 1 N 2
Liverpool-Manchester 1 N 2
PSG-Dortmund 1 N 2
1 N 2 are radio button, each member can so choose between the 3 value for each game. The problem is i Don't see how identify each radio button for be unique. I need at the end display a validation button which allows the members to save the result of each choice. For make simple if you choose 1 for the 3 games i need to pass "111" to an external server by php api
Here is my code for display the form with radio button : Note that the games are from a list (values.map), so number of games can vary
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
// Create a Form widget.
class Affiche_grille extends StatefulWidget {
#override
_Affiche_grille_State createState() {
return _Affiche_grille_State();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class _Affiche_grille_State extends State<Affiche_grille> {
#override
final _formKey = GlobalKey<FormState>();
Future <List<Match>> Grille_display() async {
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/display_grid.php';
// Store all data with Param Name.
var data = {'id_grille': 1};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into variable.
var jsondata = json.decode(response.body);
List<Match> Matchs = [];
for (var u in jsondata) {
Match match = Match(u["equipe1"],u["equipe2"],u["type_prono"]);
Matchs.add(match);
}
return Matchs;
}
List<String> radioValues = [];
#override
Widget build(BuildContext context) {
final appTitle = 'MONEYFREE';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: Container(
child:
FutureBuilder(
future: Grille_display(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container (
child: Center(
child: Text("Loading...")
)
);
}
else {
List<Match> values = snapshot.data;
values.forEach((m){
radioValues.add("N");
//like N or something
});
print('valeur radio après initialisation');
print(radioValues);
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DataTable(
columnSpacing: 20,
columns: [
DataColumn(
label: Text("Libelle Match"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("1"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("N"),
numeric: false,
tooltip: "",
),
DataColumn(
label: Text("2"),
numeric: false,
tooltip: "",
),
],
rows:
List.generate(values.length, (index) {
return DataRow(
cells: [
DataCell(
Text(values[index].equipe1.toString() + " - " + values[index].equipe2.toString()),
),
DataCell(
Radio(
value: "1",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print('Change 1');
print(radioValues);
});
},
),
),
DataCell(
Radio(
value: "N",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print(radioValues);
});
},
),
),
DataCell(
Radio(
value: "2",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
print(radioValues);
});
},
),
),
]
);
}).toList(),
),
Center(
child: RaisedButton(
color: Colors.green,
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(9, 9, 9, 9),
child: Text('VALIDER VOTRE GRILLE'),
onPressed: () {
Valide_grille();
},
),
),
],
)
);
};
},
),
),
),
);
}
Future Valide_grille() async{
// For CircularProgressIndicator.
bool visible = false ;
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// SERVER LOGIN API URL
var url = 'http://www.axis-medias.fr/game_app/valide_grid.php';
// Store all data with Param Name.
var data = jsonEncode(radioValues);
print(radioValues);
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into variable.
var message = json.decode(response.body);
// If the Response Message is Matched.
if(message == 'OK')
{
print('VALIDATION DE LA GRILLE OK');
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
}else{
// If Email or Password did not Matched.
// Hiding the CircularProgressIndicator.
setState(() {
visible = false;
});
// Showing Alert Dialog with Response JSON Message.
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text(message),
actions: <Widget>[
FlatButton(
child: new Text("OK"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
class Match {
final String equipe1;
final String equipe2;
final String typeprono;
const Match(this.equipe1, this.equipe2, this.typeprono);
}
Create a global list
List<String> radioValues = [];
and assign each default value where you are assigning data to radios
values.forEach((m){
radioValues.add(/*assign default values here*/);
//like N or something
});
rows: List.generate(values.length, (index){
return DataRow(
cells: [
DataCell(
Text(values[index].equipe1.toString()+" - "+match.equipe2.toString()),
),
DataCell(
Radio(
value:"1",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
DataCell(
Radio(
value:"N",
groupValue: radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
DataCell(
Radio(
value:"2",
groupValue:radioValues[index],
onChanged: (val) {
setState(() {
radioValues[index] = val;
});
},
),
),
]
);
}).toList(),
If you want to create a list of Radio button use RadioListTile
For details check out this link: https://api.flutter.dev/flutter/material/RadioListTile-class.html
-- i hope this will be helpful for you
You can use Custom Radio group list package available on pub.dev
https://pub.dev/packages/custom_radio_group_list
If you want to Generate a radio button list using a list of string
Just pass list of string like this.
You can set selected item by just passing index of it.
RadioGroup(
radioList: sampleListString,
selectedItem: 1,
onChanged: (value) {
print('Value : ${value}');
},
disabled: true)
If you want to generate a radio button list using any Object.
Just turn that Object to a Map and create a list of Map and key whose data needs to be displayed.
Then use it like below.
RadioGroup(
radioListObject: sampleList,
textParameterName: 'data',
selectedItem: 1,
onChanged: (value) {
print('Value : ${value}');
} )

Error: Either zero or 2 or more [DropdownMenuItem]s were detected with the same value I/flutter (18363): 'package:flutter/src/material/dropdown.dart':

Error code
Hi I'm new to flutter and have a question about dropdownbutton regarding using the same values for multiple dropdownbutton.
From my understanding from the error, it was due to using the same list for 2 or more dropdownbuttons in the same activity.
How am i able to resolve this error but still able to reuse the list for 2 or more dropdownbuttons?
String _value1;
String _value2;
final List<String> nameList = <String>[
"Name1",
"Name2",
"Name3",
"Name4",
"Name5",
"Name6",
"Name7",
"Name8"
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 2.0,
title: Text('Hello'),
),
body: ListView(
children: <Widget>[
Row(
children: <Widget>[
Text('Name: '),
Center(
child: DropdownButton(
value: _value1,
onChanged: (value) {
setState(() {
_value1 = value;
});
},
items: nameList.map(
(item) {
return DropdownMenuItem(
value: item,
child: new Text(item),
);
},
).toList(),
),
),
],
),
Row(
children: <Widget>[
Text('Name: '),
Center(
child: DropdownButton(
value: _value2,
onChanged: (value) {
setState(() {
_value2 = value;
});
},
items: nameList.map(
(item) {
return DropdownMenuItem(
value: item,
child: new Text(item),
);
},
).toList(),
),
),
],
),
],
),
backgroundColor: Colors.grey[200],
);
}
}
I had the exact same error, multiple Dropdowns all feeding from the same static list, the only difference is that in my case, it was a list of Objects, not Strings.
So, if it's a static list, there's no way it's empty, no duplicate values in the list, AND you already make sure value is not empty? Then the only option remaining is that item.value is different than value
In my case, as it was an Object list, I had to overwrite operator == and hashcode methods in my Object class.
bool operator ==(dynamic other) =>
other != null && other is TimeSelection && this.hour == other.hour;
#override
int get hashCode => super.hashCode;
And that was it. I didn't had to initialize _value1 or _value2
You are getting that exception because _value1 and _value2 aren't initialized and providing empty to the dropdown widget.
You could do something like this:
DropdownButton(
value: _value1.isNotEmpty ? _value1 : null, // guard it with null if empty
items: nameList.map((item) {
return DropdownMenuItem(
value: item,
child: new Text(item),
);
}).toList(),
);
This exception you have because of mistakes:
No _value1 and _value2 initialization.
When you initialize them make sure that _value1 and _value2 right from nameList e.g.
_value1 = nameList[0];
_value2 = nameList[3];
this is important step with complex data type, but in your case
_value1 = "Name1";
_value2 = "Name4";
will be sufficient.
Full example:
String _value1;
String _value2;
final List<String> nameList = <String>[
"Name1",
"Name2",
"Name3",
"Name4",
"Name5",
"Name6",
"Name7",
"Name8"
];
/// initialization is here:
#override
void initState() {
super.initState();
_value1 = nameList[0];
_value2 = nameList[3];
}
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Row(
children: <Widget>[
Text('Name: '),
Center(
child: DropdownButton(
value: _value1,
onChanged: (value) {
setState(() {
_value1 = value;
});
},
items: nameList.map(
(item) {
return DropdownMenuItem(
value: item,
child: new Text(item),
);
},
).toList(),
),
),
],
),
Row(
children: <Widget>[
Text('Name: '),
Center(
child: DropdownButton(
value: _value2,
onChanged: (value) {
setState(() {
_value2 = value;
});
},
items: nameList.map(
(item) {
return DropdownMenuItem(
value: item,
child: new Text(item),
);
},
).toList(),
),
),
],
),
],
);
}
}
I have the same problem, and I solved it.
The dropdown button needs items list and value. We define items and selected items, but the item chosen instance does not inside the items list.
You should try this and fix your logic.
(value ıs selected item value for user)
var _value = itemList.isEmpty
? value
: itemList.firstWhere((item) => item.value == value.value);
More : https://gist.github.com/VB10/fd560694fec0a38751e798a213408001
You must initialise the _value1 and _value2 and make sure those values are also present in nameList.
My solution was more simple than every one else. The fact that was find a value that wasn't the same as in the list, is because I have put a value in the variable, that wasn't either full or empty, the value was this ("") and it has to be null for the Dropdown value instance. So, I just have put a value null in the declaration of variable. like: "String _value;", and voila, it worked.
#Sorry for the English, Brazilian here.
In my case, I use FormBuilderDropdown of the package flutter_form_builder.
Adding key: UniqueKey() in the Widget FormBuilderDropdown is the solution for my case.
I had same issue with Getx package. When it's updated, it causes this error because add same items to list. Adding key: UniqueKey() to DropdownButtonFormField is the solution for me.
You must initialise the _value1 and _value2 with a initial Value.
_value1 and _value2 variables need to be initialized, or you can do that:
value: _value1 != null ? _value1 : null,
hint: Text('Your hint'),
var _issues = [
"Subscription Related",
"Talk Therapy Related",
"Program Related",
"Account Related",
"Technology Related",
];
String _currentSelectedValue=_issues.first;
As much as the answer accepted may be working, it is unnecessary and over achieving.
All you need to do is ENSURE THAT whatever your initialized the value to is in the list as well. That is:
String _value1 = "Name1";
If the initialized value is not in your list, you will get the error message you are getting. Period!
If you are sure that your code is right then do "hot restart" instead of "hot reload".
this solved in my case
value: _value1,
onChanged: (value) {
setState(() {
_value1 = value;
_value2 = null;
});
},
The dropdownbuttonformfield filtering mechanism uses a single field for filtering. It can not filter by class hash. Set the DropdownMenuItem value to the key of the incoming data. The dropdownmenuItem value must be unique. The key is String type. currentStatus holds the key to position in the dropdown. I map a list of class objects where the class has databaseValue field and a displayValue field to the dropdownmenuitem as value:databaseValue and child:Text(displayValue). Now, I can set _currentStatus to a databaseValue and it will position in the dropdown.
String _currentStatus;
List<DropdownMenuItem> listMenuItems =
<DropdownMenuItem<String>>[];
Provider.of<Api>(context, listen: false)
.getComboViews()
.then((data) {
setState(() {
listMenuItems =
data.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item.databaseValue, child: Text(item.displayValue));
}).toList();
DropdownButtonFormField<String>(
value: this._currentStatus,
items: listMenuItems
onChanged: (String value) {
setState(() {
this._currentStatus = value;
});
);
String? dropdownValue
hint: Text( "Select City"),
value: dropdownValue == null ? null : dropdownValue,
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: <String>[
'Islamabad',
'RawalPindi',
'Mangla',
'Mirpur'
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
Once you have Multiple DropDownButtons which are dependent on one another.
Adding key: (_value1 != null)
? Key(_value1)
: UniqueKey() to the dependent DropdownButtonFormField
children: <Widget>[
Row(
children: <Widget>[
Text('Name: '),
Center(
child: DropdownButton(
value: _value1,
onChanged: (value) {
setState(() {
_value1 = value;
});
},
items: nameList.map(
(item) {
return DropdownMenuItem(
value: item,
child: new Text(item),
);
},
).toList(),
),
),
],
),
Row(
children: <Widget>[
Text('Name: '),
Center(
child: DropdownButton(
value: _value2,
key: (_value1 != null) ? Key(_value1) : UniqueKey()
onChanged: (value) {
setState(() {
_value2 = value;
});
},
items: nameList.map(
(item) {
return DropdownMenuItem(
value: item,
child: new Text(item),
);
},
).toList(),
),
),
],
You can use the same list in multiple DropDownButton. The error you got is because of having more than one same values in the list.
For Example, if I change the list to given below where I have two items having the same value, it will throw me an error.
`final List<String> nameList = <String>[
"Name1",
"Name1",
"Name3",
"Name4",
"Name5",
"Name6",
"Name7",
"Name8"
];`
Error:
_value1 and _value2 must be in your list
I solved this problem by specifying the type (in my case String): FormBuilderDropdown<String> and setting initialValue: null
I have Flutter 3.3.4
This happens when the value field type is not the same than the types used in items. Here is a example throwing the same error.
FittedBox(
fit: BoxFit.contain,
child: DropdownButton<E>(
//isExpanded: true,
value: box?.get(hiveKey), // hive key = string value --> need to convert to E type
onChanged: (final E? newValue) {
updateSettingsOnTap(box);
},
items: values.map<DropdownMenuItem<E>>((E value) {
return DropdownMenuItem<E>( // --> E type not string
value: value.item,
child: Text(value.getItemValue(), overflow: TextOverflow.ellipsis),
);
}).toList(),
You have to use the same type in value and items fields to fix it. Working code below.
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
FittedBox(
fit: BoxFit.contain,
child: DropdownButton<String>(
//isExpanded: true,
value: box?.get(hiveKey),
onChanged: (final String? newValue) {
updateSettingsOnTap(box);
},
items: values.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value, overflow: TextOverflow.ellipsis),
);
}).toList(),
Here is how I have implemented the dropdowns. This code handles exceptions in case list is not loaded or preselected value does not exist in the list. Also, it does provide validation on selected item.
Following is the Widget implementation ( e.g. froonodropdown.dart)
import 'package:flutter/material.dart';
class FroonoDropDown<T> extends FormField<T> {
final T? selectedValue;
final FocusNode? fnNode;
final void Function(T) onChanged;
final List<T> list;
final String Function(T) getLabel;
final String label;
FroonoDropDown(this.label, this.list, this.selectedValue, this.fnNode, this.onChanged, this.getLabel, {Key? key})
: super(
key: key,
validator: (T? item) {
return item == null ? "Please choose an option" : null;
},
initialValue: list.contains(selectedValue) ? selectedValue : null,
builder: (FormFieldState<T> state) {
List<DropdownMenuItem<T>> dropdownItems = [];
dropdownItems.add(DropdownMenuItem(value: null, child: Text("Select " + label)));
//handle exception in case list is not loaded or selectedValue is not part of the list
T? defaultValue = selectedValue;
if (selectedValue != null && !list.contains(selectedValue)) {
if (getLabel(selectedValue).isEmpty) {
defaultValue = null;
} else {
list.add(selectedValue);
}
}
dropdownItems.addAll(list.map((T item) {
return DropdownMenuItem(
value: item,
child: Text(getLabel(item)),
);
}).toList());
return Column(
children: <Widget>[
InputDecorator(
decoration: InputDecoration(labelText: label),
child: DropdownButtonHideUnderline(
child: DropdownButton(
focusNode: fnNode,
value: defaultValue,
isDense: true,
onChanged: (T? selectedItem) {
state.didChange(selectedItem);
if (selectedItem != null) onChanged(selectedItem);
},
items: dropdownItems),
),
),
if (state.hasError)
Align(
alignment: Alignment.centerLeft,
child: Text(state.errorText!, style: TextStyle(color: Theme.of(state.context).errorColor, fontSize: 12)),
)
],
);
},
);
}
Examples:
With String items:
FroonoDropDown<String>("String Items", stringItemsList, defaultValue, null, (String value) {
defaultValue = value;
//do something else with value;
}, (String item) => item)
With any custom class:
FroonoDropDown<CustomClass>("My Custom List", customClassObjsList,
defaultValue, null, (CustomClass value) {
defaultValue = value;
//do something else with value;
}, (CustomClass act) => act.title)
Make sure you override comparison operator and hashcode in your CustomClass, like this:
#override
bool operator ==(dynamic other) {
return other != null && typeid == other.typeid;
}
#override
int get hashCode => super.hashCode;
I hope above implementation would be useful for you!