diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/component/notification/pop_up_confirmation.dart b/lib/component/notification/pop_up_confirmation.dart index c069e63..76fc147 100644 --- a/lib/component/notification/pop_up_confirmation.dart +++ b/lib/component/notification/pop_up_confirmation.dart @@ -2,6 +2,44 @@ import 'package:flutter/material.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart'; class AppDialog { + static Future showMessage(BuildContext context, String message) async { + await showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext context) { + return Dialog( + backgroundColor: AppColors.background, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Padding( + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Icon( + Icons.info_outline, + size: 40, + color: AppColors.primaryBlue, + ), + const SizedBox(height: 16), + Text( + message, + style: const TextStyle( + fontSize: 16, + color: AppColors.darkText, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + }, + ); + } + static Future showExitConfirmationDialog(BuildContext context) async { await showDialog( context: context, diff --git a/lib/component/widget/question_container_widget.dart b/lib/component/widget/question_container_widget.dart index 18b71d4..c53ee95 100644 --- a/lib/component/widget/question_container_widget.dart +++ b/lib/component/widget/question_container_widget.dart @@ -8,7 +8,11 @@ class QuestionContainerWidget extends StatelessWidget { final QuestionData question; final AnsweredQuestion? answeredQuestion; - const QuestionContainerWidget({super.key, required this.question, this.answeredQuestion}); + const QuestionContainerWidget({ + super.key, + required this.question, + this.answeredQuestion, + }); @override Widget build(BuildContext context) { @@ -16,103 +20,132 @@ class QuestionContainerWidget extends StatelessWidget { width: double.infinity, margin: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - border: Border.all(color: AppColors.borderLight), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 6, - offset: const Offset(2, 2), - ), - ], - ), + decoration: _containerDecoration, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Soal ${question.index}', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: AppColors.darkText)), + _buildTitle(), const SizedBox(height: 6), - Text( - _mapQuestionTypeToText(question.type), - style: const TextStyle(fontSize: 12, color: AppColors.softGrayText, fontStyle: FontStyle.italic), - ), + _buildTypeLabel(), const SizedBox(height: 12), - Text( - question.question ?? '-', - style: const TextStyle(fontSize: 16, color: AppColors.darkText), - ), + _buildQuestionText(), const SizedBox(height: 16), - _buildAnswerSection(question), + _buildAnswerSection(), if (answeredQuestion != null) ...[ const SizedBox(height: 16), - _buildAnsweredSection(answeredQuestion!), + _buildAnsweredSection(question, answeredQuestion!), ], const SizedBox(height: 10), - Text( - 'Durasi: ${question.duration} detik', - style: TextStyle(fontSize: 14, color: AppColors.softGrayText), + _buildDurationInfo(), + ], + ), + ); + } + + // --- UI Builders --- + + Widget _buildTitle() => Text( + 'Soal ${question.index}', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: AppColors.darkText, + ), + ); + + Widget _buildTypeLabel() => Text( + _mapQuestionTypeToText(question.type), + style: const TextStyle( + fontSize: 12, + color: AppColors.softGrayText, + fontStyle: FontStyle.italic, + ), + ); + + Widget _buildQuestionText() => Text( + question.question ?? '-', + style: const TextStyle(fontSize: 16, color: AppColors.darkText), + ); + + Widget _buildAnswerSection() { + switch (question.type) { + case QuestionType.option: + return _buildOptionAnswers(); + case QuestionType.fillTheBlank: + return _buildFillInBlankAnswers(); + case QuestionType.trueOrFalse: + return _buildTrueFalseAnswer(); + default: + return const SizedBox(); + } + } + + Widget _buildOptionAnswers() { + final options = question.options ?? []; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: options.map((option) { + final isCorrect = option.index == question.correctAnswerIndex; + return _buildOptionItem(option.text, isCorrect); + }).toList(), + ); + } + + Widget _buildOptionItem(String text, bool isCorrect) { + return Container( + margin: const EdgeInsets.symmetric(vertical: 4), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), + decoration: BoxDecoration( + color: isCorrect ? AppColors.primaryBlue.withOpacity(0.1) : Colors.transparent, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Icon( + isCorrect ? Icons.check_circle_rounded : Icons.circle_outlined, + size: 18, + color: isCorrect ? AppColors.primaryBlue : AppColors.softGrayText, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + text, + style: TextStyle( + fontWeight: isCorrect ? FontWeight.bold : FontWeight.normal, + color: isCorrect ? AppColors.primaryBlue : AppColors.darkText, + ), + ), ), ], ), ); } - Widget _buildAnswerSection(QuestionData question) { - if (question.type == QuestionType.option && question.options != null) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: question.options!.map((option) { - bool isCorrect = question.correctAnswerIndex == option.index; - return Container( - margin: const EdgeInsets.symmetric(vertical: 4), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), - decoration: BoxDecoration( - color: isCorrect ? AppColors.primaryBlue.withOpacity(0.1) : Colors.transparent, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - children: [ - Icon( - isCorrect ? Icons.check_circle_rounded : Icons.circle_outlined, - size: 18, - color: isCorrect ? AppColors.primaryBlue : AppColors.softGrayText, - ), - const SizedBox(width: 8), - Expanded( - child: Text( - option.text, - style: TextStyle( - fontWeight: isCorrect ? FontWeight.bold : FontWeight.normal, - color: isCorrect ? AppColors.primaryBlue : AppColors.darkText, - ), - ), - ), - ], - ), - ); - }).toList(), - ); - } else if (question.type == QuestionType.fillTheBlank) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildFillTheBlankPossibilities(question.answer ?? '-'), - ); - } else if (question.type == QuestionType.trueOrFalse) { - return Text( - 'Jawaban: ${question.answer ?? '-'}', - style: const TextStyle(color: AppColors.softGrayText), - ); - } else { - return const SizedBox(); - } + Widget _buildFillInBlankAnswers() { + final variations = _generateFillBlankVariations(question.answer ?? '-'); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: variations.map((text) => _buildBulletText(text)).toList(), + ); } - Widget _buildAnsweredSection(AnsweredQuestion answered) { + Widget _buildTrueFalseAnswer() { + return Text( + 'Jawaban: ${question.answer ?? '-'}', + style: const TextStyle(color: AppColors.softGrayText), + ); + } + + Widget _buildAnsweredSection(QuestionData question, AnsweredQuestion answered) { + String answer = question.type == QuestionType.option ? question.options![int.parse(answered.selectedAnswer)].text : answered.selectedAnswer; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text('Jawaban Anda:', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: AppColors.darkText)), + const Text( + 'Jawaban Anda:', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: AppColors.darkText), + ), const SizedBox(height: 6), Row( children: [ @@ -124,7 +157,7 @@ class QuestionContainerWidget extends StatelessWidget { const SizedBox(width: 8), Expanded( child: Text( - answered.selectedAnswer, + answer, style: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, @@ -138,57 +171,75 @@ class QuestionContainerWidget extends StatelessWidget { ); } - String _mapQuestionTypeToText(QuestionType? type) { - switch (type) { - case QuestionType.option: - return 'Tipe: Pilihan Ganda'; - case QuestionType.fillTheBlank: - return 'Tipe: Isian Kosong'; - case QuestionType.trueOrFalse: - return 'Tipe: Benar / Salah'; - default: - return 'Tipe: Tidak diketahui'; - } + Widget _buildBulletText(String text) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + children: [ + const Icon(Icons.arrow_right, size: 18, color: AppColors.softGrayText), + const SizedBox(width: 6), + Text( + text, + style: const TextStyle(color: AppColors.darkText), + ), + ], + ), + ); } - List _buildFillTheBlankPossibilities(String answer) { - List possibilities = [ + Widget _buildDurationInfo() { + String duration = question.duration.toString(); + if (answeredQuestion != null) duration = answeredQuestion!.duration.toString(); + + return Text( + 'Durasi: $duration detik', + style: const TextStyle(fontSize: 14, color: AppColors.softGrayText), + ); + } + + // --- Utils --- + + List _generateFillBlankVariations(String answer) { + return [ _capitalizeEachWord(answer), answer.toLowerCase(), _capitalizeFirstWordOnly(answer), ]; - - return possibilities.map((option) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 2), - child: Row( - children: [ - const Icon(Icons.arrow_right, size: 18, color: AppColors.softGrayText), - const SizedBox(width: 6), - Text( - option, - style: const TextStyle(color: AppColors.darkText), - ), - ], - ), - ); - }).toList(); } String _capitalizeEachWord(String text) { - return text.split(' ').map((word) { - if (word.isEmpty) return word; - return word[0].toUpperCase() + word.substring(1).toLowerCase(); - }).join(' '); + return text.split(' ').map((w) => w.isNotEmpty ? '${w[0].toUpperCase()}${w.substring(1).toLowerCase()}' : '').join(' '); } String _capitalizeFirstWordOnly(String text) { - if (text.isEmpty) return text; - List parts = text.split(' '); - parts[0] = parts[0][0].toUpperCase() + parts[0].substring(1).toLowerCase(); + final parts = text.split(' '); + if (parts.isEmpty) return text; + parts[0] = _capitalizeEachWord(parts[0]); for (int i = 1; i < parts.length; i++) { parts[i] = parts[i].toLowerCase(); } return parts.join(' '); } + + String _mapQuestionTypeToText(QuestionType? type) { + return switch (type) { + QuestionType.option => 'Tipe: Pilihan Ganda', + QuestionType.fillTheBlank => 'Tipe: Isian Kosong', + QuestionType.trueOrFalse => 'Tipe: Benar / Salah', + _ => 'Tipe: Tidak diketahui', + }; + } + + BoxDecoration get _containerDecoration => BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.borderLight), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 6, + offset: const Offset(2, 2), + ), + ], + ); } diff --git a/lib/feature/quiz_play/controller/quiz_play_controller.dart b/lib/feature/quiz_play/controller/quiz_play_controller.dart index 355fa0a..130f08c 100644 --- a/lib/feature/quiz_play/controller/quiz_play_controller.dart +++ b/lib/feature/quiz_play/controller/quiz_play_controller.dart @@ -3,22 +3,29 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:quiz_app/app/routes/app_pages.dart'; +import 'package:quiz_app/component/notification/pop_up_confirmation.dart'; import 'package:quiz_app/data/models/quiz/library_quiz_model.dart'; import 'package:quiz_app/data/models/quiz/question_listings_model.dart'; class QuizPlayController extends GetxController { late final QuizData quizData; + // State & UI final currentIndex = 0.obs; final timeLeft = 0.obs; final isStarting = true.obs; final isAnswerSelected = false.obs; + final prepareDuration = 3.obs; + + // Answer-related final selectedAnswer = ''.obs; final idxOptionSelected = (-1).obs; + final choosenAnswerTOF = 0.obs; final List answeredQuestions = []; + // Input controller final answerTextController = TextEditingController(); - final choosenAnswerTOF = 0.obs; + Timer? _timer; QuestionListing get currentQuestion => quizData.questionListings[currentIndex.value]; @@ -29,16 +36,12 @@ class QuizPlayController extends GetxController { quizData = Get.arguments as QuizData; _startCountdown(); - // Listener untuk fill the blank + // Listener untuk fill in the blank answerTextController.addListener(() { - if (answerTextController.text.trim().isNotEmpty) { - isAnswerSelected.value = true; - } else { - isAnswerSelected.value = false; - } + isAnswerSelected.value = answerTextController.text.trim().isNotEmpty; }); - // Listener untuk pilihan true/false + // Listener untuk true/false ever(choosenAnswerTOF, (value) { if (value != 0) { isAnswerSelected.value = true; @@ -47,14 +50,19 @@ class QuizPlayController extends GetxController { } void _startCountdown() async { - await Future.delayed(const Duration(seconds: 3)); isStarting.value = false; + for (int i = 3; i > 0; i--) { + prepareDuration.value = i; + await Future.delayed(const Duration(seconds: 1)); + } _startTimer(); + isStarting.value = true; } void _startTimer() { timeLeft.value = currentQuestion.duration; _timer?.cancel(); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (timeLeft.value > 0) { timeLeft.value--; @@ -63,20 +71,16 @@ class QuizPlayController extends GetxController { _nextQuestion(); } }); + isAnswerSelected.value = false; } void selectAnswerOption(int selectedIndex) { selectedAnswer.value = selectedIndex.toString(); - isAnswerSelected.value = true; idxOptionSelected.value = selectedIndex; + isAnswerSelected.value = true; } - // void selectAnswerFTB(String answer) { - // selectedAnswer.value = answer; - // isAnswerSelected.value = true; - // } - void onChooseTOF(bool value) { choosenAnswerTOF.value = value ? 1 : 2; selectedAnswer.value = value.toString(); @@ -84,22 +88,24 @@ class QuizPlayController extends GetxController { void _submitAnswerIfNeeded() { final question = currentQuestion; - String userAnswer = ""; + String userAnswer = ''; - if (question.type == "fill_the_blank") { - userAnswer = answerTextController.text.trim(); - } else if (question.type == "option") { - userAnswer = selectedAnswer.value.trim(); - } else if (question.type == "true_false") { - userAnswer = selectedAnswer.value.trim(); + switch (question.type) { + case 'fill_the_blank': + userAnswer = answerTextController.text.trim(); + break; + case 'option': + case 'true_false': + userAnswer = selectedAnswer.value.trim(); + break; } - answeredQuestions.add(AnsweredQuestion( index: currentIndex.value, - question: question.question, + questionIndex: question.index, selectedAnswer: userAnswer, correctAnswer: question.targetAnswer.trim(), isCorrect: userAnswer.toLowerCase() == question.targetAnswer.trim().toLowerCase(), + duration: currentQuestion.duration - timeLeft.value, )); } @@ -110,20 +116,28 @@ class QuizPlayController extends GetxController { void _nextQuestion() { _timer?.cancel(); + if (currentIndex.value < quizData.questionListings.length - 1) { currentIndex.value++; - answerTextController.clear(); - selectedAnswer.value = ''; - choosenAnswerTOF.value = 0; + _resetAnswerState(); _startTimer(); } else { _finishQuiz(); } } - void _finishQuiz() { - _timer?.cancel(); + void _resetAnswerState() { + answerTextController.clear(); + selectedAnswer.value = ''; + choosenAnswerTOF.value = 0; + idxOptionSelected.value = -1; + isAnswerSelected.value = false; + } + void _finishQuiz() async { + _timer?.cancel(); + AppDialog.showMessage(Get.context!, "Yeay semua soal selesai"); + await Future.delayed(Duration(seconds: 2)); Get.offAllNamed( AppRoutes.resultQuizPage, arguments: [quizData, answeredQuestions], @@ -140,26 +154,27 @@ class QuizPlayController extends GetxController { class AnsweredQuestion { final int index; - final String question; + final int questionIndex; final String selectedAnswer; final String correctAnswer; final bool isCorrect; + final int duration; AnsweredQuestion({ required this.index, - required this.question, + required this.questionIndex, required this.selectedAnswer, required this.correctAnswer, required this.isCorrect, + required this.duration, }); - Map toJson() { - return { - 'index': index, - 'question': question, - 'selectedAnswer': selectedAnswer, - 'correctAnswer': correctAnswer, - 'isCorrect': isCorrect, - }; - } + Map toJson() => { + 'index': index, + 'question_index': questionIndex, + 'selectedAnswer': selectedAnswer, + 'correctAnswer': correctAnswer, + 'isCorrect': isCorrect, + 'duration': duration, + }; } diff --git a/lib/feature/quiz_play/view/quiz_play_view.dart b/lib/feature/quiz_play/view/quiz_play_view.dart index 46981e6..dc6ac95 100644 --- a/lib/feature/quiz_play/view/quiz_play_view.dart +++ b/lib/feature/quiz_play/view/quiz_play_view.dart @@ -11,99 +11,33 @@ class QuizPlayView extends GetView { Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF9FAFB), - appBar: AppBar( - backgroundColor: Colors.transparent, - elevation: 0, - automaticallyImplyLeading: false, - title: const Text( - 'Kerjakan Soal', - style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold), - ), - iconTheme: const IconThemeData(color: Colors.black), - centerTitle: true, - ), + // appBar: _buildAppBar(), body: SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: Obx(() { - final question = controller.currentQuestion; + if (!controller.isStarting.value) { + return Center( + child: Text( + "Ready in ${controller.prepareDuration}", + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + )); + } + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LinearProgressIndicator( - value: controller.timeLeft.value / question.duration, - minHeight: 8, - backgroundColor: Colors.grey[300], - valueColor: const AlwaysStoppedAnimation(Color(0xFF2563EB)), - ), + _buildCustomAppBar(), const SizedBox(height: 20), - Text( - 'Soal ${controller.currentIndex.value + 1} dari ${controller.quizData.questionListings.length}', - style: const TextStyle( - fontSize: 16, - color: Colors.grey, - fontWeight: FontWeight.w500, - ), - ), + _buildProgressBar(), + const SizedBox(height: 20), + _buildQuestionIndicator(), const SizedBox(height: 12), - Text( - question.question, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), + _buildQuestionText(), const SizedBox(height: 30), - - // Jawaban Berdasarkan Tipe Soal - if (question.type == 'option' && question.options != null) - ...List.generate(question.options!.length, (index) { - final option = question.options![index]; - return Container( - margin: const EdgeInsets.only(bottom: 12), - width: double.infinity, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: controller.idxOptionSelected.value == index ? AppColors.primaryBlue : Colors.white, - foregroundColor: controller.idxOptionSelected.value == index ? Colors.white : Colors.black, - side: const BorderSide(color: Colors.grey), - padding: const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - ), - onPressed: () => controller.selectAnswerOption(index), - child: Text(option), - ), - ); - }) - else if (question.type == 'true_false') - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildTrueFalseButton('Ya', true, controller.choosenAnswerTOF), - _buildTrueFalseButton('Tidak', false, controller.choosenAnswerTOF), - ], - ) - else - GlobalTextField(controller: controller.answerTextController), + _buildAnswerSection(), const Spacer(), - Obx(() { - return ElevatedButton( - onPressed: controller.nextQuestion, - style: ElevatedButton.styleFrom( - backgroundColor: controller.isAnswerSelected.value ? const Color(0xFF2563EB) : Colors.grey, - foregroundColor: Colors.white, - minimumSize: const Size(double.infinity, 50), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - ), - child: const Text( - 'Next', - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - ); - }), + _buildNextButton(), ], ); }), @@ -112,9 +46,103 @@ class QuizPlayView extends GetView { ); } + Widget _buildCustomAppBar() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: const BoxDecoration( + color: Colors.transparent, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Text( + 'Kerjakan Soal', + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + ], + ), + ); + } + + Widget _buildProgressBar() { + final question = controller.currentQuestion; + return LinearProgressIndicator( + value: controller.timeLeft.value / question.duration, + minHeight: 8, + backgroundColor: Colors.grey[300], + valueColor: const AlwaysStoppedAnimation(Color(0xFF2563EB)), + ); + } + + Widget _buildQuestionIndicator() { + return Text( + 'Soal ${controller.currentIndex.value + 1} dari ${controller.quizData.questionListings.length}', + style: const TextStyle( + fontSize: 16, + color: Colors.grey, + fontWeight: FontWeight.w500, + ), + ); + } + + Widget _buildQuestionText() { + return Text( + controller.currentQuestion.question, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ); + } + + Widget _buildAnswerSection() { + final question = controller.currentQuestion; + + if (question.type == 'option' && question.options != null) { + return Column( + children: List.generate(question.options!.length, (index) { + final option = question.options![index]; + final isSelected = controller.idxOptionSelected.value == index; + + return Container( + margin: const EdgeInsets.only(bottom: 12), + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: isSelected ? AppColors.primaryBlue : Colors.white, + foregroundColor: isSelected ? Colors.white : Colors.black, + side: const BorderSide(color: Colors.grey), + padding: const EdgeInsets.symmetric(vertical: 14), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + ), + onPressed: () => controller.selectAnswerOption(index), + child: Text(option), + ), + ); + }), + ); + } else if (question.type == 'true_false') { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildTrueFalseButton('Ya', true, controller.choosenAnswerTOF), + _buildTrueFalseButton('Tidak', false, controller.choosenAnswerTOF), + ], + ); + } else { + return GlobalTextField(controller: controller.answerTextController); + } + } + Widget _buildTrueFalseButton(String label, bool value, RxInt choosenAnswer) { return Obx(() { - bool isSelected = (choosenAnswer.value == (value ? 1 : 2)); + final isSelected = (choosenAnswer.value == (value ? 1 : 2)); + return ElevatedButton.icon( style: ElevatedButton.styleFrom( backgroundColor: isSelected ? (value ? Colors.green[100] : Colors.red[100]) : Colors.white, @@ -129,4 +157,24 @@ class QuizPlayView extends GetView { ); }); } + + Widget _buildNextButton() { + return Obx(() { + final isEnabled = controller.isAnswerSelected.value; + + return ElevatedButton( + onPressed: isEnabled ? controller.nextQuestion : null, + style: ElevatedButton.styleFrom( + backgroundColor: isEnabled ? const Color(0xFF2563EB) : Colors.grey, + foregroundColor: Colors.white, + minimumSize: const Size(double.infinity, 50), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + ), + child: const Text( + 'Next', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ); + }); + } }