feat: done working on the room maker

This commit is contained in:
akhdanre 2025-05-05 14:40:26 +07:00
parent ca9e9cde7d
commit 481bfbe228
8 changed files with 277 additions and 18 deletions

View File

@ -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/quiz_result/view/quiz_result_view.dart';
import 'package:quiz_app/feature/register/binding/register_binding.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/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/search/binding/search_binding.dart';
import 'package:quiz_app/feature/splash_screen/presentation/splash_screen_page.dart'; import 'package:quiz_app/feature/splash_screen/presentation/splash_screen_page.dart';
@ -99,6 +101,11 @@ class AppPages {
name: AppRoutes.detailHistoryPage, name: AppRoutes.detailHistoryPage,
page: () => DetailHistoryView(), page: () => DetailHistoryView(),
binding: DetailHistoryBinding(), binding: DetailHistoryBinding(),
),
GetPage(
name: AppRoutes.roomPage,
page: () => RoomMakerView(),
binding: RoomMakerBinding(),
) )
]; ];
} }

View File

@ -17,4 +17,6 @@ abstract class AppRoutes {
static const resultQuizPage = "/quiz/result"; static const resultQuizPage = "/quiz/result";
static const detailHistoryPage = "/history/detail"; static const detailHistoryPage = "/history/detail";
static const roomPage = "/room/quiz";
} }

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class GlobalTextField extends StatelessWidget { class GlobalTextField extends StatelessWidget {
final TextEditingController controller; final TextEditingController controller;
@ -8,22 +9,24 @@ class GlobalTextField extends StatelessWidget {
final bool isPassword; final bool isPassword;
final bool obscureText; final bool obscureText;
final VoidCallback? onToggleVisibility; final VoidCallback? onToggleVisibility;
final TextInputType textInputType;
const GlobalTextField({ const GlobalTextField(
super.key, {super.key,
required this.controller, required this.controller,
this.hintText, this.hintText,
this.labelText, this.labelText,
this.limitTextLine = 1, this.limitTextLine = 1,
this.isPassword = false, this.isPassword = false,
this.obscureText = false, this.obscureText = false,
this.onToggleVisibility, this.onToggleVisibility,
}); this.textInputType = TextInputType.text});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return TextField( return TextField(
controller: controller, controller: controller,
keyboardType: textInputType,
obscureText: isPassword ? obscureText : false, obscureText: isPassword ? obscureText : false,
maxLines: limitTextLine, // <-- ini tambahan dari limitTextLine maxLines: limitTextLine, // <-- ini tambahan dari limitTextLine
decoration: InputDecoration( decoration: InputDecoration(

View File

@ -27,13 +27,6 @@ class HomeController extends GetxController {
RxList<SubjectModel> subjects = <SubjectModel>[].obs; RxList<SubjectModel> subjects = <SubjectModel>[].obs;
void goToQuizCreation() => Get.toNamed(AppRoutes.quizCreatePage);
void goToSearch() {
final navController = Get.find<NavigationController>();
navController.changePage(1);
}
@override @override
void onInit() { void onInit() {
_getRecomendationQuiz(); _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<NavigationController>();
navController.changePage(1);
}
void onRecommendationTap(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId); void onRecommendationTap(String quizId) => Get.toNamed(AppRoutes.detailQuizPage, arguments: quizId);
void goToListingsQuizPage(ListingType page, {String? subjectId, String? subjecName}) => Get.toNamed( void goToListingsQuizPage(ListingType page, {String? subjectId, String? subjecName}) => Get.toNamed(

View File

@ -35,7 +35,7 @@ class HomeView extends GetView<HomeController> {
// ButtonOption di luar Padding // ButtonOption di luar Padding
ButtonOption( ButtonOption(
onCreate: controller.goToQuizCreation, onCreate: controller.goToQuizCreation,
onCreateRoom: () {}, onCreateRoom: controller.goToRoomMaker,
onJoinRoom: () {}, onJoinRoom: () {},
), ),
Padding( Padding(

View File

@ -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());
}
}

View File

@ -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<QuizListingModel>();
RxBool isOnwQuiz = true.obs;
final TextEditingController nameTC = TextEditingController();
final TextEditingController maxPlayerTC = TextEditingController();
final availableQuizzes = <QuizListingModel>[
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() {}
}

View File

@ -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<RoomMakerController> {
@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,
),
),
),
),
),
);
}
}