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,
Related
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),
);
I am trying to make an age selection screen. Below is a screenshot.
As you can see there is no color filled in the radio list tile. I don't know what I am doing wrong. Please do let me know.
final ageRange = ["18 - 21", "21 - 26", "26+"];
var selectedIndex = 0;
ListView.builder(
shrinkWrap: true,
itemCount: ageRange.length,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: registerController.userAgeRange,
selected: index == selectedIndex,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
},
),
Others answer like #masum billah sanjid will work, But I think it will be better to provide data type on RadioListTile.
While the value will be string, I prefer
final ageRange = ["18 - 21", "21 - 26", "26+"];
String? selectedValue;
changeSelectedIndex(v) {
setState(() {
selectedValue = v;
});
}
///....
return RadioListTile<String>(
value: ageRange[index],
groupValue: selectedValue,
selected: selectedValue == ageRange[index],
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
Change groupValue property with selectedIndex
Here is my example
final ageRange = ["18 - 21", "21 - 26", "26+"];
var selectedIndex = 0;
changeSelectedIndex(v) {
selectedIndex = v;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: ageRange.length,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: selectedIndex,
selected: index == selectedIndex,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
},
),
);
}
you can try this:
ListView.builder(
shrinkWrap: true,
itemCount: ageRange.length,
itemBuilder: (context, index) {
return RadioListTile(
value: index,
groupValue: selectedIndex,
selected: selectedIndex == index,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
);
},
),
Wrap your RadioListTile inside a Container:
return Container(
color: Colors.blue,
child: RadioListTile(
value: index,
groupValue: registerController.userAgeRange,
selected: index == selectedIndex,
onChanged: changeSelectedIndex,
title: Text(ageRange[index]),
),
);
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 implement a search bar filter for my ListView items in Alert Dialog. However i think set State does not work for Alert Dialog as I am not getting the desired result. Is there a way to implement this?
My code
class _CreateTaskScreenState extends State<CreateTaskScreen> {
List<String> societiesNames;
var items = List<String>();
#override
void initState() {
int length = widget.societies.length;
List<String> list = new List<String>();
for(var i = 0; i<length; i++){
list.add(widget.societies[i].name);
}
setState(() {
societiesNames = list;
});
items.addAll(societiesNames);
super.initState();
}
String selected;
//Searching the List
void filterSearchResults(String query) {
List<String> dummySearchList = List<String>();
dummySearchList.addAll(societiesNames);
if(query.isNotEmpty) {
List<String> dummyListData = List<String>();
dummySearchList.forEach((item) {
if(item.contains(query)) {
dummyListData.add(item);
}
});
setState(() {
items.clear();
items.addAll(dummyListData);
});
return;
} else {
setState(() {
items.clear();
items.addAll(societiesNames);
});
}
}
#override Widget build(BuildContext context){
//searchbar
Container searchBar = Container(
child: TextField(
onChanged: (value) {
filterSearchResults(value);
},
controller: TextEditingController(),
decoration: InputDecoration(
labelText: "Search",
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(15.0)))),
),
);
//alert dialog on calling show dialog
AlertDialog dialog = AlertDialog(
title: searchBar,
content: Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: items == null? 0: items.length,
itemBuilder: (BuildContext context, int index) {
return Ink(
child: InkWell(
onTap: (){
setState(() {
selected = items[index];
Navigator.pop(context);
});
},
child: ListTile(
title: Text(items[index]),
),
),
);
},
),
),
);
}
}
The list shows properly the first time i open it , however no change takes place on typing in the search bar. And on dismissing and reopening the dialog it shows empty list.
The setState method that you are calling is the one of the '_CreateTaskScreenState' , so you should use a StatefulBuilder and wrap the content of the AlertDialog with it, like that you will be provided with the a setState method that will work for the widget that the builder return !
Like that:
AlertDialog dialog = AlertDialog(
title: searchBar,
content: StatefulBuilder(
builder: (context, setState) => Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: items == null? 0: items.length,
itemBuilder: (BuildContext context, int index) {
return Ink(
child: InkWell(
onTap: (){
setState(() {
selected = items[index];
Navigator.pop(context);
});
},
child: ListTile(
title: Text(items[index]),
),
),
);
},
),
),
)
);
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();
}
},
),
),