Flutter checkbox not deselecting after select - flutter

I have an API with an array list of values that passes values to my Radio button successfully. When I select each the select value parses but I'm unable to deselect the select radio button. Below is my flutter code:
ListView.builder( shrinkWrap:true,
physics:const NeverScrollableScrollPhysics(),
itemCount:dataOptions == null? 0: dataOptions.length,
itemBuilder:(BuildContext context,int index) {
return Container(
padding:
const EdgeInsets
.only(
bottom:
0.0),
child: Card(
elevation: 0,
child:
ListTile(
title: Text(
dataOptions[index]['option_title']),
leading:
Radio(
value: dataOptions[index]["option_price"],
//below will be the default value.
groupValue: dataOptions[index]["option_id"],
onChanged: (value) {
setState(
() {
dataOptions[index]["option_id"] = value;
debugPrint("radioSel:$value");
});
},
activeColor: Colors.green,
),
trailing:
Text(dataOptions[index]['option_price'].toString(), style:TextStyle(fontFamily: 'Montserrat',
color: colorPink, fontWeight: FontWeight.bold,
fontSize: 15,
),
),
)));
})

Since groupValue stores the currently selected value and you use the individual dataOptions[index]["option_id"] as groupValue all radio buttons are independent from each other.
If this is intended, check out checkboxes as they are meant to be used for this behavior. CheckBox
To deselect a Radio widget if another one in the group is selected do the following.
//Set default option if prefered
String? _groupValue;
Radio(
value: dataOptions[index]["option_price"],
groupValue: _groupValue,
onChanged: (value) {
setState(() {
_groupValue = value;
});
},
),

I added this and it worked.
toggleable: true,
reference: https://api.flutter.dev/flutter/material/Radio/toggleable.html

Related

How do I use radio buttons in a ListView Flutter?

I have a list of varieties [Chocolate, Strawberry, Vanilla]
This list can change depending on the menu item ordered (i.e. [BBQ, Medium, Peri-Peri, Lemon & Herb])
My current code which display the button correctly but is not functional is:
ListView.builder(
shrinkWrap: true,
itemCount: varietyList.length,
itemBuilder: ((context, index) {
return GFRadioListTile(
titleText: varietyList[index],
size: 25,
activeBorderColor: Colors.black,
focusColor: Colors.black,
type: GFRadioType.basic,
value: 0,
groupValue: 1,
onChanged: (value) {},
inactiveIcon: null,
);
})),
Where ```int number = 20'''. This variable and amount was based off another post where the code looked as follow:
ListView.builder(
shrinkWrap: true,
itemCount: varietyList.length,
itemBuilder: ((context, index) {
return RadioListTile(
value: index,
groupValue: number,
onChanged: (ind) => setState(() => number = ind),
);
})),
But it says "A value of type 'Object?' can't be assigned to a variable of type 'int'."
How can I get the radio button functional?
to fix the "object cant be int" problem I had to add as int for the ind object.
GFRadioListTile(
padding: const EdgeInsets.all(13),
color: Colors.white,
radioColor: Color.fromARGB(255, 138, 5, 5),
title: Text(
varietyList[index],
style: const TextStyle(
fontSize: 25, color: Colors.black),
),
size: 28,
activeBorderColor: Colors.black,
focusColor: Colors.black,
type: GFRadioType.basic,
value: index,
groupValue: number,
onChanged: (ind) =>
setState(() => number = ind! as int),
inactiveIcon: null,
);

Flutter Prevent FutureBuilder from running twice

I am using FutureBuilder in flutter to fill up a drop down menu DropdownButtonFormField, i have the method
onChanged: (String? newValue) {
setState(() {
dropdownBrokers = newValue!;
});
},
Which allows the widget to rebuild and then assign the new value selected from the dropdown list. However, i am now experiencing a problem from my research stating that whenever the setState() is called, the future builder runs again, which makes my dropdown menu populate again. This is the full code snippet
FutureBuilder <List<Broker>>(
future: brokerData,
builder: (context, snapshot){
if(snapshot.hasData){
//do what needs to be done here
List<Broker>? data = snapshot.data;
for(var i = 0; i< data!.length; i++){
_brokers.add(data[i].firmName);
print(data[i].firmName);
}
return Container(
padding: EdgeInsets.all(10),
child: InputDecorator(
decoration: InputDecoration(
labelStyle: TextStyle(
color: Colors.grey
),
isDense: true,
contentPadding: EdgeInsets.all(7.0),
errorStyle: TextStyle(color: Colors.redAccent, fontSize: 16.0),
hintText: 'Select Broker',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5.0))),
child: DropdownButtonHideUnderline(
child: DropdownButtonFormField<String>(
alignment: Alignment.center,
value: dropdownBrokers,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.indigo),
onChanged: (String? newValue) {
setState(() {
dropdownBrokers = newValue!;
});
},
items: _brokers.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
),
),
);
Also i get an error when i reselect an item.
How do i prevent the FutureBuilder from running twice
FutureBuilder will always be rendered once unless recreated in the build method. So the solution to this can be done in two ways:
1: Only calling brokerData in the FutureBuilder method or
2: Only calling brokerData in the initState override.
You can not do both.
And I assume the data to be populated in the dropdown comes from the future builder. Then use snapshot.data of the future instead of an already created value in this case:
items: _brokers.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
Change to:
items: snapshot.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);

How to make DropdownButton menu width to fit text?

I want to have a dropdown button that has a width based on the menu text length. The dropdown arrow does not have any additional spacing between text.
Like this:
Currently, I am getting this:
My dropdown button arrow is positioned based on the longest text.
My code:
Widget dropdownList(){
return Container(
child: DropdownButton<Location>(
value: _selectedValue,
icon: const Icon(Icons.expand_more),
iconSize: 30,
underline: SizedBox(),
onChanged: (Location? newValue) {
setState(() {
_selectedValue = newValue!;
});
},
style: const TextStyle(color: Colors.blue),
selectedItemBuilder: (BuildContext context) {
return locationList.map((Location value) {
return Container(
child: Text(
value.name,
style: const TextStyle(color: Colors.black,fontSize: 30,
fontWeight: FontWeight.w300),
),
);
}).toList();
},
items: locationList.map<DropdownMenuItem<Location>>((Location value) {
return DropdownMenuItem<Location>(
value: value,
child: Text(value.name, style: TextStyle(color: Colors.black,fontSize: 15,
fontWeight: FontWeight.w300)),
);
}).toList(),
),
);
}
According to the documentations there is a property for DropDownButton called isExpanded:
isExpanded → bool
Set the dropdown's inner contents to horizontally fill its parent. [...]
final
so just set:
isExpanded = true;

Dropdownbutton restrict shown items

Ive got the following Problem:
I want the user to choose a day of month with a dropdownbutton. So my items are the numbers 1 to 31. Now the list got pretty long and the Dropdownbutton is really large. Is there a Solution to show e.g. only 5 Elements at the same time?
Widget buildDropdownMonthlyTurnus() {
return DropdownButton<int>(
value: _selectedDay,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.blue),
underline: Container(
height: 2,
color: Colors.blue,
),
onChanged: (int newValue) {
setState(() {
_selectedDay = newValue;
});
},
items: Constants.daysOfMonth.map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Text(
value.toString(),
style: new TextStyle(color: Colors.black),
),
);
}).toList());
}
In the link you see my problem with the large list.
enter image description here
For this problem you can use listview, inside a container and show manage its size according to the list items shown, you don't need to use dropdown items at all.
Use the menuMaxHeight property of the DropdownButton class. It's a recent addition to control the height of the menu.
Widget buildDropdownMonthlyTurnus() {
return DropdownButton<int>(
menuMaxHeight: 200.0,
value: _selectedDay,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.blue),
underline: Container(
height: 2,
color: Colors.blue,
),
onChanged: (int newValue) {
setState(() {
_selectedDay = newValue;
});
},
items: Constants.daysOfMonth.map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Text(
value.toString(),
style: new TextStyle(color: Colors.black),
),
);
}).toList());

Making Unique List Tile item in Flutter from Builder

I was wondering if it was possible to make items in List View unique in the sense that if I want to add something to the one, it only changes that particular list tile item, for example, if I added a leading icon from the DropDown to the one on the list, it adds that icon only to that particular list tile, and not the others (I can then add other icons from the dropdown to other list tile items).
Here's the code for it, I am not sure if this is possible since the builder is made from Bloc and an existing class Person, basically a template for each list item.
child: BlocConsumer<PersonBloc, List<Person>>(
builder: (context, personList) {
return ListView.separated(
itemBuilder: (BuildContext context, int index) {
print("personList: $personList");
Person person = personList[index];
return ListTile(
key: UniqueKey() //added unique key here, but it still fails
//for example, is it possible to make this list tile unique
//if I make change to it, it only changes the one I change, and not
//all of them in a list at once
//leading: DropDownButton() -> if I had a dropdown here with icon choices
//would it be possible for that icon only to be chosen for a particular item on the list?
trailing: Icon(Icons.person),
title: Text("${person.name} ${person.age}",
style: TextStyle(fontSize: 30)),
subtitle: Text(
"person.name",
style: TextStyle(fontSize: 25),
),
onTap: () => showDialog(context, person, index));
},
itemCount: personList.length,
separatorBuilder: (BuildContext context, int index) =>
Divider(color: Colors.black),
);
},
listener: (BuildContext context, personList) {},
),
more is explained in the comments.
EDIT
The UniqueKey() doesn't work, I tested in on my old test app I was making that works similarly with the ListTile as well, and when you change the dropdown for one, it changes automatically for all the items:
Also, here's the dropdown just so you can see if something should be changed there:
Widget _buildSize() {
return DropdownButton<String>(
focusColor: Colors.white,
value: _chosenSize,
//elevation: 5,
style: TextStyle(color: Colors.white),
iconEnabledColor: Colors.black,
items: <String>[
'',
'S',
'M',
'L',
'XL',
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(color: Colors.black),
),
);
}).toList(),
hint: Text(
"Size",
style: TextStyle(
color: Colors.black, fontSize: 14, fontWeight: FontWeight.w500),
),
onChanged: (String value) {
setState(() {
_chosenSize = value;
});
},
);
}
you can create an id field on existing Person class.
or you can use key, here about key
https://api.flutter.dev/flutter/foundation/Key-class.html
Edit :
sorry for my mistake, for reference link only. this is simple example of my solution
This is example of key usage
ListTile(
key:UniqueKey(),
)
take a look this link too
https://medium.com/flutter/keys-what-are-they-good-for-13cb51742e7d
*EDIT
The unique key is is wrong answer sorry.
first you have to create a list to contain your widget values
List<String> _yourWidgets = [];
then change your widget methods to retrieve the index
Widget _buildSize(int index) {
return DropdownButton<String>(
focusColor: Colors.white,
value: _chosenSize,
//elevation: 5,
style: TextStyle(color: Colors.white),
iconEnabledColor: Colors.black,
items: <String>[
'',
'S',
'M',
'L',
'XL',
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: _yourWidgets[index],
child: Text(
value,
style: TextStyle(color: Colors.black),
),
);
}).toList(),
hint: Text(
"Size",
style: TextStyle(
color: Colors.black, fontSize: 14, fontWeight: FontWeight.w500),
),
onChanged: (String value) {
setState(() {
_yourWidgets[index] = value;
});
},
);