I am trying to make a quiz application. this is QuestionDetailsPage where i show all the MCQ question with 4 option from the database by MCQQuestionWidget
import 'package:flutter/material.dart';
import 'package:practice/models/CustomExam.dart';
import 'package:practice/models/exam.dart';
import 'package:practice/models/mcqquestion.dart';
import 'package:practice/pages/QuestionList.dart';
import 'package:practice/pages/questionDetailsPage.dart';
import 'package:practice/services/database_helper.dart';
import 'package:practice/widgets/CustomAppBar.dart';
import 'package:practice/widgets/MCQQuestionWidget%20.dart';
class QuestionDetailsPage extends StatefulWidget {
final int eid;
final double negetivemark;
QuestionDetailsPage({required this.eid, required this.negetivemark});
@override
_QuestionDetailsPageState createState() => _QuestionDetailsPageState();
}
class _QuestionDetailsPageState extends State<QuestionDetailsPage> {
late Future<List<MCQQuestionModel>> _questionsFuture;
DatabaseHelper _databaseHelper = DatabaseHelper();
double _totalMarks = 0;
Map<int, String> selectedOptions = {}; // Map to store selected options for each question ID
void updateTotalMarks(double marks) {
setState(() {
_totalMarks += marks;
});
}
@override
void initState() {
super.initState();
_questionsFuture = _databaseHelper.getQuestionsForExam(eid: widget.eid);
}
final gradient = LinearGradient(
colors: [Colors.blue, Colors.green],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: CustomAppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Handle button click event if needed
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25.0), // Adjust the border radius as needed
gradient: gradient, // Apply the gradient to the container
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
' $_totalMarks',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),
),
),
),
),
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
// Watermark logo
Opacity(
opacity: 0.1, // Set the opacity level of the logo
child: Image.asset(
'assests/logo.png', // Path to your watermark logo image
fit: BoxFit.cover,
),
),
// Question widgets
FutureBuilder<List<MCQQuestionModel>>(
future: _questionsFuture,
builder: (context, snapshot) {
// ... (handle connection states and errors)
if (snapshot.hasData) {
List<MCQQuestionModel> questions = snapshot.data!;
return ListView.builder(
itemCount: questions.length,
itemBuilder: (context, index) {
final question = questions[index];
final questionKey = PageStorageKey<int>(question.qid); // Unique key for each question
final questionWidget = MCQQuestionWidget(
key: questionKey,
question: question,
questionNumber:
index + 1, onUpdateMarks: updateTotalMarks,
selectedOption: selectedOptions[question.qid] ?? '',
onOptionSelected: (selectedOption) {
setState(() {
selectedOptions[question.qid] = selectedOption;
});
},
negetivemark: widget.negetivemark,// Assign question number
);
// Increment question number for the next question
return questionWidget;
},
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
],
),
),
);
}
}
This is the MCQQuestionWidget
import 'package:flutter/material.dart';
import 'package:practice/models/mcqquestion.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_math_fork/flutter_math.dart';
import 'package:flutter_math_fork/tex.dart';
import 'package:tex_text/tex_text.dart';
class CustomRadioButton extends StatelessWidget {
final String label;
final bool isSelected;
final bool isCorrectAnswer;
final Function(bool) onSelect;
final bool isEnabled;
CustomRadioButton(
{required this.label,
required this.isSelected,
required this.isCorrectAnswer,
required this.onSelect,
required this.isEnabled});
@override
Widget build(BuildContext context) {
Color borderColor = Colors.black;
Color iconColor = Colors.transparent;
IconData iconData = Icons.radio_button_unchecked;
if (isSelected) {
if (isCorrectAnswer) {
borderColor = Colors.green;
iconColor = Colors.green;
iconData = Icons.check_circle;
} else {
borderColor = Colors.red;
iconColor = Colors.red;
iconData = Icons.cancel;
}
}
return InkWell(
onTap: isEnabled ? () => onSelect?.call(!isSelected) : null,
child: Row(
children: <Widget>[
Container(
margin: const EdgeInsets.fromLTRB(20, 5, 0, 0),
width: 20.0,
height: 20.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: borderColor),
),
child: Center(
child: Icon(
iconData,
size: 18,
color: iconColor,
),
),
),
SizedBox(width: 8.0),
Expanded(
child: Text(
label,
style: GoogleFonts.notoSansBengali(
fontSize: 16, color: isSelected ? borderColor : Colors.black),
),
),
],
),
);
}
}
class MCQQuestionWidget extends StatefulWidget {
final MCQQuestionModel question;
final int questionNumber;
final Function(double) onUpdateMarks;
final double negetivemark;
MCQQuestionWidget(
{required this.question,
required this.questionNumber,
required this.onUpdateMarks,
required this.negetivemark, required String selectedOption, required Null Function(dynamic selectedOption) onOptionSelected, required PageStorageKey<int> key});
@override
_MCQQuestionWidgetState createState() => _MCQQuestionWidgetState();
}
class _MCQQuestionWidgetState extends State<MCQQuestionWidget> {
final TextEditingController _text = TextEditingController();
late int _selectedOption;
bool _showAnswer = false;
bool _isAnswerCorrect = false;
int _totalMarks = 0;
bool _questionLocked = false; // Flag to check if the question is locked
@override
void initState() {
super.initState();
_selectedOption = 0;
}
int getOptionNumber(String option) {
switch (option) {
case 'option1':
return 1;
case 'option2':
return 2;
case 'option3':
return 3;
case 'option4':
return 4;
default:
return 0; // Handle other cases if necessary
}
}
void _checkAnswer(int selectedOption) {
if (!_questionLocked) {
setState(() {
_selectedOption = selectedOption;
_isAnswerCorrect =
selectedOption == getOptionNumber(widget.question.answer);
if (_isAnswerCorrect) {
widget.onUpdateMarks(widget.question.questionmark);
} else {
widget.onUpdateMarks(-widget.negetivemark);
}
_questionLocked = true;
});
}
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
/* Text(
'প্রশ্ন- ${widget.questionNumber}. ',
style: GoogleFonts.notoSansBengali(
fontSize: 17, fontWeight: FontWeight.bold),
),*/
Flexible(
child: TexText(
'প্রশ্ন-${widget.questionNumber}ঃ '+
'${widget.question.questionwithequ}',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: Colors.deepPurpleAccent, fontWeight: FontWeight.bold),
mathStyle: MathStyle.text,
),
),
/* SizedBox(
width:
2), // Adding some space between the text widgets and Math.tex widget
Text(
'${widget.question.questionname}',
style: GoogleFonts.notoSansBengali(
fontSize: 16, fontWeight: FontWeight.bold),
),
SizedBox(
width:
2), // Adding some space between the text widgets and Math.tex widget
Flexible(
child: TexText(
'${widget.question.questionwithequ}',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: Colors.black, fontWeight: FontWeight.bold),
mathStyle: MathStyle.text,
),
),*/
],
),
),
CustomRadioButton(
label: "ক) ${widget.question.option1}",
isSelected: _selectedOption == 1,
isEnabled: _selectedOption == 0,
onSelect: (isSelected) {
setState(() {
_selectedOption = isSelected ? 1 : 0;
_checkAnswer(1);
});
},
isCorrectAnswer: widget.question.answer == 'option1',
),
CustomRadioButton(
label: "খ) ${widget.question.option2}",
isSelected: _selectedOption == 2,
isEnabled: _selectedOption == 0,
onSelect: (isSelected) {
setState(() {
_selectedOption = isSelected ? 2 : 0;
_checkAnswer(2);
});
},
isCorrectAnswer: widget.question.answer == 'option2',
),
CustomRadioButton(
label: "গ) ${widget.question.option3}",
isEnabled: _selectedOption == 0,
isSelected: _selectedOption == 3,
onSelect: (isSelected) {
setState(() {
_selectedOption = isSelected ? 3 : 0;
_checkAnswer(3);
});
},
isCorrectAnswer: widget.question.answer == 'option3',
),
CustomRadioButton(
label: "ঘ) ${widget.question.option4}",
isSelected: _selectedOption == 4,
isEnabled: _selectedOption == 0,
onSelect: (isSelected) {
setState(() {
_selectedOption = isSelected ? 4 : 0;
_checkAnswer(4);
});
},
isCorrectAnswer: widget.question.answer == 'option4',
),
Container(
margin: const EdgeInsets.fromLTRB(5, 5, 0, 0),
height: 34,
child: TextButton(
onPressed: () {
setState(() {
_showAnswer = !_showAnswer;
});
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(Set<MaterialState> states) {
return _showAnswer ? Colors.transparent : Colors.transparent;
}),
),
child: Container(
width: 120,
padding: EdgeInsets.all(0.0),
child: Row(
children: [
Text(
_showAnswer ? 'হাইড করুণ' : 'ব্যাখ্যা দেখুন',
style: TextStyle(
color: Colors.blue, fontWeight: FontWeight.bold),
),
Icon(
_showAnswer ? Icons.arrow_drop_up : Icons.arrow_drop_down,
color: Colors.blue),
],
),
),
),
),
if (_showAnswer)
Padding(
padding: EdgeInsets.all(2.0),
child: Text(' Explanation: ${widget.question.explanation}'),
),
Divider(
height: 5,
color: Colors.black12,
),
],
);
}
}
I am trying to make a quiz application. Now when user Scrolling the page then User selected/answered question are goes to deselect.
I am trying to make a quiz application. Now when user Scrolling the page then User selected/answered question are goes to deselect. I am new in flutter, i tried to find out the problem but could not find the exact reason why this is happen.
3
Answers
I think the problem is that future builders try not to use it because it resets the state when the user scrolls the screen. Try to get the data from the beginning, like initstate, and add a button at the end to load more data.
use AutomaticKeepAlive for your MCQQuestionWidget like this example :
You can try wrapping your
ListView.builder
withSingleChildScrollView
Code:
P.S. This is not optimal solution but one of the solution