I am working on Radio Button in a ListView.builder() but when I select any of the radio button it is selecting each and every radio button rather than selected one.
My code is given below:
ListView.builder(
itemCount: 67,
itemBuilder: (BuildContext context, int index) {
return _buildCheckListItems(index);
}),
Widget _buildCheckListItems(int index) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Seat Belts',
style: TextStyle(fontSize: 17),
),
Container(
width: 200,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Radio(
value: 1,
groupValue: selectedRadio,
activeColor: Colors.green,
onChanged: (val) {
changeValue(val);
},
),
Radio(
value: 2,
groupValue: selectedRadio,
activeColor: Colors.green,
onChanged: (val) {
changeValue(val);
},
)
],
),
),
],
);
}
changeValue(int val) {
setState(() {
selectedRadio = val;
});
}
The output result of above code is as follow:
Output result
The key point here is that you are using a same groupValue: selectedRadio, Each radio button group must have different groupValue otherwise it will shared the same change.
If you do not want to specify name for each radio button group you can just create a new .dart file:
import 'package:flutter/material.dart';
class buildCheckListItems extends StatefulWidget {
final int index;
buildCheckListItems(this.index); //I don't know what is this index for but I will put it in anyway
#override
_buildCheckListItemsState createState() => _buildCheckListItemsState();
}
class _buildCheckListItemsState extends State<buildCheckListItems> {
int selectedRadio = -1;
changeValue(int val) {
setState(() {
selectedRadio = val;
});
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
...
],
);
}
}
and now everything should be working just fine.
Related
Im a newbie, Not able to select items in Radio button, inside a ListTile. I tied to use same code without ListTile and working as expected. Looks like combination is not correct or i might be missing something.
class _TempState extends State<Temp> {
int selectedValue = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
child: Column(children: [
Row(
children: [
Expanded(
child: Text("Radio button with ListView",))],),
Expanded(
child: ListView.builder(
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return OrderItem();
}),),
]))));}
Widget OrderItem() {
int selectedValue = 0;
return ListTile(
title: Container(
child: Column(children: [
Row(
children: [
Expanded(
child: Text(
"Product Type :",
)),
Radio<int>(
value: 1,
groupValue: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value != null ? value.toInt() : 1;
});
},
),
Text('NRML'),
Radio<int>(
value: 2,
groupValue: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value != null ? value.toInt() : 1;
});
}),
Text('MARKET'),
],), ])));
}}
You are updating your selectedValue in wrong way ,first define your selectedValue like this:
int? selectedValue;
then update your widget like this:
Widget OrderItem() {//remove int selectedValue = 0; here
return ListTile(
title: Container(
child: Column(
children: [
Row(
children: [
Expanded(
child: Text(
"Product Type :",
)),
Radio<int>(
value: 1,
groupValue: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value; //<-- change this
});
},
),
Text('NRML'),
Radio<int>(
value: 2,
groupValue: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value; //<-- change this
});
}),
Text('MARKET'),
],
),
],
),
),
);
}
Also another reason that is not working is that you are define another int selectedValue = 0; in your OrderItem method, you need to remove it.
In your second radio you should change your setState to
selectedValue = value != null ? value.toInt() : 2;
Value you assign to radio will be used to determine which radio is selected. So if you want to select second radio you should assign its value when selecting
selectedValue = value != null ? value.toInt() : 2;
Value you assign to radio will be used to determine which radio is selected. So if you want to select second radio you should assign its value when selecting
I have a goal to change the state of the checkbox when clicking on the adjacent widget. I tried to create a stateful function but unfortunately my code doesn't work.
Here is the function -
void _changeFlagCheckBox(bool? value){
setState(() {
MyCheckBoxCountry.isChecked = value!;
});
}
Inkwell - when you click on it, it should change the state of myCheckBoxCountry.
child: ListView.builder(
itemCount: city.length,
shrinkWrap: true
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
print(city[index]);
_changeFlagCheckBox;
},
child: Container(
margin: const EdgeInsets.only(top: 15),
child:Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:[
Row(
children:[
SizeBox(width: 10,),
Text(city[index], style: TextStyle(color: ConfigColor.white, fontWeight: FontWeight.w500, fontSize: 15),)
],
),
MyCheckBoxCountry()
],
)
),
);
},
),
myCheckBoxCountry -
class MyCheckBoxCountry extends StatefulWidget {
static bool isChecked = false;
#override
_MyCheckBoxState createState() => _MyCheckBoxState();
}
class _MyCheckBoxState extends State<MyCheckBoxCountry> {
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
unselectedWidgetColor: ConfigColor.grey,
),
child: Checkbox(
activeColor: ConfigColor.green,
checkColor: ConfigColor.background,
value: MyCheckBoxCountry.isChecked,
shape: CircleBorder(),
onChanged: (bool? value) {
setState(() {
MyCheckBoxCountry.isChecked = value!;
});
},
),
);
}
}
First of all you're not sending any value in your _changeFlagCheckBox function in your Inkwell so how is it supposed to change :
InkWell(
onTap: () {
print(city[index]);
_changeFlagCheckBox; //should be _changeFlagCheckBox(!MyCheckBoxCountry.isChecked)
},
but to understand why didn't you get an error like that, well that's because you made the bool value nullable in your _changeFlagCheckBox function:
void _changeFlagCheckBox(bool? value){ //should be (bool value)
setState(() {
MyCheckBoxCountry.isChecked = value!;
});
}
Though even then you are not using your isChecked value in your MyCheckBoxCountry class anywhere :
Checkbox(
activeColor: ConfigColor.green,
checkColor: ConfigColor.background,
value: MyCheckBoxCountry.isChecked,
shape: CircleBorder(),
onChanged: (bool? value) {
setState(() {
MyCheckBoxCountry.isChecked = value!; //should be the other way around
});
},
),
Should have been :
setState(() {
value = MyCheckBoxCountry.isChecked;
});
I guess since it is an another stateful widget class, It does not recognize the setState. Calling child widget from parent widget would solve the problem.
This code fetches the data and applies it to the radiobutton, but it cannot be modified
#override
Widget build(BuildContext context) {
var filteredItem = ProList.firstWhere((element)=> element.idPush == idC, orElse: () =>null);
String license = filteredItem.license;
int _license= int.parse(license);
RadioListTile<dynamic> buildRadioListLicense(val, txt) {
return RadioListTile(
controlAffinity: ListTileControlAffinity.leading,
activeColor: myRed,
value: val,
groupValue: _license,
onChanged: (value) {
setState(() {
_license = value;
});
},
title: Text(txt),
);
}
return Scaffold(
body:Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: buildRadioListLicense(
1, getTranslated(context, 'received')),
),
Expanded(
child: buildRadioListLicense(
2, getTranslated(context, 'inProgress')),
),
Expanded(
child: buildRadioListLicense(
3, getTranslated(context, 'done')),
)
],
),
);
}
There is no "checked" attriubute inside of your builder.
docs: https://api.flutter.dev/flutter/material/RadioListTile-class.html
Update
Ok I know what is wrong. Your _license variable should not be inside of Widget builder. Whenever builder recreates itself _license always come back to its initial value and since groupValue decides which element is active it doesnt update.
When clicking the add button, the same widget is replicated. The widget contains the list of checkboxes that are multi selectable. I am able to replicate the widget but I got problem to handle the checkboxes according to the index of the widget. In image below the checkbox checked state is replicated along with the new add widget.
I have implemented as follows:
Build the widget according to the addbutton click
ListView.builder(
itemCount: counting,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (_, index) {
return _buildLayout(context, index);
});
//counting is number of **blueplus** icon is clicked
Widget _buildLayout(BuildContext context, int i) {
return Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
addContainer,
style: TextStyle(color: Colors.blueGrey),
),
Container(
width: 64.0,
alignment: Alignment.center,
child: IconButton(
onPressed: () => {i == 0 ? addRow(i) : deleteRow(i)},
icon: Icon(
i == 0
? Icons.add_circle_outline
: Icons.remove_circle_outline,
color: i == 0 ? Theme.of(context).primaryColor : Colors.red,
)),
),
],
),
_buildCheckBoxes()
],
);
}
Widget _buildCheckBoxes() {
return
Container(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {
showHide();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
productionmareketway,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
showHidee
? Icon(Icons.keyboard_arrow_up)
: Icon(Icons.keyboard_arrow_down)
])),
SizedBox(
width: 20,
),
showHidee
? ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: widget.responseMarket.length,
itemBuilder: (ctx, i) {
return _buildSingleCheckBox(
context,
widget.responseMarket[i].name,
widget.responseMarket[i].isChecked,
widget.responseMarket[i].id,
widget.responseMarket[i].identifier,
i);
})
: Container()
])
);
}
Widget _buildSingleCheckBox(BuildContext context, String name, bool isChecked,
int i, String identifier, int j) {
return Container(
child: new CheckboxListTile(
title: new Text(name),
value: isChecked,
activeColor: Theme.of(context).primaryColor,
checkColor: Colors.white,
onChanged: (bool value) {
setState(() {
widget.responseMarket[i].isChecked = value;
print(value);
print(i);
widget._onChecked(
value,
widget.responseMarket[i].id,
widget.responseMarket[i].name,
widget.responseMarket[i].identifier,
counting);
});
},
),
);
}
Add and delete widget function
addRow(int i) {
setState(() {
counting = counting + 1;
});
}
deleteRow(int i) {
setState(() {
counting = counting - 1;
});
}
My callback function
onMarketChecked(var value, int i, String name, String identifier, int j) {
setState(() {
if (responseMarket[i].isChecked == true) {
nonMarketRepated.add(name);
} else {
nonMarketRepated.remove(responseMarket[i].name);
}
});
}
This issue is because you control all replica by counting and widget.responseMarket. If you want all replicas work individually, you need to Replica it actually.
I suggest to create a new StatefulWidget to replace _buildSingleCheckBox() & _buildCheckBoxes() function. I also put showHidee inside it.
class CheckBoxesWidget extends StatefulWidget {
final responseMarket;
CheckBoxesWidget({this.responseMarket, Key key}) : super(key: key);
#override
_CheckBoxesWidgetState createState() => _CheckBoxesWidgetState();
}
class _CheckBoxesWidgetState extends State<CheckBoxesWidget> {
bool showHidee;
#override
void initState() {
showHidee = true;
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {
showHide();
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'productionmareketway',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
showHidee
? Icon(Icons.keyboard_arrow_up)
: Icon(Icons.keyboard_arrow_down)
],
),
),
SizedBox(
width: 20,
),
if (showHidee)
Column(
children: widget.responseMarket
.map(
(e) => CheckboxListTile(
title: Text(e.name),
value: e.isChecked,
activeColor: Theme.of(context).primaryColor,
checkColor: Colors.white,
onChanged: (bool value) {
setState(() {
e.isChecked = value;
});
},
),
)
.toList(),
),
],
),
);
}
void showHide() {
setState(() {
showHidee = !showHidee;
});
}
}
Second, beyond control the replica by counting, you should use a List to store all replica of responseMarket in the original class.
List<List<Market>> responseMarkets;
#override
void initState() {
responseMarkets = [widget.responseMarket];
super.initState();
}
...
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: responseMarkets.length,
itemBuilder: (_, index) {
return _buildLayout(context, index);
});
}
...
Widget _buildLayout(BuildContext context, int i) {
...
// replace _buildCheckBoxes() with this line
CheckBoxesWidget(responseMarket: responseMarkets[i],),
...
}
Finally, you have to modify the addRow, deleteRow function. Each time create a new ResponseMarkets Object.
addRow(int i) {
setState(() {
responseMarkets.add(responseMarkets[0]
.map((e) => ResponseMarkets(
id: e.id,
name: e.name,
identifier: e.identifier,
isChecked: e.isChecked,
))
.toList());
});
}
deleteRow(int i) {
setState(() {
responseMarkets.removeAt(i);
});
}
Here I have a trimmed down page which creates a ReorderableListView, which has its body set to two RecipeStepWidgets with UniqueKeys set (I've also tried this with ValueKey and ObjectKey)
import 'package:flutter/material.dart';
import 'consts.dart';
import 'recipeStep.dart';
import 'recipeStepWidget.dart';
class NewRecipePage extends StatefulWidget {
#override
_NewRecipePageState createState() => _NewRecipePageState();
}
class _NewRecipePageState extends State<NewRecipePage> {
final TextEditingController _nameController = TextEditingController();
#override
Widget build(BuildContext context) {
List<RecipeStep> recipeSteps = [];
List<RecipeStepWidget> stepWidgets = [
RecipeStepWidget(key: UniqueKey()),
RecipeStepWidget(key: UniqueKey())
];
void _onReorder(int oldIndex, int newIndex) {
setState(
() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final RecipeStepWidget item = stepWidgets.removeAt(oldIndex);
stepWidgets.insert(newIndex, item);
},
);
}
return Scaffold(
appBar: AppBar(title: Text("New Recipe")),
body: Column(
children: <Widget>[
Expanded(
child: ReorderableListView(
header: Text("Steps"),
onReorder: _onReorder,
children: stepWidgets,
),
),
],
),
);
}
}
The RecipeStepWidget class is (ignoring includes):
class RecipeStepWidget extends StatefulWidget {
RecipeStepWidget({#required Key key}) : super(key: key);
_RecipeStepWidgetState createState() => _RecipeStepWidgetState();
}
class _RecipeStepWidgetState extends State<RecipeStepWidget> {
TextEditingController _controller = TextEditingController();
TextEditingController _durationLowController = TextEditingController();
TextEditingController _durationHighController = TextEditingController();
bool concurrent = false;
RecipeStep toRecipeStep() {
return RecipeStep(
text: _controller.text,
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
controller: _controller,
decoration: InputDecoration(hintText: "Step text"),
),
Row(
children: <Widget>[
Text("Duration min: "),
Container(
width: 40.0,
//height: 100.0,
child: TextField(
controller: _durationLowController,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "0"),
onChanged: (String val) {
if (_durationHighController.text.isEmpty ||
int.parse(val) >
int.parse(_durationHighController.text)) {
_durationHighController.text = val;
}
},
),
),
Text(" max: "),
Container(
width: 40.0,
//height: 100.0,
child: TextField(
controller: _durationHighController,
keyboardType: TextInputType.number,
decoration: InputDecoration(hintText: "0"),
),
),
],
),
Row(
children: <Widget>[
Text("Start concurrently with previous step"),
Checkbox(
value: concurrent,
onChanged: (bool val) => {
setState(() {
concurrent = val;
})
}),
],
),
],
);
}
}
When I edit the textfields or checkboxes in the RecipeStateWidgets and then reorder them within the list by clicking+dragging them, the widgets get reset to their default state.
Does anyone have any ideas why this is happening? I thought that all I had to do in order to get the ReorderableListView to work as intended was to set a key on each of its children. Is that not correct?
Thanks!
I think you can use AutomaticKeepAliveClientMixin like so:
class _RecipeStepWidgetState extends State<RecipeStepWidget> with AutomaticKeepAliveClientMixin {
#override
bool get wantKeepAlive => true;
Giving the list item a ValueKey seems to fix the problem for me. Hopefully helps in your situation as well.
e.g.
List<YourModel> _items = [];
Widget _itemsListWidget() {
return ReorderableListView(
onReorder: (oldIndex, newIndex) {
//
},
children: [
for (int index = 0; index < _items.length; index += 1)
Text(
_items[index],
key: ValueKey(_items[index].id), // <--- This is what you need to add
),
],
);
}