diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index bb57760..18eb3b1 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -1,6 +1,8 @@ import 'package:get/get_navigation/src/routes/get_route.dart'; import 'package:quiz_app/app/middleware/auth_middleware.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/view/detail_history_view.dart'; import 'package:quiz_app/feature/home/binding/home_binding.dart'; import 'package:quiz_app/feature/home/view/home_page.dart'; import 'package:quiz_app/feature/detail_quiz/binding/detail_quiz_binding.dart'; @@ -92,6 +94,11 @@ class AppPages { name: AppRoutes.listingQuizPage, page: () => ListingsQuizView(), binding: ListingQuizBinding(), + ), + GetPage( + name: AppRoutes.detailHistoryPage, + page: () => DetailHistoryView(), + binding: DetailHistoryBinding(), ) ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index 12ae8a0..24a779d 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -15,4 +15,6 @@ abstract class AppRoutes { static const playQuizPage = "/quiz/play"; static const resultQuizPage = "/quiz/result"; + + static const detailHistoryPage = "/history/detail"; } diff --git a/lib/component/global_button.dart b/lib/component/global_button.dart index 0f03d20..eb54c81 100644 --- a/lib/component/global_button.dart +++ b/lib/component/global_button.dart @@ -45,7 +45,7 @@ class GlobalButton extends StatelessWidget { backgroundColor: backgroundColor, foregroundColor: foregroundColor, elevation: isDisabled ? 0 : 4, - shadowColor: !isDisabled ? backgroundColor.withOpacity(0.3) : Colors.transparent, + shadowColor: !isDisabled ? backgroundColor.withValues(alpha: 0.3) : Colors.transparent, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), diff --git a/lib/component/quiz_container_component.dart b/lib/component/quiz_container_component.dart index 97bd6f2..4e8c644 100644 --- a/lib/component/quiz_container_component.dart +++ b/lib/component/quiz_container_component.dart @@ -24,7 +24,7 @@ class QuizContainerComponent extends StatelessWidget { border: Border.all(color: const Color(0xFFE1E4E8)), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.03), + color: Colors.black.withValues(alpha: 0.03), blurRadius: 8, offset: const Offset(0, 2), ), diff --git a/lib/component/widget/question_container_widget.dart b/lib/component/widget/question_container_widget.dart index c53ee95..448652b 100644 --- a/lib/component/widget/question_container_widget.dart +++ b/lib/component/widget/question_container_widget.dart @@ -97,7 +97,7 @@ class QuestionContainerWidget extends StatelessWidget { 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, + color: isCorrect ? AppColors.primaryBlue.withValues(alpha: 0.1) : Colors.transparent, borderRadius: BorderRadius.circular(8), ), child: Row( @@ -236,7 +236,7 @@ class QuestionContainerWidget extends StatelessWidget { border: Border.all(color: AppColors.borderLight), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: Colors.black.withValues(alpha: 0.05), blurRadius: 6, offset: const Offset(2, 2), ), diff --git a/lib/core/endpoint/api_endpoint.dart b/lib/core/endpoint/api_endpoint.dart index e1c5aec..b76756a 100644 --- a/lib/core/endpoint/api_endpoint.dart +++ b/lib/core/endpoint/api_endpoint.dart @@ -12,4 +12,5 @@ class APIEndpoint { static const String quizSearch = "/quiz/search"; static const String historyQuiz = "/history"; + static const String detailHistoryQuiz = "/history/detail"; } diff --git a/lib/data/models/history/detail_quiz_history.dart b/lib/data/models/history/detail_quiz_history.dart new file mode 100644 index 0000000..226fb46 --- /dev/null +++ b/lib/data/models/history/detail_quiz_history.dart @@ -0,0 +1,75 @@ +class QuizAnswerResult { + final String answerId; + final String quizId; + final String title; + final String description; + final String authorId; + final String answeredAt; + final int totalCorrect; + final int totalScore; + final double totalSolveTime; + final List questionListings; + + QuizAnswerResult({ + required this.answerId, + required this.quizId, + required this.title, + required this.description, + required this.authorId, + required this.answeredAt, + required this.totalCorrect, + required this.totalScore, + required this.totalSolveTime, + required this.questionListings, + }); + + factory QuizAnswerResult.fromJson(Map json) { + return QuizAnswerResult( + answerId: json['answer_id'], + quizId: json['quiz_id'], + title: json['title'], + description: json['description'], + authorId: json['author_id'], + answeredAt: json['answered_at'], + totalCorrect: json['total_correct'], + totalScore: json['total_score'], + totalSolveTime: (json['total_solve_time'] as num).toDouble(), + questionListings: (json['question_listings'] as List).map((e) => QuestionAnswerItem.fromJson(e)).toList(), + ); + } +} + +class QuestionAnswerItem { + final int index; + final String question; + final String type; + final dynamic targetAnswer; + final dynamic userAnswer; + final bool isCorrect; + final double timeSpent; + final List? options; + + QuestionAnswerItem({ + required this.index, + required this.question, + required this.type, + required this.targetAnswer, + required this.userAnswer, + required this.isCorrect, + required this.timeSpent, + this.options, + }); + + factory QuestionAnswerItem.fromJson(Map json) { + return QuestionAnswerItem( + index: json['index'], + question: json['question'], + type: json['type'], + targetAnswer: json['target_answer'], + userAnswer: json['user_answer'], + isCorrect: json['is_correct'], + timeSpent: (json['time_spent'] as num).toDouble(), + options: json['options'] != null ? List.from(json['options']) : null, + ); + } +} diff --git a/lib/data/models/quiz/question/fill_in_the_blank_question_model.dart b/lib/data/models/quiz/question/fill_in_the_blank_question_model.dart index 63aa35c..b917875 100644 --- a/lib/data/models/quiz/question/fill_in_the_blank_question_model.dart +++ b/lib/data/models/quiz/question/fill_in_the_blank_question_model.dart @@ -4,11 +4,11 @@ class FillInTheBlankQuestion extends BaseQuestionModel { final String targetAnswer; FillInTheBlankQuestion({ - required int index, - required String question, - required int duration, + required super.index, + required super.question, + required super.duration, required this.targetAnswer, - }) : super(index: index, question: question, duration: duration, type: 'fill_the_blank'); + }) : super(type: 'fill_the_blank'); factory FillInTheBlankQuestion.fromJson(Map json) { return FillInTheBlankQuestion( diff --git a/lib/data/models/quiz/question/option_question_model.dart b/lib/data/models/quiz/question/option_question_model.dart index e04355a..9b5ce6f 100644 --- a/lib/data/models/quiz/question/option_question_model.dart +++ b/lib/data/models/quiz/question/option_question_model.dart @@ -5,12 +5,12 @@ class OptionQuestion extends BaseQuestionModel { final List options; OptionQuestion({ - required int index, - required String question, - required int duration, + required super.index, + required super.question, + required super.duration, required this.targetAnswer, required this.options, - }) : super(index: index, question: question, duration: duration, type: 'option'); + }) : super(type: 'option'); factory OptionQuestion.fromJson(Map json) { return OptionQuestion( diff --git a/lib/data/models/quiz/question/true_false_question_model.dart b/lib/data/models/quiz/question/true_false_question_model.dart index e4f0768..0a0717e 100644 --- a/lib/data/models/quiz/question/true_false_question_model.dart +++ b/lib/data/models/quiz/question/true_false_question_model.dart @@ -4,11 +4,11 @@ class TrueFalseQuestion extends BaseQuestionModel { final bool targetAnswer; TrueFalseQuestion({ - required int index, - required String question, - required int duration, + required super.index, + required super.question, + required super.duration, required this.targetAnswer, - }) : super(index: index, question: question, duration: duration, type: 'true_false'); + }) : super(type: 'true_false'); factory TrueFalseQuestion.fromJson(Map json) { return TrueFalseQuestion( diff --git a/lib/data/services/history_service.dart b/lib/data/services/history_service.dart index fa200f7..ef91028 100644 --- a/lib/data/services/history_service.dart +++ b/lib/data/services/history_service.dart @@ -3,6 +3,7 @@ import 'package:get/get.dart'; import 'package:quiz_app/core/endpoint/api_endpoint.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/history/detail_quiz_history.dart'; import 'package:quiz_app/data/models/history/quiz_history.dart'; import 'package:quiz_app/data/providers/dio_client.dart'; @@ -29,4 +30,19 @@ class HistoryService extends GetxService { return null; } } + + Future?> getDetailHistory(String answerId) async { + try { + final result = await _dio.get("${APIEndpoint.detailHistoryQuiz}/$answerId"); + + final parsedResponse = BaseResponseModel.fromJson( + result.data, + (data) => QuizAnswerResult.fromJson(data as Map), + ); + return parsedResponse; + } catch (e, stacktrace) { + logC.e(e, stackTrace: stacktrace); + return null; + } + } } diff --git a/lib/feature/detail_quiz/view/detail_quix_view.dart b/lib/feature/detail_quiz/view/detail_quix_view.dart index 6836916..d2e3afd 100644 --- a/lib/feature/detail_quiz/view/detail_quix_view.dart +++ b/lib/feature/detail_quiz/view/detail_quix_view.dart @@ -111,7 +111,7 @@ class DetailQuizView extends GetView { border: Border.all(color: AppColors.borderLight), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: Colors.black.withValues(alpha: 0.05), blurRadius: 6, offset: const Offset(2, 2), ), diff --git a/lib/feature/history/binding/detail_history_binding.dart b/lib/feature/history/binding/detail_history_binding.dart new file mode 100644 index 0000000..5478a70 --- /dev/null +++ b/lib/feature/history/binding/detail_history_binding.dart @@ -0,0 +1,11 @@ +import 'package:get/get.dart'; +import 'package:quiz_app/data/services/history_service.dart'; +import 'package:quiz_app/feature/history/controller/detail_history_controller.dart'; + +class DetailHistoryBinding extends Bindings { + @override + void dependencies() { + if (!Get.isRegistered()) Get.lazyPut(() => HistoryService()); + Get.lazyPut(() => DetailHistoryController(Get.find())); + } +} diff --git a/lib/feature/history/controller/detail_history_controller.dart b/lib/feature/history/controller/detail_history_controller.dart new file mode 100644 index 0000000..122d554 --- /dev/null +++ b/lib/feature/history/controller/detail_history_controller.dart @@ -0,0 +1,30 @@ +import 'package:get/get.dart'; +import 'package:quiz_app/data/models/base/base_model.dart'; +import 'package:quiz_app/data/models/history/detail_quiz_history.dart'; +import 'package:quiz_app/data/services/history_service.dart'; + +class DetailHistoryController extends GetxController { + final HistoryService _historyService; + + DetailHistoryController(this._historyService); + + late QuizAnswerResult quizAnswer; + + RxBool isLoading = true.obs; + + @override + void onInit() { + _loadData(); + super.onInit(); + } + + void _loadData() async { + String answerId = Get.arguments as String; + BaseResponseModel? result = await _historyService.getDetailHistory(answerId); + if (result != null) { + if (result.data != null) quizAnswer = result.data!; + } + + isLoading.value = false; + } +} diff --git a/lib/feature/history/controller/history_controller.dart b/lib/feature/history/controller/history_controller.dart index 786e262..9801849 100644 --- a/lib/feature/history/controller/history_controller.dart +++ b/lib/feature/history/controller/history_controller.dart @@ -1,11 +1,12 @@ import 'package:get/get.dart'; +import 'package:quiz_app/app/routes/app_pages.dart'; import 'package:quiz_app/data/controllers/user_controller.dart'; import 'package:quiz_app/data/models/history/quiz_history.dart'; import 'package:quiz_app/data/services/history_service.dart'; class HistoryController extends GetxController { - HistoryService _historyService; - UserController _userController; + final HistoryService _historyService; + final UserController _userController; HistoryController(this._historyService, this._userController); @@ -23,4 +24,6 @@ class HistoryController extends GetxController { historyList.value = await _historyService.getHistory(_userController.userData!.id) ?? []; isLoading.value = false; } + + void goToDetailHistory(String answerId) => Get.toNamed(AppRoutes.detailHistoryPage, arguments: answerId); } diff --git a/lib/feature/history/view/component/quiz_item_component.dart b/lib/feature/history/view/component/quiz_item_component.dart new file mode 100644 index 0000000..39cae80 --- /dev/null +++ b/lib/feature/history/view/component/quiz_item_component.dart @@ -0,0 +1,160 @@ +import 'package:flutter/material.dart'; +import 'package:lucide_icons/lucide_icons.dart'; +import 'package:quiz_app/app/const/colors/app_colors.dart'; +import 'package:quiz_app/data/models/history/detail_quiz_history.dart'; + +class QuizItemComponent extends StatelessWidget { + final QuestionAnswerItem item; + + const QuizItemComponent({super.key, required this.item}); + + @override + Widget build(BuildContext context) { + final bool isOptionType = item.type == 'option'; + + return Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildQuestionText(), + const SizedBox(height: 16), + if (isOptionType && item.options != null) _buildOptions(), + const SizedBox(height: 12), + _buildAnswerIndicator(), + const SizedBox(height: 16), + const Divider(height: 24), + _buildMetadata(), + ], + ), + ); + } + + Widget _buildQuestionText() { + return Text( + '${item.index}. ${item.question}', + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + ), + ); + } + + Widget _buildOptions() { + return Column( + children: item.options!.asMap().entries.map((entry) { + final int index = entry.key; + final String text = entry.value; + + final isCorrect = index == item.targetAnswer; + final isWrong = index == item.userAnswer && !isCorrect; + + final Color? backgroundColor = isCorrect + ? AppColors.primaryBlue.withValues(alpha: 0.15) + : isWrong + ? Colors.red.withValues(alpha: 0.15) + : null; + + return Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 6), + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12), + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.grey.shade300), + ), + child: Row( + children: [ + Icon( + LucideIcons.circle, + size: 16, + color: Colors.grey.shade600, + ), + const SizedBox(width: 8), + Flexible( + child: Text( + text, + style: const TextStyle(fontSize: 14), + ), + ), + ], + ), + ); + }).toList(), + ); + } + + Widget _buildAnswerIndicator() { + final correctIcon = item.isCorrect ? LucideIcons.checkCircle2 : LucideIcons.xCircle; + final correctColor = item.isCorrect ? AppColors.primaryBlue : Colors.red; + + final String userAnswerText = item.type == 'option' ? item.options![item.userAnswer] : item.userAnswer.toString(); + + final String correctAnswerText = item.targetAnswer.toString(); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(correctIcon, color: correctColor, size: 18), + const SizedBox(width: 8), + Text( + 'Jawabanmu: $userAnswerText', + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), + ), + ], + ), + if (item.type != 'option' && !item.isCorrect) ...[ + const SizedBox(height: 6), + Row( + children: [ + const SizedBox(width: 26), // offset icon size + spacing + Text( + 'Jawaban benar: $correctAnswerText', + style: const TextStyle(fontSize: 14, color: Colors.black54), + ), + ], + ), + ], + ], + ); + } + + Widget _buildMetadata() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _metaItem(icon: LucideIcons.helpCircle, label: item.type), + _metaItem(icon: LucideIcons.clock3, label: '${item.timeSpent}s'), + ], + ); + } + + Widget _metaItem({required IconData icon, required String label}) { + return Row( + children: [ + Icon(icon, size: 16, color: AppColors.primaryBlue), + const SizedBox(width: 6), + Text( + label, + style: const TextStyle(fontSize: 13, color: Colors.black54), + ), + ], + ); + } +} diff --git a/lib/feature/history/view/detail_history_view.dart b/lib/feature/history/view/detail_history_view.dart new file mode 100644 index 0000000..7e3f7db --- /dev/null +++ b/lib/feature/history/view/detail_history_view.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:get/get_state_manager/get_state_manager.dart'; +import 'package:lucide_icons/lucide_icons.dart'; +import 'package:quiz_app/app/const/colors/app_colors.dart'; +import 'package:quiz_app/component/widget/loading_widget.dart'; +import 'package:quiz_app/feature/history/controller/detail_history_controller.dart'; +import 'package:quiz_app/feature/history/view/component/quiz_item_component.dart'; + +class DetailHistoryView extends GetView { + const DetailHistoryView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: AppBar( + backgroundColor: AppColors.background, + elevation: 0, + title: const Text( + 'Detail history', + style: TextStyle( + color: AppColors.darkText, + fontWeight: FontWeight.bold, + ), + ), + centerTitle: true, + iconTheme: const IconThemeData(color: AppColors.darkText), + ), + body: SafeArea( + child: Obx(() { + if (controller.isLoading.value) { + return Expanded( + child: Center( + child: LoadingWidget(), + ), + ); + } + return ListView( + children: [ + quizMetaInfo(), + ...quizListings(), + ], + ); + }), + ), + ); + } + + List quizListings() { + return controller.quizAnswer.questionListings.map((e) => QuizItemComponent(item: e)).toList(); + } + + Widget quizMetaInfo() { + final quiz = controller.quizAnswer; + + return Container( + width: double.infinity, + margin: const EdgeInsets.all(16), + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 8, + offset: const Offset(0, 4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + quiz.title, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + Text( + quiz.description, + style: const TextStyle(fontSize: 14, color: Colors.black54), + ), + const SizedBox(height: 12), + Row( + children: [ + const Icon(LucideIcons.calendar, size: 16, color: Colors.black45), + const SizedBox(width: 6), + Text( + quiz.answeredAt, + style: const TextStyle(fontSize: 13, color: Colors.black54), + ), + ], + ), + const SizedBox(height: 6), + Row( + children: [ + const Icon(LucideIcons.clock, size: 16, color: Colors.black45), + const SizedBox(width: 6), + Text( + '12:00', // tanggal dan jam dipisahkan titik tengah + style: const TextStyle(fontSize: 13, color: Colors.black54), + ), + ], + ), + const SizedBox(height: 6), + const Divider( + height: 24, + thickness: 1, + color: AppColors.shadowPrimary, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildStatItem( + icon: LucideIcons.checkCircle2, + label: 'Benar', + value: "${quiz.totalCorrect}/${quiz.questionListings.length}", + color: Colors.green, + ), + _buildStatItem( + icon: LucideIcons.award, + label: 'Skor', + value: quiz.totalScore.toString(), + color: Colors.blueAccent, + ), + _buildStatItem( + icon: LucideIcons.clock3, + label: 'Waktu', + value: '${quiz.totalSolveTime}s', + color: Colors.orange, + ), + ], + ), + ], + ), + ); + } + + Widget _buildStatItem({ + required IconData icon, + required String label, + required String value, + required Color color, + }) { + return Column( + children: [ + Icon(icon, color: color, size: 28), + const SizedBox(height: 4), + Text( + value, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + Text( + label, + style: const TextStyle(fontSize: 12, color: Colors.black54), + ), + ], + ); + } +} diff --git a/lib/feature/history/view/history_view.dart b/lib/feature/history/view/history_view.dart index e8ab89f..c7b7f77 100644 --- a/lib/feature/history/view/history_view.dart +++ b/lib/feature/history/view/history_view.dart @@ -67,67 +67,70 @@ class HistoryView extends GetView { } Widget _buildHistoryCard(QuizHistory item) { - return Container( - margin: const EdgeInsets.only(bottom: 16), - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 4, - offset: Offset(0, 2), - ) - ], - ), - child: Row( - children: [ - Container( - width: 48, - height: 48, - decoration: BoxDecoration( - color: Colors.blue.shade100, - shape: BoxShape.circle, + return GestureDetector( + onTap: () => controller.goToDetailHistory(item.answerId), + child: Container( + margin: const EdgeInsets.only(bottom: 16), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: const [ + BoxShadow( + color: Colors.black12, + blurRadius: 4, + offset: Offset(0, 2), + ) + ], + ), + child: Row( + children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: Colors.blue.shade100, + shape: BoxShape.circle, + ), + child: const Icon(Icons.history, color: Colors.blue), ), - child: const Icon(Icons.history, color: Colors.blue), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - item.title, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), - ), - const SizedBox(height: 4), - Text( - item.date, - style: const TextStyle(fontSize: 12, color: Colors.grey), - ), - const SizedBox(height: 8), - Row( - children: [ - const Icon(Icons.check_circle, size: 14, color: Colors.green), - const SizedBox(width: 4), - Text( - "Skor: ${item.totalCorrect}/${item.totalQuestion}", - style: const TextStyle(fontSize: 12), - ), - const SizedBox(width: 16), - const Icon(Icons.timer, size: 14, color: Colors.grey), - const SizedBox(width: 4), - Text( - "3 menit", - style: const TextStyle(fontSize: 12), - ), - ], - ), - ], - ), - ) - ], + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.title, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600), + ), + const SizedBox(height: 4), + Text( + item.date, + style: const TextStyle(fontSize: 12, color: Colors.grey), + ), + const SizedBox(height: 8), + Row( + children: [ + const Icon(Icons.check_circle, size: 14, color: Colors.green), + const SizedBox(width: 4), + Text( + "Skor: ${item.totalCorrect}/${item.totalQuestion}", + style: const TextStyle(fontSize: 12), + ), + const SizedBox(width: 16), + const Icon(Icons.timer, size: 14, color: Colors.grey), + const SizedBox(width: 4), + Text( + "3 menit", + style: const TextStyle(fontSize: 12), + ), + ], + ), + ], + ), + ) + ], + ), ), ); } diff --git a/lib/feature/home/view/component/button_option.dart b/lib/feature/home/view/component/button_option.dart index fe1647c..f42006c 100644 --- a/lib/feature/home/view/component/button_option.dart +++ b/lib/feature/home/view/component/button_option.dart @@ -88,7 +88,7 @@ class ButtonOption extends StatelessWidget { borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: gradientColors.last.withOpacity(0.4), + color: gradientColors.last.withValues(alpha: 0.4), blurRadius: 6, offset: const Offset(2, 4), ), diff --git a/lib/feature/home/view/component/search_component.dart b/lib/feature/home/view/component/search_component.dart index 7409fa2..512b314 100644 --- a/lib/feature/home/view/component/search_component.dart +++ b/lib/feature/home/view/component/search_component.dart @@ -86,7 +86,7 @@ class SearchComponent extends StatelessWidget { borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: Colors.black.withValues(alpha: 0.05), blurRadius: 6, offset: Offset(0, 2), ), diff --git a/lib/feature/library/view/library_view.dart b/lib/feature/library/view/library_view.dart index 916173f..74dc05a 100644 --- a/lib/feature/library/view/library_view.dart +++ b/lib/feature/library/view/library_view.dart @@ -74,7 +74,7 @@ class LibraryView extends GetView { borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: Colors.black.withValues(alpha: 0.05), blurRadius: 6, offset: const Offset(0, 2), ), diff --git a/lib/feature/profile/controller/profile_controller.dart b/lib/feature/profile/controller/profile_controller.dart index a52dffe..95a4354 100644 --- a/lib/feature/profile/controller/profile_controller.dart +++ b/lib/feature/profile/controller/profile_controller.dart @@ -1,4 +1,5 @@ import 'package:get/get.dart'; +import 'package:quiz_app/core/utils/logger.dart'; import 'package:quiz_app/data/controllers/user_controller.dart'; class ProfileController extends GetxController { @@ -12,10 +13,10 @@ class ProfileController extends GetxController { final avgScore = 85.obs; void logout() { - print("Logout pressed"); + logC.i("Logout pressed"); } void editProfile() { - print("Edit profile pressed"); + logC.i("Edit profile pressed"); } } diff --git a/lib/feature/quiz_play/view/quiz_play_view.dart b/lib/feature/quiz_play/view/quiz_play_view.dart index e06e727..1006f80 100644 --- a/lib/feature/quiz_play/view/quiz_play_view.dart +++ b/lib/feature/quiz_play/view/quiz_play_view.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:quiz_app/app/const/colors/app_colors.dart'; -import 'package:quiz_app/component/global_text_field.dart'; import 'package:quiz_app/feature/quiz_play/controller/quiz_play_controller.dart'; class QuizPlayView extends GetView { diff --git a/pubspec.lock b/pubspec.lock index 299040d..92b35f5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -232,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.0" + lucide_icons: + dependency: "direct main" + description: + name: lucide_icons + sha256: ad24d0fd65707e48add30bebada7d90bff2a1bba0a72d6e9b19d44246b0e83c4 + url: "https://pub.dev" + source: hosted + version: "0.257.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c731b77..757e6c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: flutter_dotenv: ^5.2.1 dio: ^5.8.0+1 shared_preferences: ^2.5.3 + lucide_icons: ^0.257.0 dev_dependencies: flutter_test: