develop #1
|
@ -10,6 +10,8 @@ import 'package:quiz_app/feature/navigation/views/navbar_view.dart';
|
|||
import 'package:quiz_app/feature/profile/binding/profile_binding.dart';
|
||||
import 'package:quiz_app/feature/quiz_creation/binding/quiz_creation_binding.dart';
|
||||
import 'package:quiz_app/feature/quiz_creation/view/quiz_creation_view.dart';
|
||||
import 'package:quiz_app/feature/quiz_preview/binding/quiz_preview_binding.dart';
|
||||
import 'package:quiz_app/feature/quiz_preview/view/quiz_preview.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/search/binding/search_binding.dart';
|
||||
|
@ -56,5 +58,10 @@ class AppPages {
|
|||
page: () => QuizCreationView(),
|
||||
binding: QuizCreationBinding(),
|
||||
),
|
||||
GetPage(
|
||||
name: AppRoutes.quizPreviewPage,
|
||||
page: () => QuizPreviewPage(),
|
||||
binding: QuizPreviewBinding(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -9,4 +9,5 @@ abstract class AppRoutes {
|
|||
static const mainPage = '/main';
|
||||
|
||||
static const quizCreatePage = "/quiz/creation";
|
||||
static const quizPreviewPage = "/quiz/preview";
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:quiz_app/app/const/enums/question_type.dart';
|
||||
import 'package:quiz_app/app/routes/app_pages.dart';
|
||||
import 'package:quiz_app/data/models/quiz/quiestion_data_model.dart';
|
||||
|
||||
class QuizCreationController extends GetxController {
|
||||
|
@ -123,4 +124,8 @@ class QuizCreationController extends GetxController {
|
|||
void updateTOFAnswer(bool answer) {
|
||||
_updateCurrentQuestion(answer: answer.toString());
|
||||
}
|
||||
|
||||
void onDone() {
|
||||
Get.toNamed(AppRoutes.quizPreviewPage, arguments: quizData);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class QuizCreationView extends GetView<QuizCreationController> {
|
|||
() => controller.isGenerate.value ? GenerateComponent() : CustomQuestionComponent(),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
GlobalButton(text: "simpan semua", onPressed: () {})
|
||||
GlobalButton(text: "simpan semua", onPressed: controller.onDone)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:quiz_app/feature/quiz_preview/controller/quiz_preview_controller.dart';
|
||||
|
||||
class QuizPreviewBinding extends Bindings {
|
||||
@override
|
||||
void dependencies() {
|
||||
Get.lazyPut<QuizPreviewController>(() => QuizPreviewController());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
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/enums/question_type.dart';
|
||||
import 'package:quiz_app/data/models/quiz/quiestion_data_model.dart';
|
||||
|
||||
class QuizPreviewController extends GetxController {
|
||||
final TextEditingController titleController = TextEditingController();
|
||||
final TextEditingController descriptionController = TextEditingController();
|
||||
|
||||
late final List<QuestionData> data;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadData();
|
||||
}
|
||||
|
||||
void loadData() {
|
||||
if (Get.arguments is List<QuestionData>) {
|
||||
data = Get.arguments as List<QuestionData>;
|
||||
} else {
|
||||
data = []; // Default aman supaya gak crash
|
||||
Get.snackbar('Error', 'Data soal tidak ditemukan');
|
||||
}
|
||||
}
|
||||
|
||||
void onSaveQuiz() {
|
||||
final title = titleController.text.trim();
|
||||
final description = descriptionController.text.trim();
|
||||
|
||||
if (title.isEmpty || description.isEmpty) {
|
||||
Get.snackbar('Error', 'Judul dan deskripsi tidak boleh kosong!');
|
||||
return;
|
||||
}
|
||||
|
||||
Get.snackbar('Sukses', 'Kuis berhasil disimpan!');
|
||||
}
|
||||
|
||||
Widget buildQuestionCard(QuestionData question) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
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 ${question.index}', style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: AppColors.darkText)),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
_mapQuestionTypeToText(question.type),
|
||||
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText, fontStyle: FontStyle.italic),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
question.question ?? '-',
|
||||
style: const TextStyle(fontSize: 16, color: AppColors.darkText),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildAnswerSection(question),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
'Durasi: 0 detik',
|
||||
style: TextStyle(fontSize: 14, color: AppColors.softGrayText),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAnswerSection(QuestionData question) {
|
||||
if (question.type == QuestionType.option && question.options != null) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: question.options!.map((option) {
|
||||
bool isCorrect = question.correctAnswerIndex == option.index;
|
||||
return Container(
|
||||
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,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
isCorrect ? Icons.check_circle_rounded : Icons.circle_outlined,
|
||||
size: 18,
|
||||
color: isCorrect ? AppColors.primaryBlue : AppColors.softGrayText,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
option.text,
|
||||
style: TextStyle(
|
||||
fontWeight: isCorrect ? FontWeight.bold : FontWeight.normal,
|
||||
color: isCorrect ? AppColors.primaryBlue : AppColors.darkText,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
} else if (question.type == QuestionType.fillTheBlank) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _buildFillTheBlankPossibilities(question.answer ?? '-'),
|
||||
);
|
||||
} else if (question.type == QuestionType.trueOrFalse) {
|
||||
return Text(
|
||||
'Jawaban: ${question.answer ?? '-'}',
|
||||
style: const TextStyle(color: AppColors.softGrayText),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
String _mapQuestionTypeToText(QuestionType? type) {
|
||||
switch (type) {
|
||||
case QuestionType.option:
|
||||
return 'Tipe: Pilihan Ganda';
|
||||
case QuestionType.fillTheBlank:
|
||||
return 'Tipe: Isian Kosong';
|
||||
case QuestionType.trueOrFalse:
|
||||
return 'Tipe: Benar / Salah';
|
||||
default:
|
||||
return 'Tipe: Tidak diketahui';
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> _buildFillTheBlankPossibilities(String answer) {
|
||||
List<String> possibilities = [
|
||||
_capitalizeEachWord(answer),
|
||||
answer.toLowerCase(),
|
||||
_capitalizeFirstWordOnly(answer),
|
||||
];
|
||||
|
||||
return possibilities.map((option) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.arrow_right, size: 18, color: AppColors.softGrayText),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
option,
|
||||
style: const TextStyle(color: AppColors.darkText),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
String _capitalizeEachWord(String text) {
|
||||
return text.split(' ').map((word) {
|
||||
if (word.isEmpty) return word;
|
||||
return word[0].toUpperCase() + word.substring(1).toLowerCase();
|
||||
}).join(' ');
|
||||
}
|
||||
|
||||
String _capitalizeFirstWordOnly(String text) {
|
||||
if (text.isEmpty) return text;
|
||||
List<String> parts = text.split(' ');
|
||||
parts[0] = parts[0][0].toUpperCase() + parts[0].substring(1).toLowerCase();
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
parts[i] = parts[i].toLowerCase();
|
||||
}
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
titleController.dispose();
|
||||
descriptionController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
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/feature/quiz_preview/controller/quiz_preview_controller.dart';
|
||||
|
||||
class QuizPreviewPage extends GetView<QuizPreviewController> {
|
||||
const QuizPreviewPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
appBar: AppBar(
|
||||
backgroundColor: AppColors.background,
|
||||
elevation: 0,
|
||||
title: const Text(
|
||||
'Preview 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.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildTextField(
|
||||
label: 'Judul Kuis',
|
||||
controller: controller.titleController,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildTextField(
|
||||
label: 'Deskripsi Kuis',
|
||||
controller: controller.descriptionController,
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
const Divider(thickness: 1.2, color: AppColors.borderLight),
|
||||
const SizedBox(height: 20),
|
||||
_buildQuestionContent(),
|
||||
const SizedBox(height: 30),
|
||||
GlobalButton(
|
||||
onPressed: controller.onSaveQuiz,
|
||||
text: "Simpan Kuis",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTextField({
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
int maxLines = 1,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontWeight: FontWeight.w600, color: AppColors.softGrayText)),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: controller,
|
||||
maxLines: maxLines,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Masukkan $label',
|
||||
hintStyle: const TextStyle(color: AppColors.softGrayText),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: AppColors.borderLight),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: AppColors.borderLight),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: const BorderSide(color: AppColors.primaryBlue, width: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildQuestionContent() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: controller.data.map((question) {
|
||||
return controller.buildQuestionCard(question);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue