skip to Main Content

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


  1. 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.

    Login or Signup to reply.
  2. use AutomaticKeepAlive for your MCQQuestionWidget like this example :

    class Foo extends StatefulWidget {
      @override
      FooState createState() {
        return new FooState();
      }
    }
    
    class FooState extends State<Foo> with AutomaticKeepAliveClientMixin {
      @override
      Widget build(BuildContext context) {
        super.build(context);
        return Container(
    
        );
      }
    
      @override
      bool get wantKeepAlive => true;
    }
    
    Login or Signup to reply.
  3. You can try wrapping your ListView.builder with SingleChildScrollView

    Add following property to ListView.builder:

     shrinkWrap: true,
     physics: const NeverScrollableScrollPhysics(),
    

    Code:

    SingleChildScrollView(
                child: ListView.builder(
                  shrinkWrap: true,
                  physics: const NeverScrollableScrollPhysics(),
                  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;
                  },
                ),
              )
    

    P.S. This is not optimal solution but one of the solution

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search