How to select only one group value of Radio button in Flutter?

805

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.
enter image description here

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,
                      )
Share:
805
Terin Tittu
Author by

Terin Tittu

Updated on January 02, 2023

Comments

  • Terin Tittu
    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.

    enter image description here

    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
      KuKu over 2 years
      You 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
      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
    Terin Tittu over 2 years
    Thanks 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
    Diwyansh over 2 years
    Please check the updated answer and also change selectButton type to int. Let me know if there's any issue.
  • Terin Tittu
    Terin Tittu over 2 years
    When 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
    Diwyansh over 2 years
    my above answer working fine when you pass static values right?
  • Diwyansh
    Diwyansh over 2 years
    make sure that you are passing same value of same data type.