From 481bfbe2287a95d9cf97650ae2de49480c5362d3 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Mon, 5 May 2025 14:40:26 +0700 Subject: [PATCH] feat: done working on the room maker --- lib/app/routes/app_pages.dart | 7 + lib/app/routes/app_routes.dart | 2 + lib/component/global_text_field.dart | 23 ++- .../home/controller/home_controller.dart | 16 +- lib/feature/home/view/home_page.dart | 2 +- .../binding/room_maker_binding.dart | 9 + .../controller/room_maker_controller.dart | 61 ++++++ .../room_maker/view/room_maker_view.dart | 175 ++++++++++++++++++ 8 files changed, 277 insertions(+), 18 deletions(-) create mode 100644 lib/feature/room_maker/binding/room_maker_binding.dart create mode 100644 lib/feature/room_maker/controller/room_maker_controller.dart create mode 100644 lib/feature/room_maker/view/room_maker_view.dart diff --git a/lib/app/routes/app_pages.dart b/lib/app/routes/app_pages.dart index 18eb3b1..59eead7 100644 --- a/lib/app/routes/app_pages.dart +++ b/lib/app/routes/app_pages.dart @@ -25,6 +25,8 @@ import 'package:quiz_app/feature/quiz_result/binding/quiz_result_binding.dart'; import 'package:quiz_app/feature/quiz_result/view/quiz_result_view.dart'; import 'package:quiz_app/feature/register/binding/register_binding.dart'; import 'package:quiz_app/feature/register/view/register_page.dart'; +import 'package:quiz_app/feature/room_maker/binding/room_maker_binding.dart'; +import 'package:quiz_app/feature/room_maker/view/room_maker_view.dart'; import 'package:quiz_app/feature/search/binding/search_binding.dart'; import 'package:quiz_app/feature/splash_screen/presentation/splash_screen_page.dart'; @@ -99,6 +101,11 @@ class AppPages { name: AppRoutes.detailHistoryPage, page: () => DetailHistoryView(), binding: DetailHistoryBinding(), + ), + GetPage( + name: AppRoutes.roomPage, + page: () => RoomMakerView(), + binding: RoomMakerBinding(), ) ]; } diff --git a/lib/app/routes/app_routes.dart b/lib/app/routes/app_routes.dart index 24a779d..6fa2357 100644 --- a/lib/app/routes/app_routes.dart +++ b/lib/app/routes/app_routes.dart @@ -17,4 +17,6 @@ abstract class AppRoutes { static const resultQuizPage = "/quiz/result"; static const detailHistoryPage = "/history/detail"; + + static const roomPage = "/room/quiz"; } diff --git a/lib/component/global_text_field.dart b/lib/component/global_text_field.dart index ca8369d..73ffe19 100644 --- a/lib/component/global_text_field.dart +++ b/lib/component/global_text_field.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; class GlobalTextField extends StatelessWidget { final TextEditingController controller; @@ -8,22 +9,24 @@ class GlobalTextField extends StatelessWidget { final bool isPassword; final bool obscureText; final VoidCallback? onToggleVisibility; + final TextInputType textInputType; - const GlobalTextField({ - super.key, - required this.controller, - this.hintText, - this.labelText, - this.limitTextLine = 1, - this.isPassword = false, - this.obscureText = false, - this.onToggleVisibility, - }); + const GlobalTextField( + {super.key, + required this.controller, + this.hintText, + this.labelText, + this.limitTextLine = 1, + this.isPassword = false, + this.obscureText = false, + this.onToggleVisibility, + this.textInputType = TextInputType.text}); @override Widget build(BuildContext context) { return TextField( controller: controller, + keyboardType: textInputType, obscureText: isPassword ? obscureText : false, maxLines: limitTextLine, // <-- ini tambahan dari limitTextLine decoration: InputDecoration( diff --git a/lib/feature/home/controller/home_controller.dart b/lib/feature/home/controller/home_controller.dart index 8312079..4dff15d 100644 --- a/lib/feature/home/controller/home_controller.dart +++ b/lib/feature/home/controller/home_controller.dart @@ -27,13 +27,6 @@ class HomeController extends GetxController { RxList subjects = [].obs; - void goToQuizCreation() => Get.toNamed(AppRoutes.quizCreatePage); - - void goToSearch() { - final navController = Get.find(); - navController.changePage(1); - } - @override void onInit() { _getRecomendationQuiz(); @@ -55,6 +48,15 @@ class HomeController extends GetxController { } } + void goToQuizCreation() => Get.toNamed(AppRoutes.quizCreatePage); + + void goToRoomMaker() => Get.toNamed(AppRoutes.roomPage); + + void goToSearch() { + final navController = Get.find(); + navController.changePage(1); + } + void onRecommendationTap(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId); void goToListingsQuizPage(ListingType page, {String? subjectId, String? subjecName}) => Get.toNamed( diff --git a/lib/feature/home/view/home_page.dart b/lib/feature/home/view/home_page.dart index 7cc6bd7..2cbddb6 100644 --- a/lib/feature/home/view/home_page.dart +++ b/lib/feature/home/view/home_page.dart @@ -35,7 +35,7 @@ class HomeView extends GetView { // ButtonOption di luar Padding ButtonOption( onCreate: controller.goToQuizCreation, - onCreateRoom: () {}, + onCreateRoom: controller.goToRoomMaker, onJoinRoom: () {}, ), Padding( diff --git a/lib/feature/room_maker/binding/room_maker_binding.dart b/lib/feature/room_maker/binding/room_maker_binding.dart new file mode 100644 index 0000000..2e5157a --- /dev/null +++ b/lib/feature/room_maker/binding/room_maker_binding.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import 'package:quiz_app/feature/room_maker/controller/room_maker_controller.dart'; + +class RoomMakerBinding extends Bindings { + @override + void dependencies() { + Get.lazyPut(() => RoomMakerController()); + } +} diff --git a/lib/feature/room_maker/controller/room_maker_controller.dart b/lib/feature/room_maker/controller/room_maker_controller.dart new file mode 100644 index 0000000..23d58ed --- /dev/null +++ b/lib/feature/room_maker/controller/room_maker_controller.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; + +class RoomMakerController extends GetxController { + final roomName = ''.obs; + final selectedQuiz = Rxn(); + + RxBool isOnwQuiz = true.obs; + + final TextEditingController nameTC = TextEditingController(); + final TextEditingController maxPlayerTC = TextEditingController(); + + final availableQuizzes = [ + QuizListingModel( + quizId: '1', + authorId: 'u1', + authorName: 'Admin', + title: 'Sejarah Indonesia', + description: 'Kuis tentang kerajaan dan sejarah nusantara.', + date: '2025-05-01', + totalQuiz: 10, + duration: 600, + ), + QuizListingModel( + quizId: '2', + authorId: 'u2', + authorName: 'Guru IPA', + title: 'Ilmu Pengetahuan Alam', + description: 'Kuis IPA untuk kelas 8.', + date: '2025-04-28', + totalQuiz: 15, + duration: 900, + ), + ].obs; + + void createRoom() { + if (roomName.value.trim().isEmpty || selectedQuiz.value == null) { + Get.snackbar("Gagal", "Nama room dan kuis harus dipilih."); + return; + } + + final quiz = selectedQuiz.value!; + print("Membuat room:"); + print("- Nama: ${roomName.value}"); + print("- Quiz: ${quiz.title}"); + print("- Durasi: ${quiz.duration} detik"); + print("- Jumlah Soal: ${quiz.totalQuiz}"); + } + + void onQuizSourceChange(bool base) { + isOnwQuiz.value = base; + } + + void onQuizChoosen(String quizId) { + final selected = availableQuizzes.firstWhere((e) => e.quizId == quizId); + selectedQuiz.value = selected; + } + + void onCreateRoom() {} +} diff --git a/lib/feature/room_maker/view/room_maker_view.dart b/lib/feature/room_maker/view/room_maker_view.dart new file mode 100644 index 0000000..cf0ba5d --- /dev/null +++ b/lib/feature/room_maker/view/room_maker_view.dart @@ -0,0 +1,175 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.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/component/global_button.dart'; +import 'package:quiz_app/component/global_text_field.dart'; +import 'package:quiz_app/component/label_text_field.dart'; +import 'package:quiz_app/component/quiz_container_component.dart'; +import 'package:quiz_app/feature/room_maker/controller/room_maker_controller.dart'; + +class RoomMakerView extends GetView { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.background, + appBar: AppBar(title: Text("Buat Room Quiz")), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + LabelTextField( + label: "Room Name", + ), + GlobalTextField( + controller: controller.nameTC, + ), + SizedBox( + height: 10, + ), + LabelTextField(label: "Jumlah Maksimal Pemain"), + GlobalTextField( + controller: controller.maxPlayerTC, + textInputType: TextInputType.number, + ), + const SizedBox(height: 10), + quizMeta(), + SizedBox( + height: 10, + ), + _buildModeSelector(), + SizedBox( + height: 10, + ), + Expanded( + child: Container( + child: Obx(() => ListView.builder( + itemCount: controller.availableQuizzes.length, + itemBuilder: (context, index) { + return QuizContainerComponent( + data: controller.availableQuizzes[index], + onTap: controller.onQuizChoosen, + ); + })), + ), + ), + SizedBox( + height: 10, + ), + GlobalButton(text: "Buat Room", onPressed: controller.onCreateRoom) + ], + ), + ), + ); + } + + Widget quizMeta() { + return Obx(() { + final quiz = controller.selectedQuiz.value; + if (quiz == null) return SizedBox.shrink(); + + return Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + margin: const EdgeInsets.only(top: 16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 8, + offset: Offset(0, 4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Kuis yang Dipilih", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + const SizedBox(height: 12), + _buildMetaRow("Judul", quiz.title), + _buildMetaRow("Deskripsi", quiz.description), + _buildMetaRow("Jumlah Soal", quiz.totalQuiz.toString()), + _buildMetaRow("Durasi", "${quiz.duration ~/ 60} menit"), + ], + ), + ); + }); + } + + Widget _buildMetaRow(String label, String value) { + return Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("$label: ", style: AppTextStyles.subtitle), + Expanded( + child: Text( + value, + style: AppTextStyles.subtitle, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } + + Widget _buildModeSelector() { + return Container( + decoration: BoxDecoration( + color: AppColors.background, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.borderLight), + ), + child: Row( + children: [ + _buildModeButton('kuismu', controller.isOnwQuiz, true), + _buildModeButton('Rekomendasi', controller.isOnwQuiz, false), + ], + ), + ); + } + + Widget _buildModeButton(String label, RxBool isSelected, bool base) { + return Expanded( + child: InkWell( + onTap: () => controller.onQuizSourceChange(base), + child: Obx( + () => Container( + padding: const EdgeInsets.symmetric(vertical: 14), + decoration: BoxDecoration( + color: isSelected.value == base ? AppColors.primaryBlue : Colors.transparent, + borderRadius: base + ? BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10), + ) + : BorderRadius.only( + topRight: Radius.circular(10), + bottomRight: Radius.circular(10), + ), + ), + alignment: Alignment.center, + child: Text( + label, + style: TextStyle( + color: isSelected.value == base ? Colors.white : AppColors.softGrayText, + fontWeight: FontWeight.w600, + ), + ), + ), + ), + ), + ); + } +}