All dropdown update when one is updated || flutter || - flutter

I have a code wherein i am using listview.builder to create different dropdown menu items, the problem i am facing is, all the items in different dropdown menu updates when any one is updated.
i have attached my code down below. Your help to fix this would be highly appreciated.
I have also attached a screenshot of what exactly is happening for easier understanding. enter image description here
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection(
'${widget.destination}rates'.toLowerCase())
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text('Loading..');
} else {
List<DropdownMenuItem> tourName = [];
var selectedTour =
List(int.parse(widget.duration) + 1);
for (int i = 0;
i < snapshot.data.docs.length;
i++) {
DocumentSnapshot snap = snapshot.data.docs[i];
tourName.add(
DropdownMenuItem(
child: Text(snap['name']),
value: '${snap['name']}',
),
);
}
return DropdownButton(
items: tourName,
onChanged: (value) {
setState(() {
selectedTour[i] = value;
print(selectedTour);
});
},
value: selectedTour[i],
isExpanded: true,
hint: Text('Select Tour'),
);
}
},
),

You share the same variable selectedTour to all your tiles.
So when you update the selection :
setState(() {
selectedTour = value;
});
It will update everywhere selectedTour is used to display the value selected.
You can either use a new local variable :
} else {
List<DropdownMenuItem> tourName = [];
var selectedTourLocal = null;
for (int i = 0; i < snapshot.data.docs.length; i++)
...
setState(() {
selectedTourLocal = value;
});
...
value: selectedTourLocal,
Or use an array to store the values to the corresponding day.
// declare your array
selectedTour = new List(widget.duration);
...
setState(() {
selectedTour[i] = value;
});
...
value: selectedTour[i],

Related

How to use DropDownButton for dynamic list in flutter?

I am trying to implement dynamic dropdownButton in my app where the items of dropdown is going to come from the names of columns in my excel sheet. I am able to show all the columns of excel but I couldn't able to trace the index of column which the user is selecting from the dropdown.
I tried to make a map of dropdownitems like this in which the key is the index and value is the DropdownMenuItem like this :
late int selectedIndex; //where I want to store the selected index
late String initialDropDownVal;
List<Map<int,DropdownMenuItem<String>>> dropdownItems = [];
Then I added some values by iterating the columns of excel using a for loop :
excel = Excel.decodeBytes(bytes);
sheet = excel['Sheet1'];
for(int i = 1; i< sheet.maxCols; ++i){
var cell = sheet.cell(CellIndex.indexByColumnRow(rowIndex: 0, columnIndex: i));
String val = cell.value.toString();
if(val=="null"){
break;
}else{
if(i==1){
initialDropDownVal = val;
}
var newItem = DropdownMenuItem(
child: Text(val),
value: val,
);
dropdownItems.add({i:newItem});
}
}
But I could not able to map the values in items attribute of DropdownButton, I tried to implement like this but this is throwing error
DropdownButton(
value: selectedVal,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems.map((int i,DropdownMenuItem<String> p) => p).toList(),
onChanged: (String? value){
setState(() {
initialDropDownVal = value!;
});
})
And I am not sure how to change set the selectedIndex in onChanged function. Please help me in this. Thanks
late int selectedIndex; //where I want to store the selected index
late String initialDropDownVal;
List<DropdownMenuItem<String>> dropdownItems = [];
excel = Excel.decodeBytes(bytes);
sheet = excel['Sheet1'];
for(int i = 1; i< sheet.maxCols; ++i){
var cell = sheet.cell(CellIndex.indexByColumnRow(rowIndex: 0, columnIndex: i));
String val = cell.value.toString();
if(val=="null"){
break;
}else{
if(i==1){
initialDropDownVal = val;
}
var newItem = DropdownMenuItem(
child: Text(val),
value: val,
);
dropdownItems.add(newItem);
}
}
DropdownButton(
value: selectedVal,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems,
onChanged: (String? value){
setState(() {
initialDropDownVal = value!;
selectedIndex=dropdownItems.indexWhere((i)=>i.value==value);
});
})
late int selectedIndex; //where I want to store the selected index
late String initialDropDownVal;
List<Map<String,int>> dropdownItems = [];
excel = Excel.decodeBytes(bytes);
sheet = excel['Sheet1'];
for(int i = 1; i< sheet.maxCols; ++i){
var cell = sheet.cell(CellIndex.indexByColumnRow(rowIndex: 0, columnIndex: i));
String val = cell.value.toString();
if(val=="null"){
break;
}else{
if(i==1){
initialDropDownVal = val;
}
dropdownItems.add({val:i});
}
}
DropdownButton(
value: selectedVal,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems.map((e) {
return DropdownMenuItem(
child: Text(e.keys.first),
value: e.keys.first,
);
}).toList(),
onChanged: (String? value){
setState(() {
initialDropDownVal = value!;
for(int i = 0; i< dropdownItems.length; ++i){
if(dropdownItems[i].keys.first==value){
setState(() {
selectedIndex = dropdownItems[i].values.first;
});
break;
}
}
});
}),
)

How to reset second drop down list if i change first drop down value?

I have two drop down which second is depends on first.
When i changing the first drop down value after selecting second dropdown value it throws an error as below.
So how to reset second drop down list if i changing first drop down value?
Drop down buttons are as below.
DropdownButtonFormField<Standard>(
validator: (value) {
if (value == null) {
return "Select Standard";
}
},
isExpanded: true,
hint: Text('Select Standard'),
value: selectedStandard,
items: _standards.map((Standard standard) {
return DropdownMenuItem<Standard>(
value: standard,
child: Text(standard.standardName),
);
}).toList(),
onChanged: (val) {
setState(() {
selectedStandard = val;
standardId = val?.standardId;
onMediumChange(val);
});
}),
SizedBox(height: 20),
DropdownButtonFormField<Medium>(
validator: (value) {
if (value == null) {
return "Select Medium";
}
},
isExpanded: true,
hint: Text('Select Medium'),
value: selectedMedium,
items: _mediums.map((Medium medium) {
return DropdownMenuItem<Medium>(
value: medium,
child: Text(medium.mediumName),
);
}).toList(),
onChanged: (val) {
setState(() {
selectedMedium = val;
mediumId = val?.mediumId;
});
}),
And get Values code is as below. Varibles which i used.
var _standards = <Standard>[];
var _mediums = <Medium>[];
Standard? selectedStandard;
Medium? selectedMedium;
#override
void initState() {
super.initState();
ApiManager().getStandards().then((standards) {
setState(() {
_standards = standards;
});
});
}
void onMediumChange(standard) {
setState(() {
selectedStandard = standard;
_mediums = [];
});
String mediumUrl =
"$baseUrl/medium/get_by_course_id?standard_id=${selectedStandard?.standardId}";
ApiManager().getMediums(mediumUrl).then((List<Medium> value) => {
setState(() {
_mediums = value;
})
});
}
As I understand there is a problem with selectedMedium
Try change Make it selectedMedium null inside onMediumChange
void onMediumChange(standard) {
setState(() {
selectedMedium = null;
selectedStandard = standard;
_mediums = [];
});
String mediumUrl =
"$baseUrl/medium/get_by_course_id?standard_id=${selectedStandard?.standardId}";
ApiManager().getMediums(mediumUrl).then((List<Medium> value) => {
setState(() {
_mediums = value;
})
});
}
first, check the values in _mediums because you must have a different value of selectedMedium here as according to this error !!!

Creating dynamic dropdown - Flutter Issue Items reads zero

Hello I tried to create a dynamic dropdown with flutter. the request to end point returns the data successfully but the DropdownButtonFormField items array is reading zero. it is not getting the data at all. Please can any one help me?
Dropdown menu items variable:
List<DropdownMenuItem> _anchorLists = List<DropdownMenuItem>();
dynamic _selectedAnchor;
void initState() {
super.initState();
_getAnchorsByProvider();
}
The Function:
_getAnchorsByProvider() async {
try {
_prefs = await SharedPreferences.getInstance();
var _anchorService = AnchorService();
var result = await _anchorService
.getAnchorsByProviderId(_prefs.getInt('providerOid'));
var anchors = json.decode(result.body);
anchors.forEach((anchor) {
setState(() {
_anchorLists.add(DropdownMenuItem(
child: Text(anchor['acronym']),
value: anchor['oid'],
));
});
});
setState(() {
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
return e.toString();
}
}
The Dropdown
SizedBox(height: 20),
(_anchorLists.length > 0)?
DropdownButtonFormField(
value: _selectedAnchor,
items: _anchorLists,
hint: const Text('Select your Anchor'),
onChanged: (value) {
setState(() {
_selectedAnchor = value;
});
},
)
: Text('Loading'),
Result
Values of the json:
{
"oid": 1,
"acronym": "MAAN",
"contactAddress": "Abuja",
"idCardInstruction": null,
}
Because you are retrieving a json object (Map) and not a json list (List), forEach must iterate through key and value pairs, like so:
anchors.forEach((key, value) {
_anchorLists.add(DropdownMenuItem(
child: Text(value.toString()),
value: value.toString(),
));
});
I'm not sure of the specifics of your needs so tweak accordingly, but if you only wish to provide specific values, you can specify like so:
_anchorLists.addAll([anchors['acronym'], anchors['oid']]
.map((value) => DropdownMenuItem(
child: Text(value.toString()),
value: value.toString(),
)));

How can I set values to Firestore from a list

I am trying to make a grading app and I wanted to list students from Firestore and fill their grades and I have got the list for the students list but I cant get the number to work I have a working attendance code that only uses boolean values
here is the working code for booleans
Switch(
value: switchStates[
students[index].data()["id"]],
onChanged: (value) {
setState(() {
stateofvalue = value;
});
onSwitchChanged(
students[index].data()["id"], value);
},
activeColor: Color(0xff2fbd9f),
),
this is how I call the values
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> markAttendance() {
Map<String, dynamic> resultMap = {};
var formatter = new DateFormat('dd-MM-yyyy');
String todaysDate = formatter.format(now);
switchStates.forEach((key, value) {
keys.add(key);
values.add(value);
});
for (var i = 0; i < keys.length; i++) {
resultMap["${keys[i]}"] = values[i];
}
FirebaseFirestore.instance
.collection("data")
.doc("attendance")
.collection("school")
.doc(todaysDate)
.set(resultMap);
}
and this is how I set th value but now I want to set grades in the same manner how can I manage to do that by replacing Switch() with TextFeild()
Try using TextField with onSubmitted or onChanged callbacks.
TextField(
controller: _controller,
onSubmitted: (text) {
sendMessage(text);
_controller.clear();
},
onChanged: () {},
textInputAction: TextInputAction.send,
)
If you have the user ID, you could try something like this
Firestore.instance
.collection("users")
.document(userId)
.get().then((value) => value.update({
field: value
})
I mean, get the collection and then update it individually

Flutter: Add value to LIst triggers RangeError (index)

Via this function, I add that much DataRows the user chooses in the UI. What I want to do is that the choosen value of the first row is the first value of the "besetzungsList" and the choosen value of the second row is the second value of the List and so on. And if a new value in the first row is choosen I want to replace the value.
addAufgabe() {
print(besetzungsList);
if (aufgabe.text.isNotEmpty || zeit.text.isNotEmpty) {
setState(() {});
if (event != null) {
...
} else {
for (var i = 0; i < currentValue; i++) {
aufgabenList.add(aufgabe.text);
zeitList.add(zeit.text);
dataTableRows.add(
DataRow(
cells: [
DataCell(Text(aufgabe.text), onTap: () {
removeRow(i);
}),
DataCell(Text(zeit.text), onTap: () {
removeRow(i);
}),
DataCell(DropDown(
hint: "Wählen",
users: users,
besetzungsListChanged: (String value) {
for (var j = 0; j < dataTableRows.length; j++) {
setState(() {
besetzungsList[j] = value;
});
}
// }
},
fromDropDown: (bool value) => fromDropDown = value,
))
],
),
);
}
}
}
It seems you remove an Element at i and add one at same index which is the cause.
You should do it the other way around...
besetzungsList.insert(i, value);
besetzungsList.removeAt(i + 1);