diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5706166 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "quiz_app", + "request": "launch", + "type": "dart" + }, + // { + // "name": "quiz_app (profile mode)", + // "request": "launch", + // "type": "dart", + // "flutterMode": "profile" + // }, + // { + // "name": "quiz_app (release mode)", + // "request": "launch", + // "type": "dart", + // "flutterMode": "release" + // } + ] +} \ No newline at end of file diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index c9f278f..7d3ada7 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -3,9 +3,9 @@ import 'package:quiz_app/app/middleware/auth_middleware.dart'; import 'package:quiz_app/feature/history/binding/history_binding.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/library/binding/detail_quiz_binding.dart'; +import 'package:quiz_app/feature/detail_quiz/binding/detail_quiz_binding.dart'; import 'package:quiz_app/feature/library/binding/library_binding.dart'; -import 'package:quiz_app/feature/library/view/detail_quix_view.dart'; +import 'package:quiz_app/feature/detail_quiz/view/detail_quix_view.dart'; import 'package:quiz_app/feature/login/bindings/login_binding.dart'; import 'package:quiz_app/feature/login/view/login_page.dart'; import 'package:quiz_app/feature/navigation/bindings/navigation_binding.dart'; diff --git a/lib/component/quiz_container_component.dart b/lib/component/quiz_container_component.dart index 2e09756..137ad6c 100644 --- a/lib/component/quiz_container_component.dart +++ b/lib/component/quiz_container_component.dart @@ -4,82 +4,86 @@ import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; class QuizContainerComponent extends StatelessWidget { final QuizListingModel data; - const QuizContainerComponent({required this.data, super.key}); + final Function(String) onTap; + const QuizContainerComponent({required this.data, required this.onTap, super.key}); @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: AppColors.background, - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: Color(0xFFE1E4E8), - ), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.03), - blurRadius: 8, - offset: Offset(0, 2), - ) - ], - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: Color(0xFF0052CC), - borderRadius: BorderRadius.circular(8), - ), - child: const Icon(Icons.school, color: Colors.white, size: 28), + return GestureDetector( + onTap: () => onTap(data.quizId), + child: Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.background, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Color(0xFFE1E4E8), ), - const SizedBox(width: 12), - // Quiz Info - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - data.title, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Color(0xFF172B4D), - ), - ), - const SizedBox(height: 4), - Text( - "created by ${data.authorName}", - style: TextStyle( - fontSize: 12, - color: Color(0xFF6B778C), - ), - ), - const SizedBox(height: 8), - Row( - children: const [ - Icon(Icons.format_list_bulleted, size: 14, color: Color(0xFF6B778C)), - SizedBox(width: 4), - Text( - "50 Quizzes", - style: TextStyle(fontSize: 12, color: Color(0xFF6B778C)), - ), - SizedBox(width: 12), - Icon(Icons.access_time, size: 14, color: Color(0xFF6B778C)), - SizedBox(width: 4), - Text( - "1 hr duration", - style: TextStyle(fontSize: 12, color: Color(0xFF6B778C)), - ), - ], - ) - ], + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.03), + blurRadius: 8, + offset: Offset(0, 2), + ) + ], + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: Color(0xFF0052CC), + borderRadius: BorderRadius.circular(8), + ), + child: const Icon(Icons.school, color: Colors.white, size: 28), ), - ) - ], + const SizedBox(width: 12), + // Quiz Info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + data.title, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Color(0xFF172B4D), + ), + ), + const SizedBox(height: 4), + Text( + "created by ${data.authorName}", + style: TextStyle( + fontSize: 12, + color: Color(0xFF6B778C), + ), + ), + const SizedBox(height: 8), + Row( + children: const [ + Icon(Icons.format_list_bulleted, size: 14, color: Color(0xFF6B778C)), + SizedBox(width: 4), + Text( + "50 Quizzes", + style: TextStyle(fontSize: 12, color: Color(0xFF6B778C)), + ), + SizedBox(width: 12), + Icon(Icons.access_time, size: 14, color: Color(0xFF6B778C)), + SizedBox(width: 4), + Text( + "1 hr duration", + style: TextStyle(fontSize: 12, color: Color(0xFF6B778C)), + ), + ], + ) + ], + ), + ) + ], + ), ), ); } diff --git a/lib/component/widget/loading_widget.dart b/lib/component/widget/loading_widget.dart new file mode 100644 index 0000000..35b24c8 --- /dev/null +++ b/lib/component/widget/loading_widget.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class LoadingWidget extends StatelessWidget { + const LoadingWidget({super.key}); + + @override + Widget build(BuildContext context) { + return const Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator(), + SizedBox(height: 12), + Text( + "Memuat data...", + style: TextStyle(color: Colors.grey, fontSize: 14), + ), + ], + ), + ); + } +} diff --git a/lib/component/widget/recomendation_component.dart b/lib/component/widget/recomendation_component.dart index 3cfb4a1..2cd43af 100644 --- a/lib/component/widget/recomendation_component.dart +++ b/lib/component/widget/recomendation_component.dart @@ -5,9 +5,11 @@ import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; class RecomendationComponent extends StatelessWidget { final String title; final List datas; + final Function(String) itemOnTap; const RecomendationComponent({ required this.title, required this.datas, + required this.itemOnTap, super.key, }); @@ -24,7 +26,10 @@ class RecomendationComponent extends StatelessWidget { shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: datas.length, - itemBuilder: (context, index) => QuizContainerComponent(data: datas[index]), + itemBuilder: (context, index) => QuizContainerComponent( + data: datas[index], + onTap: itemOnTap, + ), ) : SizedBox.shrink() ], diff --git a/lib/data/services/quiz_service.dart b/lib/data/services/quiz_service.dart index 8a46eaa..94a1948 100644 --- a/lib/data/services/quiz_service.dart +++ b/lib/data/services/quiz_service.dart @@ -5,7 +5,6 @@ import 'package:quiz_app/core/utils/logger.dart'; import 'package:quiz_app/data/models/base/base_model.dart'; import 'package:quiz_app/data/models/quiz/library_quiz_model.dart'; import 'package:quiz_app/data/models/quiz/question_create_request.dart'; -import 'package:quiz_app/data/models/quiz/question_listings_model.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/data/providers/dio_client.dart'; @@ -91,4 +90,24 @@ class QuizService extends GetxService { return null; } } + + Future?> getQuizById(String quizId) async { + try { + final response = await _dio.get("${APIEndpoint.quiz}/$quizId"); + + if (response.statusCode == 200) { + final parsedResponse = BaseResponseModel.fromJson( + response.data, + (data) => QuizData.fromJson(data), + ); + return parsedResponse; + } else { + logC.e("Failed to fetch quiz by id. Status: ${response.statusCode}"); + return null; + } + } catch (e) { + logC.e("Error fetching quiz by id $e"); + return null; + } + } } diff --git a/lib/feature/detail_quiz/binding/detail_quiz_binding.dart b/lib/feature/detail_quiz/binding/detail_quiz_binding.dart new file mode 100644 index 0000000..500837a --- /dev/null +++ b/lib/feature/detail_quiz/binding/detail_quiz_binding.dart @@ -0,0 +1,13 @@ +import 'package:get/get.dart'; +import 'package:quiz_app/data/services/quiz_service.dart'; +import 'package:quiz_app/feature/detail_quiz/controller/detail_quiz_controller.dart'; + +class DetailQuizBinding extends Bindings { + @override + void dependencies() { + if (!Get.isRegistered()) { + Get.lazyPut(() => QuizService()); + } + Get.lazyPut(() => DetailQuizController(Get.find())); + } +} diff --git a/lib/feature/detail_quiz/controller/detail_quiz_controller.dart b/lib/feature/detail_quiz/controller/detail_quiz_controller.dart new file mode 100644 index 0000000..a4ecddf --- /dev/null +++ b/lib/feature/detail_quiz/controller/detail_quiz_controller.dart @@ -0,0 +1,40 @@ +import 'package:get/get.dart'; +import 'package:quiz_app/app/routes/app_pages.dart'; +import 'package:quiz_app/data/models/base/base_model.dart'; +import 'package:quiz_app/data/models/quiz/library_quiz_model.dart'; +import 'package:quiz_app/data/services/quiz_service.dart'; + +class DetailQuizController extends GetxController { + final QuizService _quizService; + + DetailQuizController(this._quizService); + + RxBool isLoading = true.obs; + + late QuizData data; + + @override + void onInit() { + super.onInit(); + loadData(); + } + + void loadData() async { + final args = Get.arguments; + if (args is QuizData) { + data = args; + } else { + getQuizData(args); + } + } + + void getQuizData(String quizId) async { + BaseResponseModel? response = await _quizService.getQuizById(quizId); + if (response != null) { + data = response.data; + } + isLoading.value = false; + } + + void goToPlayPage() => Get.toNamed(AppRoutes.playQuizPage, arguments: data); +} diff --git a/lib/feature/detail_quiz/view/detail_quix_view.dart b/lib/feature/detail_quiz/view/detail_quix_view.dart new file mode 100644 index 0000000..a178585 --- /dev/null +++ b/lib/feature/detail_quiz/view/detail_quix_view.dart @@ -0,0 +1,181 @@ +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_button.dart'; +import 'package:quiz_app/component/widget/loading_widget.dart'; +import 'package:quiz_app/data/models/quiz/question_listings_model.dart'; +import 'package:quiz_app/feature/detail_quiz/controller/detail_quiz_controller.dart'; + +class DetailQuizView extends GetView { + const DetailQuizView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: AppBar( + backgroundColor: AppColors.background, + elevation: 0, + title: const Text( + 'Detail Quiz', + style: TextStyle( + color: AppColors.darkText, + fontWeight: FontWeight.bold, + ), + ), + centerTitle: true, + iconTheme: const IconThemeData(color: AppColors.darkText), + ), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(20), + child: Obx( + () => controller.isLoading.value + ? Center(child: LoadingWidget()) + : SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header Section + Text( + controller.data.title, + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: AppColors.darkText, + ), + ), + const SizedBox(height: 8), + Text( + controller.data.description ?? "", + style: const TextStyle( + fontSize: 14, + color: AppColors.softGrayText, + ), + ), + const SizedBox(height: 16), + Row( + children: [ + const Icon(Icons.calendar_today_rounded, size: 16, color: AppColors.softGrayText), + const SizedBox(width: 6), + Text( + controller.data.date ?? "", + style: const TextStyle(fontSize: 12, color: AppColors.softGrayText), + ), + const SizedBox(width: 12), + const Icon(Icons.timer_rounded, size: 16, color: AppColors.softGrayText), + const SizedBox(width: 6), + Text( + '${controller.data.limitDuration ~/ 60} menit', + style: const TextStyle(fontSize: 12, color: AppColors.softGrayText), + ), + ], + ), + const SizedBox(height: 20), + const SizedBox(height: 20), + + GlobalButton(text: "Kerjakan", onPressed: controller.goToPlayPage), + const SizedBox(height: 20), + GlobalButton(text: "buat ruangan", onPressed: () {}), + + const SizedBox(height: 20), + const Divider(thickness: 1.2, color: AppColors.borderLight), + const SizedBox(height: 20), + // Soal Section + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: controller.data.questionListings.length, + itemBuilder: (context, index) { + final question = controller.data.questionListings[index]; + return _buildQuestionItem(question, index + 1); + }, + ), + ], + ), + ), + ), + ), + ), + ); + } + + Widget _buildQuestionItem(QuestionListing question, int index) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(bottom: 16), + 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), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Soal $index', + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: AppColors.darkText, + ), + ), + const SizedBox(height: 8), + Text( + _mapQuestionTypeToText(question.type), + style: const TextStyle( + fontSize: 12, + fontStyle: FontStyle.italic, + color: AppColors.softGrayText, + ), + ), + const SizedBox(height: 12), + Text( + question.question, + style: const TextStyle( + fontSize: 14, + color: AppColors.darkText, + ), + ), + // const SizedBox(height: 12), + // Text( + // 'Jawaban: ${question.targetAnswer}', + // style: const TextStyle( + // fontSize: 14, + // color: AppColors.softGrayText, + // ), + // ), + const SizedBox(height: 8), + Text( + 'Durasi: ${question.duration} detik', + style: const TextStyle( + fontSize: 12, + color: AppColors.softGrayText, + ), + ), + ], + ), + ); + } + + String _mapQuestionTypeToText(String? type) { + switch (type) { + case 'option': + return 'Pilihan Ganda'; + case 'fill_the_blank': + return 'Isian Kosong'; + case 'true_false': + return 'Benar / Salah'; + default: + return 'Tipe Tidak Diketahui'; + } + } +} diff --git a/lib/feature/history/view/history_view.dart b/lib/feature/history/view/history_view.dart index 7bded36..2b9a09e 100644 --- a/lib/feature/history/view/history_view.dart +++ b/lib/feature/history/view/history_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:quiz_app/component/widget/loading_widget.dart'; import 'package:quiz_app/data/models/history/quiz_history.dart'; import 'package:quiz_app/feature/history/controller/history_controller.dart'; @@ -36,12 +37,7 @@ class HistoryView extends GetView { const SizedBox(height: 20), if (historyList.isEmpty) const Expanded( - child: Center( - child: Text( - "Belum ada kuis yang dikerjakan.", - style: TextStyle(fontSize: 16, color: Colors.grey), - ), - ), + child: Center(child: LoadingWidget()), ) else Expanded( diff --git a/lib/feature/home/controller/home_controller.dart b/lib/feature/home/controller/home_controller.dart index 0b74bac..bc8b970 100644 --- a/lib/feature/home/controller/home_controller.dart +++ b/lib/feature/home/controller/home_controller.dart @@ -29,4 +29,6 @@ class HomeController extends GetxController { data.assignAll(response.data as List); } } + + void onRecommendationTap(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId); } diff --git a/lib/feature/home/view/home_page.dart b/lib/feature/home/view/home_page.dart index 5874e55..932453a 100644 --- a/lib/feature/home/view/home_page.dart +++ b/lib/feature/home/view/home_page.dart @@ -47,6 +47,7 @@ class HomeView extends GetView { () => RecomendationComponent( title: "Quiz Rekomendasi", datas: controller.data.toList(), + itemOnTap: controller.onRecommendationTap, ), ), ], diff --git a/lib/feature/library/binding/detail_quiz_binding.dart b/lib/feature/library/binding/detail_quiz_binding.dart deleted file mode 100644 index 684c18c..0000000 --- a/lib/feature/library/binding/detail_quiz_binding.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:get/get.dart'; -import 'package:quiz_app/feature/library/controller/detail_quiz_controller.dart'; - -class DetailQuizBinding extends Bindings { - @override - void dependencies() { - Get.lazyPut(() => DetailQuizController()); - } -} diff --git a/lib/feature/library/binding/library_binding.dart b/lib/feature/library/binding/library_binding.dart index 854c38f..6bdaf97 100644 --- a/lib/feature/library/binding/library_binding.dart +++ b/lib/feature/library/binding/library_binding.dart @@ -6,8 +6,9 @@ import 'package:quiz_app/feature/library/controller/library_controller.dart'; class LibraryBinding extends Bindings { @override void dependencies() { - Get.lazyPut(() => QuizService()); - + if (!Get.isRegistered()) { + Get.lazyPut(() => QuizService()); + } Get.lazyPut(() => LibraryController(Get.find(), Get.find())); } } diff --git a/lib/feature/library/controller/detail_quiz_controller.dart b/lib/feature/library/controller/detail_quiz_controller.dart deleted file mode 100644 index e5b04fe..0000000 --- a/lib/feature/library/controller/detail_quiz_controller.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:get/get.dart'; -import 'package:quiz_app/app/routes/app_pages.dart'; -import 'package:quiz_app/data/models/quiz/library_quiz_model.dart'; - -class DetailQuizController extends GetxController { - late QuizData data; - @override - void onInit() { - loadData(); - super.onInit(); - } - - void loadData() { - data = Get.arguments as QuizData; - } - - void goToPlayPage() => Get.toNamed(AppRoutes.playQuizPage, arguments: data); -} diff --git a/lib/feature/library/view/detail_quix_view.dart b/lib/feature/library/view/detail_quix_view.dart deleted file mode 100644 index 5b0be23..0000000 --- a/lib/feature/library/view/detail_quix_view.dart +++ /dev/null @@ -1,176 +0,0 @@ -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_button.dart'; -import 'package:quiz_app/data/models/quiz/question_listings_model.dart'; -import 'package:quiz_app/feature/library/controller/detail_quiz_controller.dart'; - -class DetailQuizView extends GetView { - const DetailQuizView({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: AppColors.background, - appBar: AppBar( - backgroundColor: AppColors.background, - elevation: 0, - title: const Text( - 'Detail Quiz', - style: TextStyle( - color: AppColors.darkText, - fontWeight: FontWeight.bold, - ), - ), - centerTitle: true, - iconTheme: const IconThemeData(color: AppColors.darkText), - ), - body: SafeArea( - child: Padding( - padding: const EdgeInsets.all(20), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Header Section - Text( - controller.data.title, - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: AppColors.darkText, - ), - ), - const SizedBox(height: 8), - Text( - controller.data.description ?? "", - style: const TextStyle( - fontSize: 14, - color: AppColors.softGrayText, - ), - ), - const SizedBox(height: 16), - Row( - children: [ - const Icon(Icons.calendar_today_rounded, size: 16, color: AppColors.softGrayText), - const SizedBox(width: 6), - Text( - controller.data.date ?? "", - style: const TextStyle(fontSize: 12, color: AppColors.softGrayText), - ), - const SizedBox(width: 12), - const Icon(Icons.timer_rounded, size: 16, color: AppColors.softGrayText), - const SizedBox(width: 6), - Text( - '${controller.data.limitDuration ~/ 60} menit', - style: const TextStyle(fontSize: 12, color: AppColors.softGrayText), - ), - ], - ), - const SizedBox(height: 20), - const SizedBox(height: 20), - - GlobalButton(text: "Kerjakan", onPressed: controller.goToPlayPage), - const SizedBox(height: 20), - GlobalButton(text: "buat ruangan", onPressed: () {}), - - const SizedBox(height: 20), - const Divider(thickness: 1.2, color: AppColors.borderLight), - const SizedBox(height: 20), - // Soal Section - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: controller.data.questionListings.length, - itemBuilder: (context, index) { - final question = controller.data.questionListings[index]; - return _buildQuestionItem(question, index + 1); - }, - ), - ], - ), - ), - ), - ), - ); - } - - Widget _buildQuestionItem(QuestionListing question, int index) { - return Container( - width: double.infinity, - margin: const EdgeInsets.only(bottom: 16), - 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), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Soal $index', - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - color: AppColors.darkText, - ), - ), - const SizedBox(height: 8), - Text( - _mapQuestionTypeToText(question.type), - style: const TextStyle( - fontSize: 12, - fontStyle: FontStyle.italic, - color: AppColors.softGrayText, - ), - ), - const SizedBox(height: 12), - Text( - question.question, - style: const TextStyle( - fontSize: 14, - color: AppColors.darkText, - ), - ), - // const SizedBox(height: 12), - // Text( - // 'Jawaban: ${question.targetAnswer}', - // style: const TextStyle( - // fontSize: 14, - // color: AppColors.softGrayText, - // ), - // ), - const SizedBox(height: 8), - Text( - 'Durasi: ${question.duration} detik', - style: const TextStyle( - fontSize: 12, - color: AppColors.softGrayText, - ), - ), - ], - ), - ); - } - - String _mapQuestionTypeToText(String? type) { - switch (type) { - case 'option': - return 'Pilihan Ganda'; - case 'fill_the_blank': - return 'Isian Kosong'; - case 'true_false': - return 'Benar / Salah'; - default: - return 'Tipe Tidak Diketahui'; - } - } -} diff --git a/lib/feature/library/view/library_view.dart b/lib/feature/library/view/library_view.dart index 7f574c6..58bcbcb 100644 --- a/lib/feature/library/view/library_view.dart +++ b/lib/feature/library/view/library_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:quiz_app/component/widget/loading_widget.dart'; import 'package:quiz_app/data/models/quiz/library_quiz_model.dart'; import 'package:quiz_app/feature/library/controller/library_controller.dart'; @@ -36,19 +37,7 @@ class LibraryView extends GetView { Expanded( child: Obx(() { if (controller.isLoading.value) { - return const Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CircularProgressIndicator(), - SizedBox(height: 12), - Text( - "Memuat data...", - style: TextStyle(color: Colors.grey, fontSize: 14), - ), - ], - ), - ); + return LoadingWidget(); } if (controller.quizs.isEmpty) { diff --git a/lib/feature/search/controller/search_controller.dart b/lib/feature/search/controller/search_controller.dart index da1e848..2a59484 100644 --- a/lib/feature/search/controller/search_controller.dart +++ b/lib/feature/search/controller/search_controller.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:quiz_app/app/routes/app_pages.dart'; import 'package:quiz_app/data/models/base/base_model.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/data/services/quiz_service.dart'; @@ -44,6 +45,8 @@ class SearchQuizController extends GetxController { } } + void goToDetailPage(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId); + @override void onClose() { searchController.dispose(); diff --git a/lib/feature/search/view/search_view.dart b/lib/feature/search/view/search_view.dart index 248d695..14c5ee5 100644 --- a/lib/feature/search/view/search_view.dart +++ b/lib/feature/search/view/search_view.dart @@ -23,15 +23,15 @@ class SearchView extends GetView { if (isSearching) ...[ _buildCategoryFilter(), const SizedBox(height: 20), - ...controller.searchQData.map( - (e) => QuizContainerComponent(data: e), + (e) => QuizContainerComponent(data: e, onTap: controller.goToDetailPage), ) ] else ...[ Obx( () => RecomendationComponent( title: "Quiz Rekomendasi", datas: controller.recommendationQData.toList(), + itemOnTap: controller.goToDetailPage, ), ), const SizedBox(height: 30), @@ -39,6 +39,7 @@ class SearchView extends GetView { () => RecomendationComponent( title: "Quiz Populer", datas: controller.recommendationQData.toList(), + itemOnTap: controller.goToDetailPage, ), ), ],