feat: adjustment on the interface
This commit is contained in:
parent
20017d5bf1
commit
128afe9ad6
|
@ -5,6 +5,7 @@ class AppColors {
|
||||||
static const Color darkText = Color(0xFF172B4D);
|
static const Color darkText = Color(0xFF172B4D);
|
||||||
static const Color softGrayText = Color(0xFF6B778C);
|
static const Color softGrayText = Color(0xFF6B778C);
|
||||||
static const Color background = Color(0xFFFAFBFC);
|
static const Color background = Color(0xFFFAFBFC);
|
||||||
|
static const Color background2 = Color(0xFFF9FAFB);
|
||||||
|
|
||||||
static const Color borderLight = Color(0xFFE1E4E8);
|
static const Color borderLight = Color(0xFFE1E4E8);
|
||||||
static const Color accentBlue = Color(0xFFD6E4FF);
|
static const Color accentBlue = Color(0xFFD6E4FF);
|
||||||
|
|
|
@ -62,62 +62,102 @@ class HistoryView extends GetView<HistoryController> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHistoryCard(QuizHistory item) {
|
Widget _buildHistoryCard(QuizHistory item) {
|
||||||
|
final scorePercentage = item.totalCorrect / item.totalQuestion;
|
||||||
|
final scoreColor = scorePercentage >= 0.7 ? AppColors.primaryBlue : AppColors.scorePoor;
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => controller.goToDetailHistory(item.answerId),
|
onTap: () => controller.goToDetailHistory(item.answerId),
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(14),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: AppColors.background,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(12),
|
||||||
boxShadow: const [
|
border: Border.all(color: AppColors.borderLight),
|
||||||
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black12,
|
color: Colors.black.withValues(alpha: 0.03),
|
||||||
blurRadius: 4,
|
blurRadius: 8,
|
||||||
offset: Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
_buildIconBox(scoreColor),
|
||||||
width: 48,
|
|
||||||
height: 48,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.blue.shade100,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(Icons.history, color: Colors.blue),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
Expanded(
|
Expanded(child: _buildHistoryInfo(item, scorePercentage)),
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(item.title, style: AppTextStyles.statValue),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(item.date, style: AppTextStyles.caption),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.check_circle, size: 14, color: Colors.green),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
tr('score_label', namedArgs: {'correct': item.totalCorrect.toString(), 'total': item.totalQuestion.toString()}),
|
|
||||||
style: AppTextStyles.caption,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
const Icon(Icons.timer, size: 14, color: Colors.grey),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(tr("duration_minutes", namedArgs: {"minute": "3"}), style: AppTextStyles.caption),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildIconBox(Color scoreColor) {
|
||||||
|
return Container(
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: scoreColor,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.assignment_turned_in,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHistoryInfo(QuizHistory item, double scorePercentage) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: AppColors.darkText,
|
||||||
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'Completed on ${item.date}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: AppColors.softGrayText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.check_circle_outline, size: 14, color: AppColors.softGrayText),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'${item.totalCorrect}/${item.totalQuestion} Correct',
|
||||||
|
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
const Icon(Icons.access_time, size: 14, color: AppColors.softGrayText),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
tr("duration_minutes", namedArgs: {"minute": "3"}),
|
||||||
|
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
const Icon(Icons.percent, size: 14, color: AppColors.softGrayText),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'${(scorePercentage * 100).toInt()}%',
|
||||||
|
style: const TextStyle(fontSize: 12, color: AppColors.softGrayText),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||||
import 'package:quiz_app/feature/history/view/history_view.dart';
|
import 'package:quiz_app/feature/history/view/history_view.dart';
|
||||||
import 'package:quiz_app/feature/home/view/home_page.dart';
|
import 'package:quiz_app/feature/home/view/home_page.dart';
|
||||||
import 'package:quiz_app/feature/library/view/library_view.dart';
|
import 'package:quiz_app/feature/library/view/library_view.dart';
|
||||||
|
@ -14,6 +15,7 @@ class NavbarView extends GetView<NavigationController> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
backgroundColor: AppColors.background,
|
||||||
body: Obx(() {
|
body: Obx(() {
|
||||||
switch (controller.selectedIndex.value) {
|
switch (controller.selectedIndex.value) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -32,6 +34,8 @@ class NavbarView extends GetView<NavigationController> {
|
||||||
}),
|
}),
|
||||||
bottomNavigationBar: Obx(
|
bottomNavigationBar: Obx(
|
||||||
() => BottomNavigationBar(
|
() => BottomNavigationBar(
|
||||||
|
fixedColor: AppColors.primaryBlue,
|
||||||
|
backgroundColor: AppColors.background2,
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
currentIndex: controller.selectedIndex.value,
|
currentIndex: controller.selectedIndex.value,
|
||||||
onTap: controller.changePage,
|
onTap: controller.changePage,
|
||||||
|
|
|
@ -1,134 +1,348 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:quiz_app/app/const/colors/app_colors.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/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/component/quiz_container_component.dart';
|
||||||
import 'package:quiz_app/feature/room_maker/controller/room_maker_controller.dart';
|
import 'package:quiz_app/feature/room_maker/controller/room_maker_controller.dart';
|
||||||
|
|
||||||
class RoomMakerView extends GetView<RoomMakerController> {
|
class RoomMakerView extends GetView<RoomMakerController> {
|
||||||
|
const RoomMakerView({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.background,
|
backgroundColor: const Color(0xFFF9FAFB),
|
||||||
appBar: AppBar(title: Text("Buat Room Quiz")),
|
body: SafeArea(
|
||||||
body: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
LabelTextField(
|
_buildCustomAppBar(context),
|
||||||
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(
|
Expanded(
|
||||||
child: Container(
|
child: SingleChildScrollView(
|
||||||
child: Obx(() => ListView.builder(
|
padding: const EdgeInsets.all(16.0),
|
||||||
controller: controller.scrollController,
|
child: Column(
|
||||||
itemCount: controller.availableQuizzes.length + (controller.isLoading ? 1 : 0),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
itemBuilder: (context, index) {
|
children: [
|
||||||
if (index < controller.availableQuizzes.length) {
|
_buildRoomSettingsCard(),
|
||||||
return QuizContainerComponent(
|
const SizedBox(height: 20),
|
||||||
data: controller.availableQuizzes[index],
|
_buildQuizMetaCard(),
|
||||||
onTap: controller.onQuizChoosen,
|
const SizedBox(height: 20),
|
||||||
);
|
_buildModeSelector(),
|
||||||
} else {
|
const SizedBox(height: 20),
|
||||||
// Loading Indicator di Bawah
|
_buildQuizListSection(),
|
||||||
return Padding(
|
const SizedBox(height: 80),
|
||||||
padding: const EdgeInsets.all(16.0),
|
],
|
||||||
child: Center(
|
),
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
|
||||||
height: 10,
|
|
||||||
),
|
|
||||||
GlobalButton(text: "Buat Room", onPressed: controller.onCreateRoom)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
floatingActionButton: _buildCreateRoomButton(),
|
||||||
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCustomAppBar(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => Get.back(),
|
||||||
|
icon: const Icon(Icons.arrow_back_ios, color: Colors.black87),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
"Buat Room Quiz",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Siapkan room untuk bermain bersama",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget quizMeta() {
|
Widget _buildRoomSettingsCard() {
|
||||||
return Obx(() {
|
return AnimatedContainer(
|
||||||
final quiz = controller.selectedQuiz.value;
|
duration: const Duration(milliseconds: 300),
|
||||||
if (quiz == null) return SizedBox.shrink();
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 15,
|
||||||
|
offset: const Offset(0, 5),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.primaryBlue.withValues(alpha: 0.1),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.settings,
|
||||||
|
color: AppColors.primaryBlue,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
const Text(
|
||||||
|
"Pengaturan Room",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildInputSection(
|
||||||
|
"Nama Room",
|
||||||
|
"Masukkan nama room",
|
||||||
|
Icons.meeting_room,
|
||||||
|
controller.nameTC,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_buildInputSection(
|
||||||
|
"Maksimal Pemain",
|
||||||
|
"Berapa banyak pemain yang bisa bergabung?",
|
||||||
|
Icons.group,
|
||||||
|
controller.maxPlayerTC,
|
||||||
|
isNumber: true,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
Widget _buildInputSection(String label, String hint, IconData icon, TextEditingController textController, {bool isNumber = false}) {
|
||||||
width: double.infinity,
|
return Column(
|
||||||
padding: const EdgeInsets.all(16),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
margin: const EdgeInsets.only(top: 16),
|
children: [
|
||||||
decoration: BoxDecoration(
|
Row(
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withValues(alpha: 0.05),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
size: 20,
|
||||||
|
color: AppColors.primaryBlue,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
"Kuis yang Dipilih",
|
label,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.w600,
|
||||||
color: Colors.black87,
|
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"),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
hint,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
GlobalTextField(
|
||||||
|
controller: textController,
|
||||||
|
textInputType: isNumber ? TextInputType.number : TextInputType.text,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildQuizMetaCard() {
|
||||||
|
return Obx(() {
|
||||||
|
final quiz = controller.selectedQuiz.value;
|
||||||
|
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 400),
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: quiz == null ? Colors.grey[50] : Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
border: quiz == null ? Border.all(color: Colors.grey[300]!, style: BorderStyle.solid, width: 2) : null,
|
||||||
|
boxShadow: quiz == null
|
||||||
|
? null
|
||||||
|
: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 15,
|
||||||
|
offset: const Offset(0, 5),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: quiz == null ? _buildNoQuizSelected() : _buildSelectedQuizInfo(quiz),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildNoQuizSelected() {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.quiz_outlined,
|
||||||
|
size: 32,
|
||||||
|
color: Colors.grey[500],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
"Pilih kuis untuk dimainkan",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Scroll ke bawah untuk memilih kuis",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey[500],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSelectedQuizInfo(dynamic quiz) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.green.withValues(alpha: 0.1),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
color: Colors.green,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
"Kuis Terpilih",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Siap untuk dimainkan!",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: AppColors.primaryBlue.withValues(alpha: 0.05),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: AppColors.primaryBlue.withValues(alpha: 0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildMetaRow("Judul", quiz.title),
|
||||||
|
_buildMetaRow("Deskripsi", quiz.description),
|
||||||
|
_buildMetaRow("Jumlah Soal", "${quiz.totalQuiz} soal"),
|
||||||
|
_buildMetaRow("Durasi", "${quiz.duration ~/ 60} menit"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildMetaRow(String label, String value) {
|
Widget _buildMetaRow(String label, String value) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
padding: const EdgeInsets.only(bottom: 12.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("$label: ", style: AppTextStyles.subtitle),
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
value,
|
value,
|
||||||
style: AppTextStyles.subtitle,
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey[700],
|
||||||
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -137,51 +351,289 @@ class RoomMakerView extends GetView<RoomMakerController> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildModeSelector() {
|
Widget _buildModeSelector() {
|
||||||
return Container(
|
return Column(
|
||||||
decoration: BoxDecoration(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
color: AppColors.background,
|
children: [
|
||||||
borderRadius: BorderRadius.circular(12),
|
Row(
|
||||||
border: Border.all(color: AppColors.borderLight),
|
children: [
|
||||||
),
|
Icon(
|
||||||
child: Row(
|
Icons.source,
|
||||||
|
size: 20,
|
||||||
|
color: AppColors.primaryBlue,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text(
|
||||||
|
"Sumber Kuis",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_buildModeButton('Kuisku', controller.isOnwQuiz, true, Icons.person),
|
||||||
|
_buildModeButton('Rekomendasi', controller.isOnwQuiz, false, Icons.recommend),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildModeButton(String label, RxBool isSelected, bool base, IconData icon) {
|
||||||
|
return Expanded(
|
||||||
|
child: Obx(() {
|
||||||
|
final selected = isSelected.value == base;
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: base
|
||||||
|
? const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16),
|
||||||
|
bottomLeft: Radius.circular(16),
|
||||||
|
)
|
||||||
|
: const BorderRadius.only(
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
bottomRight: Radius.circular(16),
|
||||||
|
),
|
||||||
|
onTap: () => controller.onQuizSourceChange(base),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: selected ? AppColors.primaryBlue : Colors.transparent,
|
||||||
|
borderRadius: base
|
||||||
|
? const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(16),
|
||||||
|
bottomLeft: Radius.circular(16),
|
||||||
|
)
|
||||||
|
: const BorderRadius.only(
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
bottomRight: Radius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
size: 20,
|
||||||
|
color: selected ? Colors.white : Colors.grey[600],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: TextStyle(
|
||||||
|
color: selected ? Colors.white : Colors.grey[600],
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildQuizListSection() {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.list,
|
||||||
|
size: 20,
|
||||||
|
color: AppColors.primaryBlue,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text(
|
||||||
|
"Pilih Kuis",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Container(
|
||||||
|
height: 400,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 15,
|
||||||
|
offset: const Offset(0, 5),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Obx(() => controller.availableQuizzes.isEmpty && !controller.isLoading
|
||||||
|
? _buildEmptyQuizList()
|
||||||
|
: ListView.builder(
|
||||||
|
controller: controller.scrollController,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
itemCount: controller.availableQuizzes.length + (controller.isLoading ? 1 : 0),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index < controller.availableQuizzes.length) {
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: Duration(milliseconds: 200 + (index * 50)),
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
child: QuizContainerComponent(
|
||||||
|
data: controller.availableQuizzes[index],
|
||||||
|
onTap: controller.onQuizChoosen,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return _buildLoadingIndicator();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEmptyQuizList() {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
_buildModeButton('kuismu', controller.isOnwQuiz, true),
|
Container(
|
||||||
_buildModeButton('Rekomendasi', controller.isOnwQuiz, false),
|
padding: const EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[100],
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.quiz_outlined,
|
||||||
|
size: 48,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
"Belum ada kuis tersedia",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
"Coba ganti sumber kuis atau buat kuis baru",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey[500],
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildModeButton(String label, RxBool isSelected, bool base) {
|
Widget _buildLoadingIndicator() {
|
||||||
return Expanded(
|
return Padding(
|
||||||
child: InkWell(
|
padding: const EdgeInsets.all(16.0),
|
||||||
onTap: () => controller.onQuizSourceChange(base),
|
child: Center(
|
||||||
child: Obx(
|
child: Row(
|
||||||
() => Container(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
children: [
|
||||||
decoration: BoxDecoration(
|
SizedBox(
|
||||||
color: isSelected.value == base ? AppColors.primaryBlue : Colors.transparent,
|
width: 20,
|
||||||
borderRadius: base
|
height: 20,
|
||||||
? BorderRadius.only(
|
child: CircularProgressIndicator(
|
||||||
topLeft: Radius.circular(10),
|
strokeWidth: 2,
|
||||||
bottomLeft: Radius.circular(10),
|
valueColor: AlwaysStoppedAnimation<Color>(AppColors.primaryBlue),
|
||||||
)
|
|
||||||
: 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,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
"Memuat kuis lainnya...",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildCreateRoomButton() {
|
||||||
|
return Obx(() {
|
||||||
|
final canCreate = controller.selectedQuiz.value != null && controller.nameTC.text.isNotEmpty && controller.maxPlayerTC.text.isNotEmpty;
|
||||||
|
|
||||||
|
return AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
width: MediaQuery.of(Get.context!).size.width - 32,
|
||||||
|
height: 56,
|
||||||
|
child: Material(
|
||||||
|
elevation: canCreate ? 8 : 2,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
child: InkWell(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
onTap: canCreate ? controller.onCreateRoom : null,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: canCreate
|
||||||
|
? LinearGradient(
|
||||||
|
colors: [AppColors.primaryBlue, AppColors.primaryBlue.withValues(alpha: 0.8)],
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
color: !canCreate ? Colors.grey[300] : null,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.add_circle,
|
||||||
|
color: canCreate ? Colors.white : Colors.grey[500],
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
"Buat Room Sekarang",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: canCreate ? Colors.white : Colors.grey[500],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||||
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
import 'package:quiz_app/app/const/enums/listing_type.dart';
|
||||||
import 'package:quiz_app/component/quiz_container_component.dart';
|
import 'package:quiz_app/component/quiz_container_component.dart';
|
||||||
import 'package:quiz_app/component/widget/recomendation_component.dart';
|
import 'package:quiz_app/component/widget/recomendation_component.dart';
|
||||||
|
@ -13,7 +14,7 @@ class SearchView extends GetView<SearchQuizController> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: const Color(0xFFF8F9FB),
|
backgroundColor: AppColors.background2,
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
|
|
Loading…
Reference in New Issue