Flutter: Add value to LIst triggers RangeError (index) - flutter

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);

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;
}
}
});
}),
)

Flutter: struggle with async functions

What I want to do is to load users and put them in a list. I am doing this with the function getUsers().
After that I want to load an Event (with fetchEvent()) which is a table with some dropdown menus.
My problem is that the users are sometimes not there when the event gets loaded. Thats why dropdown button is disabled.
I tried to solve this with using "then".
This is my initState:
void initState() {
besetzungsList.clear();
if (widget.eventId != null) {
getUsers().then((_) => fetchEvent());
} else
getUsers();
print(widget.eventId);
super.initState();
}
This is getUsers()
getUsers() async {
getCurrentUser();
usersStream = authBloc.getUsers();
usersStream.listen((allUsers) {
for (ApplicationUser user in allUsers) {
if (user.userRoleList.contains(team)) users.add(user.username);
}
});
}
fetchEvent() async {
...
dataTableRows.add(DataRow(
cells: [
DataCell(
Text(event.dienstMap["Aufgabe"][i]),
onTap: () {
removeRow(i);
},
),
DataCell(
Text(event.dienstMap["Zeit"][i]),
onTap: () {},
),
DataCell(
Text(event.dienstMap["Team"][i]),
onTap: () {},
),
DataCell((i > event.dienstMap["Besetzung"].length - 1 == true)
? DropDown(
hint: "Wählen",
users: users,
besetzungsListChanged: (String value) {
if (besetzungsList.length > 0) {
besetzungsList.removeAt(i);
}
besetzungsList.insert(i, value);
},
fromDropDown: (bool value) => fromDropDown = value,
)
: DropDown(
hint: event.dienstMap["Besetzung"][i],
users: users,
besetzungsListChanged: (String value) {
besetzungsList.removeAt(i);
besetzungsList.insert(i, value);
},
fromDropDown: (bool value) => fromDropDown = value,
))
],
));
}
});
setState(() {});
}
You can add that fetchEvent in your stream listener, so that it will get called after adding the user object to the array. Since it's a stream if you get a new user object, it will again call that fetchEvent and update your dropdown values.
Future<void> getUsers() async {
getCurrentUser();
usersStream = authBloc.getUsers();
usersStream.listen((allUsers) {
for (ApplicationUser user in allUsers) {
if (user.userRoleList.contains(team)) {
users.add(user.username);
}
}
await fetchEvent();
});
}

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 !!!

Other fields gets updated to blank as I call set State, Is there a way by which TextFormFields won't become Blank as i call set state

I know these fields are getting updated to blank because i am calling the set state method, but if i don't call the set state method in my toggle button the values won't change.
So is there a way by which i can use my toggle button and the TextFormFields won't get updated to blank
This is where the values are
class _AddPostState extends State<AddPost> {
List<bool> _typeSelection = [false, false, false];
String title = "";
String type = "";
This is the field which gets updated as i press the Toggle Button
TextFormField(
decoration: InputDecoration(
hintText: "Enter Title",
labelText: "Title",
),
validator: (value) {
if (value.isEmpty) {
return 'Enter an Title';
} else {
return null;
}
},
onChanged: (value) {
title = value;
},
),
This is the button
ToggleButtons(
onPressed: (int newIndex) {
if (newIndex == 0) {
type = "Dog";
print(type);
}
if (newIndex == 1) {
type = "Cat";
print(type);
}
if (newIndex == 2) {
type = "Bird";
print(type);
}
setState(() {
for (int index = 0;
index < _typeSelection.length;
index++) {
if (index == newIndex) {
_typeSelection[index] = true;
} else {
_typeSelection[index] = false;
}
}
});

All dropdown update when one is updated || 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],