How to select only one group value of Radio button in Flutter?
Solution 1
Here is sample code by using your 'OptionRadioPage' Widget.
(but some code deleted because of leak of code)
As I commented, 'groupValue' should shared with two 'OptionRadioPage' widgets.
So I store that value 'OptionRadioPage' parent widget and
also pass the that value to 'OptionRadioPage' widgets.
import 'package:flutter/material.dart';
import 'package:flutter/src/material/radio_list_tile.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Main(),
);
}
}
class Main extends StatefulWidget {
Main({Key key}) : super(key: key);
@override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
int selectedButton;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
OptionRadio(
text: 'Male',
index: 0,
selectedButton: selectedButton,
press: (val) {
selectedButton = val;
setState(() {});
}),
OptionRadio(
text: 'Female',
index: 1,
selectedButton: selectedButton,
press: (val) {
selectedButton = val;
setState(() {});
}),
],
),
),
);
}
}
class OptionRadio extends StatefulWidget {
final String text;
final int index;
final int selectedButton;
final Function press;
const OptionRadio({
Key key,
this.text,
this.index,
this.selectedButton,
this.press,
}) : super();
@override
OptionRadioPage createState() => OptionRadioPage();
}
class OptionRadioPage extends State<OptionRadio> {
// QuestionController controllerCopy =QuestionController();
int id = 1;
bool _isButtonDisabled;
OptionRadioPage();
@override
void initState() {
_isButtonDisabled = false;
}
int _selected = null;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
widget.press(widget.index);
},
child: Container(
child: Row(
children: <Widget>[
Expanded(
child: Container(
// height: 60.0,
child: Theme(
data: Theme.of(context).copyWith(
unselectedWidgetColor: Colors.grey,
disabledColor: Colors.blue),
child: Column(children: [
RadioListTile(
title: Text(
"${widget.index + 1}. ${widget.text}",
style: TextStyle(color: Colors.black, fontSize: 16),
softWrap: true,
),
/*Here the selectedButton which is null initially takes place of value after onChanged. Now, I need to clear the selected button when other button is clicked */
groupValue: widget.selectedButton,
value: widget.index,
activeColor: Colors.green,
onChanged: (val) async {
debugPrint('Radio button is clicked onChanged $val');
// setState(() {
// debugPrint('Radio button setState $val');
// selectedButton = val;
// debugPrint('Radio button is clicked onChanged $widget.index');
// });
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setInt('intValue', val);
widget.press(widget.index);
},
toggleable: true,
),
]),
)),
),
],
),
),
);
}
}
Solution 2
This is happening because you are defining individual groupValue
for each Radio button. Rather than defining selectedButton
field for each button define it once in that file where you are calling this widget
and pass the value
through it's constructor
like below code :
class OptionRadio extends StatefulWidget {
final String text;
final int index;
final ValueChanged<Object> press;
final int selectButton;
const OptionRadio({
Key key,
this.text,
this.index,
this.press,
this.selectButton,
}) : super();
@override
OptionRadioPage createState() =>
OptionRadioPage(this.text, this.index, this.press);
}
and use this value
and press
function to your Radio button like this :
RadioListTile(
title: Text(
"${index + 1}. $text",
style: TextStyle(
color: Colors.green, fontSize: 16),
softWrap: true,
),
groupValue: widget.selectButton,
value: index,
activeColor: Colors.green,
onChanged: widget.press,
)
also pass ValueChanged(Object)
in place of VoidCallBack
:
final ValueChanged<Object> press;
and pass a proper function there like :
OptionRadio(
text: "abc",
index: index,
press: (val) => setState(() {
selectButton = int.parse(val.toString());
print("$selectButton");
}),
selectButton: selectButton,
)
Terin Tittu
Updated on January 02, 2023Comments
-
Terin Tittu over 1 year
I'm using Flutter-Dart-SQLite to develop a quiz application. Here I have used RadioListTile for radio button functionality. I am passing the text value from an array to this
StatefulWidget
. This is the code I'm using,import 'package:flutter/material.dart'; import 'package:get/get_state_manager/get_state_manager.dart'; import 'package:loginform_validation_demo/QuizApp/controllers/question_controller.dart'; import 'package:loginform_validation_demo/resource-file/constants.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter/src/material/radio_list_tile.dart'; class OptionRadio extends StatefulWidget { final String text; final int index; final VoidCallback press; const OptionRadio({ Key key, this.text, this.index, this.press, }) : super(); @override OptionRadioPage createState() => OptionRadioPage(this.text, this.index, this.press); } class OptionRadioPage extends State<OptionRadio> { final String text; int index; final VoidCallback press; QuestionController controllerCopy =QuestionController(); int id = 1; int selectedButton = null; bool _isButtonDisabled; OptionRadioPage(this.text, this.index, this.press); @override void initState() { _isButtonDisabled = false; } int _selected = null; @override Widget build(BuildContext context) { return GetBuilder<QuestionController>( init: QuestionController(), builder: (qnController) { Color getTheRightColor() { if (qnController.isAnswered) { if (index == qnController.selectedAns && qnController.selectedAns == qnController.correctAns) { return kBlackColor; } else if (index == qnController.selectedAns && qnController.selectedAns != qnController.correctAns) { return kBlackColor; } } return kBlackColor; } return InkWell( onTap: press, child: Container( child: Row( children: <Widget>[ Expanded( child: Container( // height: 60.0, child: Theme( data: Theme.of(context).copyWith( unselectedWidgetColor: Colors.grey, disabledColor: Colors.blue), child: Column(children: [ RadioListTile( title: Text( "${index + 1}. $text", style: TextStyle( color: getTheRightColor(), fontSize: 16), softWrap: true, ), /*Here the selectedButton which is null initially takes place of value after onChanged. Now, I need to clear the selected button when other button is clicked */ groupValue: selectedButton, value: index, activeColor: Colors.green, onChanged: (val) async { debugPrint( 'Radio button is clicked onChanged $val'); setState(() { debugPrint('Radio button setState $val'); selectedButton = val; debugPrint('Radio button is clicked onChanged $index'); }); SharedPreferences prefs = await SharedPreferences.getInstance(); prefs.setInt('intValue', val); }, toggleable: true, ), ] ), )), ), ], ), ), ); }); } @override State<StatefulWidget> createState() { throw UnimplementedError(); } }
Here the selectedButton which is null initially takes place of value of group value after
onChanged
. Now, I need to clear the selected button when the other button is clicked.I'm guessing I have something done wrong in group value part.
The part where I use OptionRadio,
class QuestionCardWidgetRadio extends StatelessWidget { OptionRadio optionRadio = OptionRadio(); QuestionCardWidgetRadio({ Key key, this.note, this.index, this.questionLength, }) : super(key: key); final BlazorQuiz note; final int index; final int questionLength; bool _isAnswered = false; bool get isAnswered => this._isAnswered; int selectedButton; @override Widget build(BuildContext context) { QuestionController _questionController = Get.put(QuestionController()); debugPrint("Answer Print : ${note.Answer.split(",")}"); return Container( margin: EdgeInsets.symmetric(horizontal: kDefaultPadding, vertical: 35), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(25), ), padding: EdgeInsets.all(kDefaultPadding), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( children: [ ...List.generate( note.Answer.split(",").length, (index) => OptionRadio( index: index, text: note.Answer.split(",")[index], press: (val) => setState(() { selectedButton = int.parse(val.toString()); print("$selectedButton"); }), selectedButton: selectedButton, // press: // () =>{ // _isAnswered = true, // _questionController.checkAns(note, index), // selectedButton, // } ), SingleChildScrollView( scrollDirection: Axis.vertical, child: TextButton( onPressed: () async { debugPrint("Clicked options : ${note.Answer.isNotEmpty} isAnswered: $isAnswered"); debugPrint('Check Index : ${index} check quiz model Lenght : ${QuizModel.question.length} check note Id : ${note.Id}'); if(index+1 == questionLength){ // score screen debugPrint('Navigate to score screen'); Text( "Submit", style: Theme.of(context) .textTheme .button .copyWith(color: Colors.white), ); _questionController.scoreNavigation(index, context); } else{ if (note.Answer.isNotEmpty == !isAnswered) { debugPrint('Clicked RadioBtn ${optionRadio.index}'); SharedPreferences prefs = await SharedPreferences.getInstance(); //Return int int intValue = prefs.getInt('intValue'); debugPrint('Int value from sharedPrefs: $intValue'); _questionController.nextQuestion(note,intValue,"","",[]); prefs.remove("intValue"); }else{ debugPrint('Click proper option for Radio'); } } }, child: Container( width: double.infinity, alignment: Alignment.center, padding: EdgeInsets.all(kDefaultPadding * 0.75), margin: EdgeInsets.all(37), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(10)), color: Color(ColorData.button) ), child: Text( "Next Question", style: Theme.of(context) .textTheme .button .copyWith(color: Colors.white), ), ), ), ), ], ), ), ); } setState(Null Function() param0) {} }
I get values from an array where the options are separated by comma. "Answer":"Male,Female"
-
KuKu over 2 yearsYou need to save 'selectedButton' value at outside of 'OptionRadio' Widget and share that value with 'OptionRadio' widget for 'Male' and 'OptionRadio' widget for 'Female'.
-
Terin Tittu over 2 years@KuKu I tested the above method and was able to receive the correct value that was clicked in the log, but the icon did not change colors. For both, Icon stays unselected. I tried to make a widget out of the specified value in setState(). selectedButton = val; because it is a final field, it is not possible to assign a value. What should I do next?
-
-
Terin Tittu over 2 yearsThanks for the answer. I tried the above code and in log I'm able to get proper value that is clicked but the icon doesn't change to any color. Icon remains unselected for both. I tried to assign selected value in setState() as widget.selectButton = val; but since its a final field couldn't assign value. Next what should I do?
-
Diwyansh over 2 yearsPlease check the updated answer and also change selectButton type to int. Let me know if there's any issue.
-
Terin Tittu over 2 yearsWhen I give static text value no issue rises, but here I'm getting value from an array and when I implement this code in my OptionRadio class error occurs in Scaffold part. I have added more details about the place where I use this OptionRadio. Please help me solve this issue, I'm stuck with this for days
-
Diwyansh over 2 yearsmy above answer working fine when you pass static values right?
-
Diwyansh over 2 yearsmake sure that you are passing same value of same data type.