hello guys as a beginner on flutter i'm having an issue with the dropdownbutton widget as shown on the joined GIF
on the backend everything seems to work normally every row n the list has it Answer properly but on the screen the values changes for the whole screen not row by row
that's the source code for the ChangeNotifier() :
class ChoiceHandler extends ChangeNotifier {
final List<String> _dropdownElements = ['Not Done', 'Partially Done', 'Done'];
List<String> get dropdownElement => _dropdownElements;
late String _selectedItemValue;
String get selected => _selectedItemValue;
selectedValues(String s) {
_selectedItemValue = s;
notifyListeners();
}
}
and under listView here is the code for the dropdownButton :
Expanded(
child: ListView.builder(
itemCount: propositions.length,
itemExtent: 50.0,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(propositions[index]),
trailing: Consumer<ChoiceHandler>(
builder: (_, provider, __) {
return DropdownButton<String>(
value: dropdownValue,
onChanged: (newValue) {
provider.selectedValues(newValue as String);
dropdownValue = newValue;
print((propositions[index]) + " " + newValue); // here i can the results when i'm changing values
},
items: provider.dropdownElement
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
},
) //_dropdown(index),
);
thanks in advance
i've found the issue i've misplaced the declaration of a veriable ,th variable
"dropdownValue" was declared above as a global variable , and then i've changed it place to where it's shown on the source code :
child: ListView.builder(
itemCount: propositions.length,
itemExtent: 50.0,
itemBuilder: (BuildContext context, int index) {
String dropdownValue = "Not Done"; // <==== i declared it here
return ListTile(
title: Text(propositions[index]),
trailing: Consumer<ChoiceHandler>(
builder: (_, provider, __) {
return DropdownButton<String>(
value: dropdownValue,
onChanged: (newValue) {
provider.selectedValues(newValue as String);
dropdownValue = newValue;
print((propositions[index]) + " " + newValue);
},
items: provider.dropdownElement
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
},
) //_dropdown(index),
);
Related
Below is the code in which i want to display documents ids from cloud firestore but i got error.
FutureBuilder<QuerySnapshot>(
future: db.collection('/Exam').get(),
builder:
(BuildContext context, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.hasError)
return Text("Error: ${asyncSnapshot.error}");
if (!asyncSnapshot.hasData)
return const CircularProgressIndicator();
return DropdownButton<String>(
isExpanded: true,
items: asyncSnapshot.data!.docs
.map(
(snap) => DropdownMenuItem(
value: snap.id,
child: Text(
snap.id.toString(),
),
),
)
.toList(),
value: _selectedexam,
onChanged: (String? newValue) {
setState(() {
_selectedexam = newValue!;
_selectedsemester = null;
print(_selectedexam);
});
},
);
}),
Try adding the Generic type to the map<T>() method:
asyncSnapshot.data!.docs
.map<DropdownMenuItem<String>>(
(snap) => DropdownMenuItem<String>(
value: snap.id,
child: Text(
snap.id.toString(),
),
),
)
.toList(),
In my onChanged method, I add an entry to the list and change the state of the checkbox. How to move onChanged into a separate method?
BlocConsumer<StudentBloc, StudentState>(
listener: _checkboxListener,
builder: (context, state) {
return Expanded(
child: ListView.builder(
itemCount: _lessonsList.length,
itemBuilder: (BuildContext context, int index) {
final lesson = _lessonsList[index];
bool? checkboxValue = _checkboxValues[index];
return CheckboxListTile(
title: Text(lesson.lessonName ?? ''),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
value: checkboxValue,
onChanged: (bool? value) {
_checkedLesson.add(lesson);
setState(
() {
_checkboxValues[index] = value ?? false;
},
);
},
);
},
),
);
},
),
you can do it like this:
BlocConsumer<StudentBloc, StudentState>(
listener: _checkboxListener,
builder: (context, state) {
return Expanded(
child: ListView.builder(
itemCount: _lessonsList.length,
itemBuilder: (BuildContext context, int index) {
final lesson = _lessonsList[index];
bool? checkboxValue = _checkboxValues[index];
return CheckboxListTile(
title: Text(lesson.lessonName ?? ''),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
value: checkboxValue,
onChanged: myFunction,
);
},
),
);
},
),
void myFunction(bool? value) {
_checkedLesson.add(lesson);
setState(
() {
_checkboxValues[index] = value ?? false;
},
);
}
the fastest way to do this in Android Studio is to have the cursor on where you have (bool? value) now, and then press Ctrl+Alt+M. This is a shortcut to extract a method.
If you want to pass other parameters to this function you could do it like this:
BlocConsumer<StudentBloc, StudentState>(
listener: _checkboxListener,
builder: (context, state) {
return Expanded(
child: ListView.builder(
itemCount: _lessonsList.length,
itemBuilder: (BuildContext context, int index) {
final lesson = _lessonsList[index];
bool? checkboxValue = _checkboxValues[index];
return CheckboxListTile(
title: Text(lesson.lessonName ?? ''),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
value: checkboxValue,
onChanged: (bool? value) => myFunction(value, index, lesson),
);
},
),
);
},
),
void myFunction(bool? value, int index, Lesson lesson) { //I don't know what type lesson is, I just assumed Lesson here
_checkedLesson.add(lesson);
setState(
() {
_checkboxValues[index] = value ?? false;
},
);
}
The onChanged parameter takes a function with a nullable boolean parameter i.e. void Function(bool?)
So you have to create a function such as this one:
void aFunctionToPassIn(bool? value){
// Do something in here
}
and then pass it to onChanged like onChanged: aFunctionToPassIn,
If i select the 1st index RadioListTile, then all 1st index in the expansion Tile is selected this is my problem, Here i want to select particular radio list and add the index to list
class Addon extends StatefulWidget {
#override
_AddonState createState() => _AddonState();
} class _AddonState extends State<Addon> {
List addonItem = ["Cool Drinks", "Extra Sauce"];
int dummy;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: ListView.builder(
dragStartBehavior: DragStartBehavior.down,
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: addonItem.length,
itemBuilder: (context, index) {
return ExpansionTile(
initiallyExpanded: false,
title: Text(
addonItem[index],
),
children: [
for (int i = 0; i < 3; i++)
RadioListTile(
value: i,
title: Text("item $i"),
groupValue: dummy,
activeColor: Theme.of(context).primaryColor,
onChanged: (val) {
print(val);
setState(() {
dummy = val;
});
},
)
],
);
}),
),
);
}
}
The problem is that you are using int dummy; as the selected value for both lists of buttons you can use two dummy values and add a condition to the rendering of the children of the ExpansionTile like this:
class Addon extends StatefulWidget {
#override
_AddonState createState() => _AddonState();
}
class _AddonState extends State<Addon> {
List addonItem = ["Cool Drinks", "Extra Sauce"];
int dummy1;
int dummy2;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: ListView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
itemCount: addonItem.length,
itemBuilder: (context, index) {
return ExpansionTile(
initiallyExpanded: false,
title: Text(
addonItem[index],
),
children: [
if (addonItem[index] == 'Cool Drinks')
for (int i = 0; i < 3; i++)
RadioListTile(
value: i,
title: Text("item $i"),
groupValue: dummy1,
activeColor: Theme.of(context).primaryColor,
onChanged: (val) {
print(val);
setState(
() {
dummy1 = val;
},
);
},
),
if (addonItem[index] == 'Extra Sauce')
for (int i = 0; i < 3; i++)
RadioListTile(
value: i,
title: Text("item $i"),
groupValue: dummy2,
activeColor: Theme.of(context).primaryColor,
onChanged: (val) {
print(val);
setState(
() {
dummy2 = val;
},
);
},
)
],
);
},
),
),
);
}
}
This checks if the value if the index you are accessing in addonItem and if the value is 'Cool Drinks' you use one of the dummy values, if the index is 'Extra Sauce' you use the other value, this makes sure you dont share the same value for both lists.
This only works if you are not adding values dynamically to the addonItem list.
i want to build Dropdown list from Future,here is my function for simple list view which is working,,but how to populate drop down list from it, i am really confuse about this list map etc in flutter coming from php background,
child: FutureBuilder(
future:userApi.getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.data == null){
return Container(
child: Center(
child: Text("Loading...")
)
);
} else {
return Container(
child: DropdownButton(
items: snapshot.data.map((item) {
return DropdownMenuItem(child: Text(item.title));
}).toList(),
onChanged: (value){},
)
);
}
},
),
class UserApi{
Future<List<User>>getUsers() async {
var data = await http.get("https://jsonplaceholder.typicode.com/albums/");
var jsonData = json.decode(data.body);
List<User> users = [];
for(var u in jsonData){
User user = User(u["id"], u["title"]);
users.add(user);
}
return users;
}
class User {
final int id;
final String title;
User(this.id,this.title);
}
Ok I just read the comment left above if your problem is getting your data then this might not help you but if snapshot has data this will work
//initialize this outside your build method
String dropDownValue;
FutureBuilder(
future:userApi.getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return snapshot.hasData
? Container(
child: DropdownButton<String>(
hint: Text(dropDownValue ?? 'Make a selection'),
items: snapshot.data.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item.title,
child: Text(item.title),
);
}).toList(),
onChanged: (value) {
setState(() {
dropDownValue = value;
print(value);
});
},
),
)
: Container(
child: Center(
child: Text('Loading...'),
),
);
},
),
you can call the map function on any list converting the elements of the list.
return DropdownButton(
items: snapshot.data.map((item) {
return DropdownMenuItem(child: Text(item.title));
}).toList(),
onChanged: (value){},
);
if you can see here we're converting the list snapshot.data to another list of DropdownMenuItem type by calling map on snapshot.data the map function takes another function that's being called on every element of snapshot.data , map returns an Iterable of type DropdownMenuItem (the returned type of the function being called on every element) and we convert the Iterable to a list by calling toList() on it
I wish that is explanatory enough, the map function is very useful.
I am trying to make a dropdown select input in flutter! I want to get the data from a future list ref.
I am not sure how to go about it, as this gives me error :
Here is the code:
dropdownButtonHideUnderline(FormFieldState<String> state) {
return FutureBuilder(
future: Global.payCountriesRef.getData(),
builder: (BuildContext context, AsyncSnapshot snap) {
if (snap.hasData) {
List<PayCountries> countries = snap.data;
return new DropdownButtonHideUnderline(
child: new DropdownButton<String>(
value: _sendForm.country,
isDense: true,
onChanged: (String newValue) {
setState(() {
_sendForm.country = newValue;
//_updateDropdownValue(newValue, model);
state.didChange(newValue);
});
},
items: List<String>.from(countries)
.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList()
),
);
}
}
);
}
The code Below shows an example to populate a dropdown from a future list
new DropdownButtonHideUnderline(
child: new FutureBuilder<List<BranchItems>>(
future: new BranchService().fetchBranchItems(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return new Container();
} else if (snapshot.hasData) {
list.clear();
//listItemNames.clear();
dropDownItemsMap = new Map();
snapshot.data.forEach((branchItem) {
//listItemNames.add(branchItem.itemName);
int index = snapshot.data.indexOf(branchItem);
dropDownItemsMap[index] = branchItem;
list.add(new DropdownMenuItem(
child: new DropDownItem(
image: Image.network(branchItem.itemPicture),
text: branchItem.itemName),
value: index));
});
return DropdownButton(
items: list,
onChanged: (int selected) {
_selectedItem = list[selected].value;
setState(() {
selectedItemName =
dropDownItemsMap[_selectedItem].itemName;
});
},
hint: new Text(
selectedItemName,
style: new TextStyle(color: Colors.blue),
),
);
} else {
return CircularProgressIndicator();
}
},
),
),