Flutter toggle Radio widget - flutter

I'm using Radio widgets to set the user gender. These Radio button widgets are designed very well to handle the selection changes act accordingly, but I does not find a solution to somehow deselect one already selected option. I would like the gender radio group to be optional. But after user selects let's say female, he/she can change the selection to male, but can't deselect the female/male option. (So that none of the option is selected.)
There is only an onChanged property and it only changes if the gender changes. No such thing like onTap or something similar. So no matter if I check that gender if it's the same, onChanged won't be called.
For that reason I tried to use a GestureDetector which should solve this issue (with this I should be able to deselect the already selected option) but in case of radio widgets it does not work. I also tried to change it's behavior property but it did not help either.
Here is the function which I use to create gender radio option with a text in a Row.
Widget _buildRadioWithText(final genderChangeNotifier, final Gender genderParam, final String genderText) {
return Row(
children: <Widget>[
GestureDetector(
behavior: HitTestBehavior.translucent,
child: Radio<Gender>(
visualDensity: VisualDensity.compact,
value: genderParam,
groupValue: genderChangeNotifier.gender,
onChanged: (Gender gender) {
genderChangeNotifier.gender = gender;
print(gender.toString());
},
),
onTap: () {
if (genderChangeNotifier.gender == genderParam) {
genderChangeNotifier.gender = Gender.NA;
print("Not answered!");
}
},
),
GestureDetector(
onTap: () {
if (genderChangeNotifier.gender == genderParam) {
genderChangeNotifier.gender = Gender.NA;
print("Not answered!");
} else {
genderChangeNotifier.gender = genderParam;
print(genderParam.toString());
}
},
child: Text(
genderText,
style: TextStyle(
fontSize: _fontSize,
),
),
),
],
);
}
Function call:
_buildRadioWithText(genderChangeNotifier, Gender.FEMALE, "Female"),
_buildRadioWithText(genderChangeNotifier, Gender.MALE, "Male"),
genderChangeNotifieris just a provider to set and get the Gender value and notify listeners when a Gender is set.
final GenderNotifier genderChangeNotifier= Provider.of<GenderNotifier>(context);
GestureDetector's onTap works well when I tap on the Text widget. It selects then deselects the option just as I'd like to but in case of the Radio widget onTap is never called.
Any idea how to achieve the deselection when I tap/click on the Radio widget itself? And why the GestureDetector that wraps the Radio does not register the tap events?

The GestureDetector's onTap is not being called because the GestureDetector inside the Radio widget is taking priority. By default, when two GestureDetectors are competing for input, only one will "win". In this case, the winner is the one in the Radio. This article discusses "GestureArenas" and how you can allow both GestureDetectors to process input.
However, consider whether allowing the user to deselect the radio button is the correct solution. Radio buttons are designed not to be deselected once the user has selected an option. You might instead:
Offer a third, "I'd rather not say" option
Use toggleable buttons instead of a radio group
Use a dropdown menu instead of a radio group
See this answer for more info on the usability aspect of this.

Related

How do i modify the data of an existing variable in flutter?

I want to make an editable TextWidget in flutter but I don't really know how to go around it, I did some research, but still can't find a good solution.
Here's my sample code below.
I have a variable called
int qty = 1;
and so I called the variable in TextWidget
Column(
children: [
Text(
"${qty}",
style: TextStyle(),
)
],
),
I want to have these features that make user tab on the value to change it if they want, upon tap, a pop-up dialog will show to give the user the ability to change the existing value to whatever the user wants.
Please if anyone knows how, please help.
You will need a statfull widget to call setState and make the UI update with the new value stored in your qty variable. (I'am assuming that you are not using any state managment).
I wrote a possible solution for what you need.
Let look into some considerations:
Text will show whatever is in the qty as long we call setState after (or do it inside) we change the value of qty.
You need some widget to detect your tap. If you want to the text be 'clicable' then it should be wraped inside that widget.
The onTap/onPress call back of that widget should show a new widget. For this you can use the already made showDialog() and pass it a Dialog Widget. in here you will put your ui for that.
In some point of that UI you need to introduce the new value. So you can use a simple TextField that will save the introduced value, where you can assign it to qty, without forgetting to call setState! Note that it deal with strings, so you neet to do an int.parse() ou double.parse accordingly to you qty var type.
And I think that's it.
The could be other ways of doing it. This is a good and simple approach for your need.
I wrote a piece of code to help or somelse how is trying to do it:
InkWell(
// can be gesture detector, button, etc
onTap: () => showDialog(
context: context,
builder: (context) => Dialog(
child: Container(
color:
Colors.white60, // change it accordingly to you
height: 80, // change it accordingly to you
width: 200, // change it accordingly to you
child: Column(
children: [
const Text('Change your value here'),
TextField(
decoration:
InputDecoration(hintText: qty.toString()),
onChanged: (insertValue) => setState(() {
qty = int.parse(insertValue);
}),
// you can use other callBack function (like onComplete,
// onSaved), wich is more eficient than calling setState eveytime,
// but you have to do the needed adtaptions. Like onSave
// needs a key to call the save function. is easy just google it.
),
],
)),
)),
child: Text(
"${qty}",
),
),
What you are probably looking is a DropdownButton.
You would have something like this:
int qty = 1;
List<int> listOfValues = [1,2,3,4];
and then in your column you would have
DropdownButton<int>(
// This are the list of items that will appear in your dropdown menu.
// items is all the options you want your users to be able to select from,
// and it take a list of `DropdownMenuItem`. So instead of creating a `DropdownMenuItem`
// for each of the items in `listOfValues`, we iterate through it and return
// a `DropdownMenuItem`
items: listOfValues
.map((item) => DropdownMenuItem<int>(
value: item,
child: Text('$item'),
))
.toList(),
value: qty,
onChanged: (value) {
if (value != null) {
setState(() {
qty = value;
});
}
},
),
For more information on DropDownButton, check the following links:
https://api.flutter.dev/flutter/material/DropdownButton-class.html
https://www.youtube.com/watch?v=K8Y7sWZ7Q3s
Note: In a scenario where you want to increase the quantity of an item, like in a shopping cart, maybe having a button increment qty by 1 would be better.

Flutter- how to disable a Button untill the requireed fields are filled

I want to disable a button untill all the required fields are filled. I found similar questions here but all answers are based on making the onPressed property to null. But it does not disable the tap effect.
I want something like this. On disabled mode, clicking on the button won't even cause a Tap effect. Can someone help with a solution?
For a limited number of widgets, you can wrap them in a widget IgnorePointer: when its ignoring property is set to true, the sub-widget (actually, the entire subtree) is not clickable.
IgnorePointer(
ignoring: true, // or false
child: CustomButton(
onPressed: _login,
child: Text("Login"),
),
)
checkout this widget.set absorb to true when the required field is empty or is not validated.
https://api.flutter.dev/flutter/widgets/AbsorbPointer-class.html
In the textField add
onChanged : (val){ setastate((){});}
You mustbe already having a TextEditingController for the textfield. For example i shall name it phoneTextController.
Now in the button check the condition
phoneTextController.text.length > 10
For example
Inkwell(
onTap:(){
if(phoneTextController.text.length > 10){
SendOtp()
}
}
child: Container(
color: phoneTextController.text.length > 10 ? Colors.blue : Color.blue.withOpacity(0.5),
)
)
If you're using one of Flutter's built-in button Widgets, setting the onTap to null should automatically disable the button and its effect. Now all that remains is to conditionally do this during your build. Say, like the other answer, your TextEditingController is named phoneTextController:
ElevatedButton(
child: Text('Next'),
onTap: phoneTextController.text.length > 10 ? () => goToNextPage() : null,
),
Which will automatically enable the button and add the callback as soon as the condition is met (in this example, the input length is >10.

How to prevent selectedItemBuilder from moving text up

Using a DropdownButton, I need the selected item to be a different color than the list of items in the dropdown menu that appears when you tap the button. According to the Flutter documentation, I should use selectedItemBuilder. As can be seen right there in the example within the documentation, using the selectedItemBuilder results with the selected text being shifted up which does not look good.
How can I get the selected item to be down in line with the drop-down icon where it is supposed to be while having separate colors for the menu items and the selected item?
I found that this is an open issue on GitHub. A work around is to wrap the widget returned by the builder (and also the hint if you have one) in a Center widget.
DropdownButton<String>(
selectedItemBuilder: (_) {
return items.map<Widget>((String item) {
return Center(
child: Text(item),
);
}).toList();
},
hint: Center(child: myHintWidget),
...

SliverList setState to Affect Only Selected Item Flutter

I have a button on every item on my SliverList. When I click a specific list item button, I wish it change to a different widget by using setState. Only that specific item button should change to a different widget while the rest on the list retains its own item button.
Example below. The problem of course is that when I click any button on the list, all buttons on every item on the list changes. What is needed so that it affects only the specific item on the list whose button was pressed?
SliverList(
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
final item = gd[index];
return Container(
child: Column(
children: [
item.mypicwidget(context,item.pid),
_shownewwidget
? item.newwidget(context)
: RaisedButton(
onPressed: () {
setState(() {
_shownewwidget=true;
});
},
child: const Text('Press Me'),
),
]
),
);
Figured this out.
I believe the approach is to add to the existing list an object key to represent whether widget1 or widget2 to render, eg. tag: 'normal' or tag: 'clicked'. Depending on index.tag, either widget1 or widget2 will be displayed. So for a widget with a button wherein the tag is normal, the button will have a function when clicked changes the index.tag of the item from normal to clicked. When the Sliverlist sees this new value, it automatically renders the corresponding widget.

Flutter identify Widget built by loop or listview.builder

I can't get around this issue, how do I select Widget built by for loop or a ListView.builder?
In code I will post, I have buttons built by for loop,
I want to change button's background color on click.
I'm using Provider in my project so this function is implemented with Provider.
In my Provider class I made a boolean and a function to switch that boolean, and then I assigned it to buttons, and set it's color to value of that boolean with ternary operator.
While this somewhat works, the problem is... All of the buttons built by the for loop are affected,
meaning. I press one button, all of them change color.
How do I fix this?
My widget which includes mentioned buttons:
ListView(
children: [
for(int i = 0; i < recipeIngredients.length; i++)
IngredientsTab(
ingredientText: recipeIngredients[i],
textColor: categoryAndSeeAllColor,
width: width,
**buttonColor: provider.isButtonPressed ? accentColor : Colors.grey[100],**
buttonIcon: Icons.add,
buttonText: 'Add to cart',
buttonTextColor: accentColor,
**buttonAction: provider.changeBackground,**
),
],
),
In my Provider class:
bool isButtonPressed = false;
void changeBackground() {
isButtonPressed = isButtonPressed ? false : true;
notifyListeners();
}
}
While I understand this happens for obvious reason, I'm assigning this isButtonPressed to all buttons so when its true they all change... So that makes sense, I just don't know how to solve it, I'm also having similar issue in another project where items are built with ListView.builder...
So if someone would help me with this would mean a lot to me :)
Usually you have in the Provider a List of Ingredients.
Each Ingredient have an ID and a bool selected variable.
The buttonAction calls the method with the ID:
void changeBackground(int ingredientID) {
ListOfIngredients.singleWhere((elem)=>elem.ID==ingredientID)).isButtonPressed = !isButtonPressed;
notifyListeners();
}
Then you build the ListView using the List of Ingredients and for each Ingredient you check the isButtonPressed value.