在颤动中洗牌单选按钮

shuffling radio button in flutter

假设我想制作一个可以随机播放单选按钮选项的测验页面。

我已经尝试在初始状态下随机播放 List()(包含单选按钮),但它不起作用。单选按钮工作得很好,但它不会随机播放。这是我的清单

  List<Widget> listRadioOption(){
    return [
      //this should be shuffeled 1 time after build (in init state)
      radioListTile(text: widget.option1, jawaban: ListJawaban.answer1),
      radioListTile(text: widget.option2, jawaban: ListJawaban.answer2),
      radioListTile(text: widget.option3, jawaban: ListJawaban.answer3),
      radioListTile(text: widget.option4, jawaban: ListJawaban.answer4),
      radioListTile(text: widget.option5, jawaban: ListJawaban.answer5),
    ];
  }

然后我使用我的第二个 tot 制作列表,但我将在 initState 中使用 listRadioOption() 对其进行初始化,如下所示:

 late List<Widget> list2; //list for checking my second tot..

这是我的 initState :

 @override
  void initState() {
    super.initState();
    print("---- INIT STATE ----");
    listRadioOption().shuffle(); //this doesn't shuffle the option, but radio option works perfectly
    list2 = listRadioOption(); //this work if i use this as children,
    list2.shuffle(); //but it won't changing radio option value when user click in it (i dont know why)
  }

list2 可以很好地随机播放,但最终导致用户无法按单选按钮中的任何按钮。我的 listRadioOption() 工作正常,但不能随机播放。

我再次尝试使用这样的级联运算符:

  List<Widget> listRadioOption(){
    return [
      //this should be shuffeled 1 time after build (in init state)
      radioListTile(text: widget.option1, jawaban: ListJawaban.answer1),
      radioListTile(text: widget.option2, jawaban: ListJawaban.answer2),
      radioListTile(text: widget.option3, jawaban: ListJawaban.answer3),
      radioListTile(text: widget.option4, jawaban: ListJawaban.answer4),
      radioListTile(text: widget.option5, jawaban: ListJawaban.answer5),
    ]..shuffle(); 
  }

它可以工作,但是当用户按下任何单选按钮时它会一直随机播放。 我对级联运算符略知一二,但是如果我像上面的代码那样在 return 语句中使用它意味着什么?

我想要的实际上只是随机播放单选按钮(1 次),单选按钮会根据用户的点击而改变

谁能帮帮我?

这是我的完整代码:

import 'package:flutter/material.dart';

enum ListJawaban{
  answer1,
  answer2,
  answer3,
  answer4,
  answer5
}

class QuizTemplate extends StatefulWidget {
  const QuizTemplate({
    Key? key,
    required this.question,
    required this.option1,
    required this.option2,
    required this.option3,
    required this.option4,
    required this.option5,
    required this.qNumber
  }) : super(key: key);

  final String question;
  final String option1;
  final String option2;
  final String option3;
  final String option4;
  final String option5;
  final int qNumber;

  @override
  State<QuizTemplate> createState() => _QuizTemplateState();
}

class _QuizTemplateState extends State<QuizTemplate> {

  ListJawaban? _value = null;

  Widget radioListTile({required String text, required ListJawaban jawaban}){
    return RadioListTile<ListJawaban>(
      title: Text("$text"),
      value: jawaban,
      groupValue: _value,
      onChanged: (ListJawaban? newValue){
        setState(() {
          print("jawaban : $newValue");
          _value = newValue;
        });
      },
    );
  }

 
  List<Widget> listRadioOption(){
    return [
      //this should be shuffeled 1 time after build (in init state)
      radioListTile(text: widget.option1, jawaban: ListJawaban.answer1),
      radioListTile(text: widget.option2, jawaban: ListJawaban.answer2),
      radioListTile(text: widget.option3, jawaban: ListJawaban.answer3),
      radioListTile(text: widget.option4, jawaban: ListJawaban.answer4),
      radioListTile(text: widget.option5, jawaban: ListJawaban.answer5),
    ];
  }

  late List<Widget> list2; //list for checking my second tot..

  @override
  void initState() {
    super.initState();
    print("---- INIT STATE ----");
    listRadioOption().shuffle(); //this doesn't shuffle the option, but radio option works perfectly
    list2 = listRadioOption(); //this work if i use this as children,
    list2.shuffle(); //but it won't changing radio option value when user click in it (i dont know why)
  }

  @override
  Widget build(BuildContext context) {
    print("---- BUILD ----");
    return Container(
      child: Column(
        children: [
          Text("${widget.qNumber}. ${widget.question}",style: TextStyle(
            fontSize: 20,
          ),),
          Builder(
            //i actually didn't need this builder, but i used it just for checking variables
            builder: (context){
              print("---- BUILDER ----");
              listRadioOption().shuffle();
              return Column(
                children: listRadioOption(),
                //children: list2,
              );
            },
          )
        ],
      ),
    );
  }
}

更新

这是我的随机播放工作,但我无法更改值:

import 'package:flutter/material.dart';

enum ListJawaban{
  answer1,
  answer2,
  answer3,
  answer4,
  answer5
}

class QuizTemplate extends StatefulWidget {
  const QuizTemplate({
    Key? key,
    required this.question,
    required this.option1,
    required this.option2,
    required this.option3,
    required this.option4,
    required this.option5,
    required this.qNumber
  }) : super(key: key);

  final String question;
  final String option1;
  final String option2;
  final String option3;
  final String option4;
  final String option5;
  final int qNumber;

  @override
  State<QuizTemplate> createState() => _QuizTemplateState();
}

class _QuizTemplateState extends State<QuizTemplate> {

  ListJawaban? _value = null;

  Widget radioListTile({required String text, required ListJawaban jawaban}){
    return RadioListTile<ListJawaban>(
      title: Text("$text"),
      value: jawaban,
      groupValue: _value,
      onChanged: (ListJawaban? newValue){
        setState(() {
          print("jawaban : $newValue");
          _value = newValue;
        });
      },
    );
  }


  List<Widget> listRadioOption(){
    return [
      //this should be shuffeled 1 time after build (in init state)
      radioListTile(text: widget.option1, jawaban: ListJawaban.answer1),
      radioListTile(text: widget.option2, jawaban: ListJawaban.answer2),
      radioListTile(text: widget.option3, jawaban: ListJawaban.answer3),
      radioListTile(text: widget.option4, jawaban: ListJawaban.answer4),
      radioListTile(text: widget.option5, jawaban: ListJawaban.answer5),
    ];
  }

  late List<Widget> list2; //list for checking my second tot..

  List<Widget> option(){
    return list2;
  }

  @override
  void initState() {
    super.initState();
    print("---- INIT STATE ----");
    // listRadioOption().shuffle(); //this doesn't shuffle the option, but radio option works perfectly
    list2 = listRadioOption(); //this work if i use this as children,
    list2.shuffle(); //but it won't changing radio option value when user click in it (i dont know why)
  }

  @override
  Widget build(BuildContext context) {
    print("---- BUILD ----");
    return Container(
      child: Column(
        children: [
          Text("${widget.qNumber}. ${widget.question}",style: TextStyle(
            fontSize: 20,
          ),),
          Builder(
            //i actually didn't need this builder, but i used it just for checking variables
            builder: (context){
              print("---- BUILDER ----");
              return Column(
                children: option(),
              );
            },
          )
        ],
      ),
    );
  }
}

这是我对你问题的第二个回答,第一个问题很简短,但在这里我解释一下我在第一个答案中的意思。我重写你的代码来回答你的问题。只需将其复制并粘贴到 DartPad 即可播放和测试。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Home(title: 'English Words Quiz'),
    );
  }
}

class Home extends StatelessWidget {
  final String title;

  const Home({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: QuizTemplate(
        model: QuizModel(
          question: 'They cleaned _____ shoes.',
          describe: 'Choose correct alternative',
          questionNumber: 4,
          answers: ['there', 'their', 'they´re', 'they'],
          correctAnswer: 'their',
        ),
      ),
    );
  }
}

class QuizTemplate extends StatefulWidget {
  const QuizTemplate({Key? key, required this.model}) : super(key: key);

  final QuizModel model;

  @override
  State<QuizTemplate> createState() => _QuizTemplateState();
}

class _QuizTemplateState extends State<QuizTemplate> {
  // The list outside of builder to avoid rebuilding.
  late final List<String> _answers;

  @override
  void initState() {
    _answers = widget.model.shuffled;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ListTile(
          leading: CircleAvatar(
            child: Text(
              '${widget.model.questionNumber}',
              style: Theme.of(context).textTheme.headline5,
            ),
          ),
          title: Text(
            widget.model.question,
            style: Theme.of(context).textTheme.headline5,
          ),
          subtitle: widget.model.describe == null
              ? null
              : Text(
                  widget.model.describe!,
                  style: Theme.of(context).textTheme.subtitle2,
                ),
        ),
        for (final answer in _answers)
          RadioListTile<int>(
            title: Text(answer),
            value: widget.model.indexOf(answer),
            groupValue: widget.model.currentIndex,
            onChanged: (index) {
              setState(() {
                widget.model.updateCurrentIndex(index);
              });
            },
          ),
        if (widget.model.currentIndex != null)
          Text(
            widget.model.isCorrectAnswer
                ? 'The correct answer is ${widget.model.correctAnswer}'
                : 'The answer is incorrect',
          ),
      ],
    );
  }
}

class QuizModel {
  QuizModel({
    required this.question,
    required this.answers,
    required this.correctAnswer,
    this.describe,
    this.questionNumber,
  })  : assert(answers.isNotEmpty, "The answers can't be empty"),
        assert(
          answers.contains(correctAnswer),
          'The answers must include correct answer, $correctAnswer is incorrect.',
        );

  final String question;
  final String? describe;
  final int? questionNumber;
  final String correctAnswer;
  final List<String> answers;

  int? _currentIndex;
  int? get currentIndex => _currentIndex;
  List<String> get shuffled => answers..shuffle();
  bool get isCorrectAnswer => indexOf(correctAnswer) == _currentIndex;

  int indexOf(String answer) => answers.indexOf(answer);

  void updateCurrentIndex(int? index) {
    _currentIndex = index;
  }
}

Dart 2.17 enhanced enum will be perfect for your situation and will make your code cleaner.