【问题标题】:How to select only one group value of Radio button in Flutter?如何在 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;因为它是最终字段,所以无法赋值。接下来我该怎么办?

标签: flutter dart radio-button flutter-layout flutter-getx


【解决方案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 的位置的更多详细信息。请帮我解决这个问题,我被这个问题困扰了好几天
    • 当您传递静态值时,我上面的答案工作正常吗?
    • 确保您传递相同数据类型的相同值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-17
    • 2015-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 2012-04-23
    相关资源
    最近更新 更多