如何在 Flutter 中只选择一组单选按钮的值?

Posted

技术标签:

【中文标题】如何在 Flutter 中只选择一组单选按钮的值?【英文标题】:How to select only one group value of Radio button in Flutter? 【发布时间】:2021-12-29 10:52:54 【问题描述】:

我正在使用 Flutter-Dart-SQLite 开发一个测验应用程序。在这里,我使用 RadioListTile 来实现单选按钮功能。我将数组中的文本值传递给这个StatefulWidget。 这是我正在使用的代码,

    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();
      
    

这里的 selectedButton 最初为 null 代替 onChanged 之后的组值的值。现在,我需要在单击另一个按钮时清除选定的按钮。

我猜我在组值部分做错了。

我使用 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) 

我从一个以逗号分隔选项的数组中获取值。 “答案”:“男,女”

【问题讨论】:

您需要将 'selectedButton' 值保存在 'OptionRadio' 小部件之外,并与 'Male' 的 'OptionRadio' 小部件和 'Female' 的 'OptionRadio' 小部件共享该值。 @KuKu 我测试了上面的方法并且能够接收到在日志中被点击的正确值,但是图标没有改变颜色。对于两者,Icon 保持未选中状态。我试图用 setState() 中的指定值制作一个小部件。选定按钮 = val;因为它是最终字段,所以无法赋值。接下来我该怎么办? 【参考方案1】:

这是使用“OptionRadioPage”小部件的示例代码。 (但由于代码泄漏,部分代码被删除) 正如我评论的那样,“groupValue”应该与两个“OptionRadioPage”小部件共享。 所以我存储该值“OptionRadioPage”父小部件和 还将该值传递给“OptionRadioPage”小部件。

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,
                  ),
                ]),
              )),
            ),
          ],
        ),
      ),
    );
  

【讨论】:

【参考方案2】:

发生这种情况是因为您为每个 Radio 按钮定义了单独的 groupValue。而不是为每个按钮定义selectedButton 字段,而是在您调用此widget 的文件中定义一次,然后通过constructor 传递value,如下代码所示:

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);

然后将这个valuepress 函数用于您的Radio 按钮,如下所示:

                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,
                            )

也通过ValueChanged(Object) 代替VoidCallBack

final ValueChanged<Object> press;

并在那里传递一个适当的函数,例如:

OptionRadio(
                        text: "abc",
                        index: index,
                        press: (val) => setState(() 
                          selectButton = int.parse(val.toString());
                          print("$selectButton");
                        ),
                        selectButton: selectButton,
                      )

【讨论】:

感谢您的回答。我尝试了上面的代码,在日志中我能够获得被点击的正确值,但图标不会变成任何颜色。两者的图标均未选中。我试图将 setState() 中的选定值分配为 widget.selectButton = val;但由于它的最终字段无法赋值。接下来我该怎么办? 请检查更新的答案,并将 selectButton 类型更改为 int。如果有任何问题,请告诉我。 当我给出静态文本值时没有问题出现,但是在这里我从数组中获取值并且当我在我的 OptionRadio 类中实现此代码时,Scaffold 部分发生错误。我添加了有关我使用此 OptionRadio 的位置的更多详细信息。请帮我解决这个问题,我被这个问题困扰了好几天 当您传递静态值时,我上面的答案工作正常吗? 确保您传递相同数据类型的相同值。

以上是关于如何在 Flutter 中只选择一组单选按钮的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的程序中保存一组单选按钮的状态?

如何指示需要一组单选按钮[重复]

一行中的一组单选按钮,同时选择一行中的一个单选按钮,它会影响另一行目标-c

在一组单选按钮中怎样设置默认选项呢/在哪里设置呢?

如何居中对齐测验标题中心下方的一组单选按钮并保持响应?

如何将“其他”文本输入添加到 HTML 表单中的一组单选按钮?