let's say i want to make a quiz page that will shuffle the radio button option.
i've done trying to shuffle the List() (containing radio button) in init state but it doesn't working. radio button works perfectly fine, but it won't shuffle. here's my 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),
];
}
and then i use my second tot to make List but i will initialize it with listRadioOption() in initState, like this :
late List<Widget> list2; //list for checking my second tot..
and here's my 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 works fine with shuffle, but ended up with user can't press any button in radio button. my listRadioOption() works fine, but can't shuffling.
and i try again using this cascade operator like this :
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();
}
it works, but it keeps shuffling when user press any radio button. i know a little about cascade operator, but what does it mean if i used it in return statement like above code?
what i want is actually just shuffling the radio button (1 times) and radio button will change according to user tap
can anyone help me?
here is my full code :
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,
);
},
)
],
),
);
}
}
UPDATE
Here my shuffle's work but i can't change values :
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(),
);
},
)
],
),
);
}
}
Here is my second answer to your question, the first question was short but here I explain what I meant in the first answer. I rewrite your code to answer your question. Just copy and paste it to the DartPad to play and test with it.
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.