feat: done final result on the multiplayer quiz

This commit is contained in:
akhdanre 2025-05-19 01:42:15 +07:00
parent 871ec13c31
commit abe21031ec
10 changed files with 521 additions and 281 deletions

View File

@ -1,7 +1,9 @@
import 'package:get/get_navigation/src/routes/get_route.dart'; import 'package:get/get_navigation/src/routes/get_route.dart';
import 'package:quiz_app/app/middleware/auth_middleware.dart'; import 'package:quiz_app/app/middleware/auth_middleware.dart';
import 'package:quiz_app/feature/admin_result_page/bindings/admin_result_binding.dart'; import 'package:quiz_app/feature/admin_result_page/bindings/admin_result_binding.dart';
import 'package:quiz_app/feature/admin_result_page/bindings/detail_participant_result_binding.dart';
import 'package:quiz_app/feature/admin_result_page/view/admin_result_page.dart'; import 'package:quiz_app/feature/admin_result_page/view/admin_result_page.dart';
import 'package:quiz_app/feature/admin_result_page/view/detail_participant_result_page.dart';
import 'package:quiz_app/feature/history/binding/detail_history_binding.dart'; import 'package:quiz_app/feature/history/binding/detail_history_binding.dart';
import 'package:quiz_app/feature/history/binding/history_binding.dart'; import 'package:quiz_app/feature/history/binding/history_binding.dart';
import 'package:quiz_app/feature/history/view/detail_history_view.dart'; import 'package:quiz_app/feature/history/view/detail_history_view.dart';
@ -148,6 +150,11 @@ class AppPages {
name: AppRoutes.monitorResultMPLPage, name: AppRoutes.monitorResultMPLPage,
page: () => AdminResultPage(), page: () => AdminResultPage(),
binding: AdminResultBinding(), binding: AdminResultBinding(),
),
GetPage(
name: AppRoutes.quizMPLResultPage,
page: () => ParticipantDetailPage(),
binding: DetailParticipantResultBinding(),
) )
]; ];
} }

View File

@ -27,4 +27,6 @@ abstract class AppRoutes {
static const monitorResultMPLPage = "/room/quiz/monitor/result"; static const monitorResultMPLPage = "/room/quiz/monitor/result";
static const updateProfilePage = "/profile/update"; static const updateProfilePage = "/profile/update";
static const quizMPLResultPage = "/room/quiz/result";
} }

View File

@ -11,6 +11,7 @@ class APIEndpoint {
static const String quiz = "/quiz"; static const String quiz = "/quiz";
static const String quizGenerate = "/quiz/ai"; static const String quizGenerate = "/quiz/ai";
static const String quizAnswer = "/quiz/answer"; static const String quizAnswer = "/quiz/answer";
static const String quizAnswerSession = "/quiz/answer/session";
static const String userQuiz = "/quiz/user"; static const String userQuiz = "/quiz/user";
static const String quizRecomendation = "/quiz/recomendation"; static const String quizRecomendation = "/quiz/recomendation";

View File

@ -0,0 +1,78 @@
class QuestionAnswer {
final int index;
final String question;
final dynamic targetAnswer;
final int duration;
final String type;
final List<String>? options;
final String answer;
final bool isCorrect;
final double timeSpent;
QuestionAnswer({
required this.index,
required this.question,
required this.targetAnswer,
required this.duration,
required this.type,
required this.options,
required this.answer,
required this.isCorrect,
required this.timeSpent,
});
factory QuestionAnswer.fromJson(Map<String, dynamic> json) {
return QuestionAnswer(
index: json['index'],
question: json['question'],
targetAnswer: json['target_answer'],
duration: json['duration'],
type: json['type'],
options: json['options'] != null ? List<String>.from(json['options']) : null,
answer: json['answer'],
isCorrect: json['is_correct'],
timeSpent: (json['time_spent'] as num).toDouble(),
);
}
}
class ParticipantResult {
final String id;
final String sessionId;
final String quizId;
final String userId;
final String answeredAt;
final List<QuestionAnswer> answers;
final int totalScore;
final int totalCorrect;
ParticipantResult({
required this.id,
required this.sessionId,
required this.quizId,
required this.userId,
required this.answeredAt,
required this.answers,
required this.totalScore,
required this.totalCorrect,
});
factory ParticipantResult.fromJson(Map<String, dynamic> json) {
return ParticipantResult(
id: json['id'],
sessionId: json['session_id'],
quizId: json['quiz_id'],
userId: json['user_id'],
answeredAt: json['answered_at'],
answers: (json['answers'] as List).map((e) => QuestionAnswer.fromJson(e)).toList(),
totalScore: json['total_score'],
totalCorrect: json['total_correct'],
);
}
double get scorePercent => (totalCorrect / answers.length) * 100;
int get totalQuestions => answers.length;
String get name => "User $userId";
}

View File

@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:quiz_app/core/endpoint/api_endpoint.dart'; import 'package:quiz_app/core/endpoint/api_endpoint.dart';
import 'package:quiz_app/core/utils/logger.dart'; import 'package:quiz_app/core/utils/logger.dart';
import 'package:quiz_app/data/models/base/base_model.dart'; import 'package:quiz_app/data/models/base/base_model.dart';
import 'package:quiz_app/data/models/history/participant_history_result.dart';
import 'package:quiz_app/data/providers/dio_client.dart'; import 'package:quiz_app/data/providers/dio_client.dart';
class AnswerService extends GetxService { class AnswerService extends GetxService {
@ -26,4 +27,22 @@ class AnswerService extends GetxService {
return null; return null;
} }
} }
Future<BaseResponseModel<ParticipantResult>?> getAnswerSession(String sessionId, String userId) async {
try {
final response = await _dio.post(APIEndpoint.quizAnswerSession, data: {
"session_id": sessionId,
"user_id": userId,
});
final parsedResponse = BaseResponseModel<ParticipantResult>.fromJson(
response.data,
(data) => ParticipantResult.fromJson(data),
);
return parsedResponse;
} on DioException catch (e) {
logC.e('Gagal mengirim jawaban: ${e.response?.data['message'] ?? e.message}');
return null;
}
}
} }

View File

@ -0,0 +1,11 @@
import 'package:get/get.dart';
import 'package:quiz_app/data/services/answer_service.dart';
import 'package:quiz_app/feature/admin_result_page/controller/detail_participant_result_controller.dart';
class DetailParticipantResultBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => AnswerService());
Get.lazyPut(() => ParticipantResultController(Get.find<AnswerService>()));
}
}

View File

@ -1,4 +1,5 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:quiz_app/app/routes/app_pages.dart';
import 'package:quiz_app/data/models/history/session_history.dart'; import 'package:quiz_app/data/models/history/session_history.dart';
import 'package:quiz_app/data/services/history_service.dart'; import 'package:quiz_app/data/services/history_service.dart';
@ -10,6 +11,8 @@ class AdminResultController extends GetxController {
SessionHistory? sessionHistory; SessionHistory? sessionHistory;
RxBool isLoading = false.obs; RxBool isLoading = false.obs;
String sessionId = "";
@override @override
void onInit() { void onInit() {
loadData(); loadData();
@ -19,7 +22,7 @@ class AdminResultController extends GetxController {
void loadData() async { void loadData() async {
isLoading.value = true; isLoading.value = true;
final sessionId = Get.arguments as String; sessionId = Get.arguments as String;
final result = await _historyService.getSessionHistory(sessionId); final result = await _historyService.getSessionHistory(sessionId);
if (result != null) { if (result != null) {
@ -29,4 +32,9 @@ class AdminResultController extends GetxController {
isLoading.value = false; isLoading.value = false;
} }
void goToDetailParticipants(String userId, String username) => Get.toNamed(
AppRoutes.quizMPLResultPage,
arguments: {"user_id": userId, "session_id": sessionId, "username": username},
);
} }

View File

@ -0,0 +1,47 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:quiz_app/data/models/history/participant_history_result.dart';
import 'package:quiz_app/data/services/answer_service.dart';
class ParticipantResultController extends GetxController {
final AnswerService _answerService;
ParticipantResultController(this._answerService);
final Rx<ParticipantResult?> participantResult = Rx<ParticipantResult?>(null);
final RxBool isLoading = false.obs;
RxString participantName = "".obs;
@override
void onInit() {
loadData();
super.onInit();
}
void loadData() async {
isLoading.value = true;
final args = Get.arguments;
participantName.value = args["username"];
final response = await _answerService.getAnswerSession(args["session_id"], args["user_id"]);
if (response != null) {
participantResult.value = response.data;
}
isLoading.value = false;
}
double calculateScorePercent() {
if (participantResult.value == null) return 0;
return participantResult.value!.scorePercent;
}
int getTotalCorrect() {
return participantResult.value?.totalCorrect ?? 0;
}
int getTotalQuestions() {
return participantResult.value?.totalQuestions ?? 0;
}
}

View File

@ -167,64 +167,67 @@ class AdminResultPage extends GetView<AdminResultController> {
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
side: BorderSide(color: AppColors.accentBlue.withOpacity(0.2)), side: BorderSide(color: AppColors.accentBlue.withOpacity(0.2)),
), ),
child: Padding( child: InkWell(
padding: const EdgeInsets.all(20), onTap: () => controller.goToDetailParticipants(participant.id, participant.name),
child: Row( child: Padding(
children: [ padding: const EdgeInsets.all(20),
Container( child: Row(
width: 36, children: [
height: 36, Container(
decoration: BoxDecoration( width: 36,
shape: BoxShape.circle, height: 36,
color: _getPositionColor(position), decoration: BoxDecoration(
), shape: BoxShape.circle,
child: Center( color: _getPositionColor(position),
child: Text( ),
position.toString(), child: Center(
style: const TextStyle( child: Text(
fontSize: 16, position.toString(),
fontWeight: FontWeight.bold, style: const TextStyle(
color: Colors.white, fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
), ),
), ),
), ),
), const SizedBox(width: 16),
const SizedBox(width: 16), Expanded(
Expanded( child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ Text(participant.name, style: AppTextStyles.subtitle),
Text(participant.name, style: AppTextStyles.subtitle), const SizedBox(height: 4),
const SizedBox(height: 4), Text("Skor: ${participant.score}", style: AppTextStyles.caption),
Text("Skor: ${participant.score}", style: AppTextStyles.caption), ],
],
),
),
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _getScoreColor(scorePercent).withOpacity(0.1),
border: Border.all(
color: _getScoreColor(scorePercent),
width: 2,
), ),
), ),
child: Center( Container(
child: Text( width: 60,
"${participant.score}%", height: 60,
style: TextStyle( decoration: BoxDecoration(
fontSize: 16, shape: BoxShape.circle,
fontWeight: FontWeight.bold, color: _getScoreColor(scorePercent).withOpacity(0.1),
border: Border.all(
color: _getScoreColor(scorePercent), color: _getScoreColor(scorePercent),
width: 2,
),
),
child: Center(
child: Text(
"${participant.score}%",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _getScoreColor(scorePercent),
),
), ),
), ),
), ),
), const SizedBox(width: 12),
const SizedBox(width: 12), const Icon(Icons.chevron_right, color: AppColors.softGrayText, size: 20),
const Icon(Icons.chevron_right, color: AppColors.softGrayText, size: 20), ],
], ),
), ),
), ),
); );

View File

@ -1,238 +1,302 @@
// // Halaman detail untuk peserta import 'package:flutter/material.dart';
// import 'package:flutter/material.dart'; import 'package:get/get.dart';
// import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
// import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart';
// import 'package:quiz_app/app/const/text/text_style.dart'; import 'package:quiz_app/data/models/history/participant_history_result.dart';
// import 'package:quiz_app/feature/admin_result_page/view/admin_result_page.dart'; import 'package:quiz_app/feature/admin_result_page/controller/detail_participant_result_controller.dart';
// class ParticipantDetailPage extends StatelessWidget { class ParticipantDetailPage extends GetView<ParticipantResultController> {
// final ParticipantResult participant; const ParticipantDetailPage({
Key? key,
}) : super(key: key);
// const ParticipantDetailPage({ @override
// Key? key, Widget build(BuildContext context) {
// required this.participant, return Scaffold(
// }) : super(key: key); backgroundColor: AppColors.background,
appBar: AppBar(
title: const Text('Detail Peserta'),
backgroundColor: Colors.white,
foregroundColor: AppColors.darkText,
elevation: 0,
leading: IconButton(
icon: const Icon(LucideIcons.arrowLeft),
onPressed: () => Get.back(),
),
),
body: Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
// @override final participant = controller.participantResult.value;
// Widget build(BuildContext context) { if (participant == null) {
// return Scaffold( return const Center(child: Text('Data peserta tidak tersedia.'));
// backgroundColor: AppColors.background, }
// appBar: AppBar(
// title: Text('Detail ${participant.name}'),
// backgroundColor: Colors.white,
// foregroundColor: AppColors.darkText,
// elevation: 0,
// leading: IconButton(
// icon: const Icon(LucideIcons.arrowLeft),
// onPressed: () => Navigator.pop(context),
// ),
// ),
// body: Column(
// children: [
// _buildParticipantHeader(),
// Expanded(
// child: ListView.builder(
// padding: const EdgeInsets.all(16),
// itemCount: participant.answers.length,
// itemBuilder: (context, index) {
// return _buildAnswerCard(participant.answers[index], index + 1);
// },
// ),
// ),
// ],
// ),
// );
// }
// Widget _buildParticipantHeader() { return Column(
// return Container( children: [
// width: double.infinity, _buildParticipantHeader(participant),
// padding: const EdgeInsets.all(16), Expanded(
// decoration: BoxDecoration( child: ListView.builder(
// color: Colors.white, padding: const EdgeInsets.all(16),
// border: Border( itemCount: participant.answers.length,
// bottom: BorderSide( itemBuilder: (context, index) {
// color: AppColors.borderLight, return _buildAnswerCard(participant.answers[index], index + 1);
// width: 1, },
// ), ),
// ), ),
// ), ],
// child: Row( );
// children: [ }),
// CircleAvatar( );
// radius: 26, }
// backgroundColor: AppColors.accentBlue,
// child: Text(
// participant.name.isNotEmpty ? participant.name[0].toUpperCase() : "?",
// style: TextStyle(
// fontSize: 22,
// fontWeight: FontWeight.bold,
// color: AppColors.primaryBlue,
// ),
// ),
// ),
// const SizedBox(width: 16),
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// participant.name,
// style: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// color: AppColors.darkText,
// ),
// ),
// const SizedBox(height: 4),
// Text(
// "Jumlah Soal: ${participant.totalQuestions}",
// style: TextStyle(
// fontSize: 14,
// color: AppColors.softGrayText,
// ),
// ),
// ],
// ),
// ),
// Container(
// padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
// decoration: BoxDecoration(
// color: _getScoreColor(participant.scorePercent).withValues(alpha:0.1),
// borderRadius: BorderRadius.circular(16),
// border: Border.all(
// color: _getScoreColor(participant.scorePercent),
// ),
// ),
// child: Row(
// children: [
// Icon(
// LucideIcons.percent,
// size: 16,
// color: _getScoreColor(participant.scorePercent),
// ),
// const SizedBox(width: 6),
// Text(
// "${participant.scorePercent.toInt()}%",
// style: TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// color: _getScoreColor(participant.scorePercent),
// ),
// ),
// ],
// ),
// ),
// ],
// ),
// );
// }
// Widget _buildAnswerCard(QuestionAnswer answer, int number) { Widget _buildParticipantHeader(ParticipantResult participant) {
// return Container( return Container(
// width: double.infinity, width: double.infinity,
// margin: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.all(16),
// padding: const EdgeInsets.all(16), decoration: BoxDecoration(
// // decoration: _containerDecoration, color: Colors.white,
// child: Column( border: Border(
// crossAxisAlignment: CrossAxisAlignment.start, bottom: BorderSide(
// children: [ color: AppColors.borderLight,
// Row( width: 1,
// crossAxisAlignment: CrossAxisAlignment.start, ),
// children: [ ),
// Container( ),
// width: 28, child: Row(
// height: 28, children: [
// decoration: BoxDecoration( CircleAvatar(
// shape: BoxShape.circle, radius: 26,
// color: answer.isCorrect ? AppColors.primaryBlue.withValues(alpha:0.1) : AppColors.scorePoor.withValues(alpha:0.1), backgroundColor: AppColors.accentBlue,
// ), child: Text(
// child: Center( controller.participantName.value[0].toUpperCase(),
// child: Text( style: const TextStyle(
// number.toString(), fontSize: 22,
// style: TextStyle( fontWeight: FontWeight.bold,
// fontSize: 13, color: AppColors.primaryBlue,
// fontWeight: FontWeight.bold, ),
// color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor, ),
// ), ),
// ), const SizedBox(width: 16),
// ), Expanded(
// ), child: Column(
// const SizedBox(width: 12), crossAxisAlignment: CrossAxisAlignment.start,
// Expanded( children: [
// child: Text( Text(
// 'Soal $number: ${answer.question}', controller.participantName.value,
// style: const TextStyle( style: const TextStyle(
// fontSize: 16, fontSize: 16,
// fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
// color: AppColors.darkText, color: AppColors.darkText,
// ), ),
// ), ),
// ), const SizedBox(height: 4),
// Icon( Text(
// answer.isCorrect ? LucideIcons.checkCircle : LucideIcons.xCircle, "Jumlah Soal: ${participant.totalQuestions}",
// color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor, style: const TextStyle(
// size: 20, fontSize: 14,
// ), color: AppColors.softGrayText,
// ], ),
// ), ),
// const SizedBox(height: 12), ],
// Divider(color: AppColors.borderLight), ),
// const SizedBox(height: 12), ),
// _buildAnswerRow( Container(
// label: "Jawaban Siswa:", padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
// answer: answer.userAnswer, decoration: BoxDecoration(
// isCorrect: answer.isCorrect, color: _getScoreColor(participant.scorePercent).withOpacity(0.1),
// ), borderRadius: BorderRadius.circular(16),
// if (!answer.isCorrect) ...[ border: Border.all(
// const SizedBox(height: 10), color: _getScoreColor(participant.scorePercent),
// _buildAnswerRow( ),
// label: "Jawaban Benar:", ),
// answer: answer.correctAnswer, child: Row(
// isCorrect: true, children: [
// ), Icon(
// ], LucideIcons.percent,
// ], size: 16,
// ), color: _getScoreColor(participant.scorePercent),
// ); ),
// } const SizedBox(width: 6),
Text(
"${participant.scorePercent.toInt()}%",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: _getScoreColor(participant.scorePercent),
),
),
],
),
),
],
),
);
}
// Widget _buildAnswerRow({ Widget _buildAnswerCard(QuestionAnswer answer, int number) {
// required String label, return Container(
// required String answer, width: double.infinity,
// required bool isCorrect, margin: const EdgeInsets.only(bottom: 20),
// }) { padding: const EdgeInsets.all(16),
// return Row( decoration: BoxDecoration(
// crossAxisAlignment: CrossAxisAlignment.start, color: Colors.white,
// children: [ borderRadius: BorderRadius.circular(12),
// SizedBox( boxShadow: [
// width: 110, BoxShadow(
// child: Text( color: Colors.grey.withOpacity(0.1),
// label, blurRadius: 6,
// style: TextStyle( offset: const Offset(0, 3),
// fontSize: 14, ),
// fontWeight: FontWeight.w500, ],
// color: AppColors.softGrayText, ),
// ), child: Column(
// ), crossAxisAlignment: CrossAxisAlignment.start,
// ), children: [
// Expanded( Row(
// child: Text( crossAxisAlignment: CrossAxisAlignment.start,
// answer, children: [
// style: TextStyle( Container(
// fontSize: 15, width: 28,
// fontWeight: FontWeight.w600, height: 28,
// color: isCorrect ? AppColors.primaryBlue : AppColors.scorePoor, decoration: BoxDecoration(
// ), shape: BoxShape.circle,
// ), color: answer.isCorrect ? AppColors.primaryBlue.withOpacity(0.1) : AppColors.scorePoor.withOpacity(0.1),
// ), ),
// ], child: Center(
// ); child: Text(
// } number.toString(),
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.bold,
color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Text(
'Soal $number: ${answer.question}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: AppColors.darkText,
),
),
),
Icon(
answer.isCorrect ? LucideIcons.checkCircle : LucideIcons.xCircle,
color: answer.isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
size: 20,
),
],
),
const SizedBox(height: 12),
Divider(color: AppColors.borderLight),
const SizedBox(height: 12),
_buildAnswerRow(
label: "Tipe Soal:",
answer: answer.type,
isCorrect: true,
),
_buildAnswerRow(
label: "Waktu Diberikan:",
answer: "${answer.duration} detik",
isCorrect: true,
),
_buildAnswerRow(
label: "Waktu Dihabiskan:",
answer: "${answer.timeSpent} detik",
isCorrect: true,
),
_buildAnswerRow(
label: "Jawaban Siswa:",
answer: answer.answer,
isCorrect: answer.isCorrect,
),
if (!answer.isCorrect) ...[
const SizedBox(height: 10),
_buildAnswerRow(
label: "Jawaban Benar:",
answer: answer.targetAnswer.toString(),
isCorrect: true,
),
],
if (answer.options != null) ...[
const SizedBox(height: 10),
_buildOptions(answer.options!),
],
],
),
);
}
// Color _getScoreColor(double score) { Widget _buildOptions(List<String> options) {
// if (score >= 70) return AppColors.scoreGood; return Column(
// if (score >= 60) return AppColors.scoreAverage; crossAxisAlignment: CrossAxisAlignment.start,
// return AppColors.scorePoor; children: [
// } const Text(
// } "Pilihan Jawaban:",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.softGrayText,
),
),
const SizedBox(height: 6),
...options.map((opt) => Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Text(
"- $opt",
style: const TextStyle(
fontSize: 15,
color: AppColors.darkText,
),
),
)),
],
);
}
Widget _buildAnswerRow({
required String label,
required String answer,
required bool isCorrect,
}) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 110,
child: Text(
label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.softGrayText,
),
),
),
Expanded(
child: Text(
answer,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: isCorrect ? AppColors.primaryBlue : AppColors.scorePoor,
),
),
),
],
);
}
Color _getScoreColor(double score) {
if (score >= 70) return AppColors.scoreGood;
if (score >= 60) return AppColors.scoreAverage;
return AppColors.scorePoor;
}
}