feat: adding localization

This commit is contained in:
akhdanre 2025-05-07 21:45:36 +07:00
parent b575f75f6d
commit 7dc0994162
29 changed files with 530 additions and 174 deletions

View File

@ -0,0 +1,83 @@
{
"greeting_time": "Good Afternoon",
"greeting_user": "Hello {user}",
"create_room": "Create Room",
"join_room": "Join Room",
"create_quiz": "Create Quiz",
"ready_new_challenge": "Ready for a new challenge?",
"search_or_select_category": "Search or select by category",
"search_for_quizzes": "Search for quizzes...",
"quiz_recommendation": "Recommended Quiz",
"log_in": "Log In",
"sign_in": "Sign In",
"email": "Email",
"enter_your_email": "Enter Your Email",
"password": "Password",
"enter_your_password": "Enter Your Password",
"or": "OR",
"register_title": "Register",
"full_name": "Full Name",
"birth_date": "Birth Date",
"phone_optional": "Phone Number (Optional)",
"verify_password": "Verify Password",
"register_button": "Register",
"nav_home": "Home",
"nav_search": "Search",
"nav_library": "Library",
"nav_history": "History",
"nav_profile": "Profile",
"quiz_popular": "Popular Quiz",
"see_all": "See All",
"library_title": "Quiz Library",
"library_description": "A collection of quiz questions created for study.",
"no_quiz_available": "No quizzes available yet.",
"quiz_count_label": "Quizzes",
"quiz_count_named": "{total} Quizzes",
"history_title": "Quiz History",
"history_subtitle": "Review the quizzes you've taken",
"no_history": "You don't have any quiz history yet",
"score_label": "Score: {correct}/{total}",
"duration_minutes": "{minute} minutes",
"edit_profile": "Edit Profile",
"logout": "Logout",
"total_quiz": "Total Quiz",
"avg_score": "Average Score",
"history_detail_title": "Quiz Detail",
"correct_answer": "Correct",
"score": "Score",
"time_taken": "Time",
"duration_seconds": "{second}s",
"your_answer": "Your answer: {answer}",
"question_type_option": "Multiple Choice",
"question_type_fill": "Fill in the Blank",
"question_type_true_false": "True / False",
"question_type_unknown": "Unknown Type",
"enter_room_code": "Enter Room Code",
"room_code_hint": "AB123C",
"join_now": "Join Now",
"create_quiz_title": "Create Quiz",
"save_all": "Save All",
"mode_generate": "Generate",
"mode_manual": "Manual",
"quiz_play_title": "Answer Quiz",
"ready_in": "Ready in {second}",
"question_indicator": "Question {current} of {total}",
"yes": "Yes",
"no": "No",
"next": "Next",
"quiz_preview_title": "Preview Quiz",
"quiz_title_label": "Title",
"quiz_description_label": "Short Description",
"quiz_subject_label": "Subject",
"make_quiz_public": "Make Quiz Public",
"save_quiz": "Save Quiz"
}

View File

@ -0,0 +1,83 @@
{
"greeting_time": "Selamat Siang",
"greeting_user": "Halo {user}",
"create_room": "Buat Ruangan",
"join_room": "Gabung Ruangan",
"create_quiz": "Buat Kuis",
"ready_new_challenge": "Siap untuk tantangan baru?",
"search_or_select_category": "Cari atau pilih berdasarkan kategori",
"search_for_quizzes": "Cari kuis...",
"quiz_recommendation": "Rekomendasi Kuis",
"log_in": "Masuk",
"sign_in": "Masuk",
"email": "Email",
"enter_your_email": "Masukkan Email Anda",
"password": "Kata Sandi",
"enter_your_password": "Masukkan Kata Sandi Anda",
"or": "ATAU",
"register_title": "Daftar",
"full_name": "Nama Lengkap",
"birth_date": "Tanggal Lahir",
"phone_optional": "Nomor Telepon (Opsional)",
"verify_password": "Verifikasi Kata Sandi",
"register_button": "Daftar",
"nav_home": "Beranda",
"nav_search": "Cari",
"nav_library": "Pustaka",
"nav_history": "Riwayat",
"nav_profile": "Profil",
"quiz_popular": "Kuis Populer",
"see_all": "Lihat Semua",
"library_title": "Pustaka Kuis",
"library_description": "Kumpulan pertanyaan kuis untuk belajar.",
"no_quiz_available": "Belum ada kuis yang tersedia.",
"quiz_count_label": "Kuis",
"quiz_count_named": "{total} Kuis",
"history_title": "Riwayat Kuis",
"history_subtitle": "Tinjau kuis yang telah kamu kerjakan",
"no_history": "Kamu belum memiliki riwayat kuis",
"score_label": "Skor: {correct}/{total}",
"duration_minutes": "{minute} menit",
"edit_profile": "Edit Profil",
"logout": "Keluar",
"total_quiz": "Total Kuis",
"avg_score": "Skor Rata-rata",
"history_detail_title": "Detail Kuis",
"correct_answer": "Benar",
"score": "Skor",
"time_taken": "Waktu",
"duration_seconds": "{second} detik",
"your_answer": "Jawaban kamu: {answer}",
"question_type_option": "Pilihan Ganda",
"question_type_fill": "Isian Kosong",
"question_type_true_false": "Benar / Salah",
"question_type_unknown": "Tipe Tidak Dikenal",
"enter_room_code": "Masukkan Kode Ruangan",
"room_code_hint": "AB123C",
"join_now": "Gabung Sekarang",
"create_quiz_title": "Buat Kuis",
"save_all": "Simpan Semua",
"mode_generate": "Otomatis",
"mode_manual": "Manual",
"quiz_play_title": "Kerjakan Kuis",
"ready_in": "Siap dalam {second}",
"question_indicator": "Pertanyaan {current} dari {total}",
"yes": "Ya",
"no": "Tidak",
"next": "Berikutnya",
"quiz_preview_title": "Pratinjau Kuis",
"quiz_title_label": "Judul",
"quiz_description_label": "Deskripsi Singkat",
"quiz_subject_label": "Mata Pelajaran",
"make_quiz_public": "Jadikan Kuis Publik",
"save_quiz": "Simpan Kuis"
}

View File

@ -0,0 +1,83 @@
{
"greeting_time": "Selamat Tengah Hari",
"greeting_user": "Hai {user}",
"create_room": "Cipta Bilik",
"join_room": "Sertai Bilik",
"create_quiz": "Cipta Kuiz",
"ready_new_challenge": "Bersedia untuk cabaran baharu?",
"search_or_select_category": "Cari atau pilih mengikut kategori",
"search_for_quizzes": "Cari kuiz...",
"quiz_recommendation": "Kuiz Disyorkan",
"log_in": "Log Masuk",
"sign_in": "Daftar Masuk",
"email": "E-mel",
"enter_your_email": "Masukkan E-mel Anda",
"password": "Kata Laluan",
"enter_your_password": "Masukkan Kata Laluan Anda",
"or": "ATAU",
"register_title": "Daftar",
"full_name": "Nama Penuh",
"birth_date": "Tarikh Lahir",
"phone_optional": "Nombor Telefon (Pilihan)",
"verify_password": "Sahkan Kata Laluan",
"register_button": "Daftar",
"nav_home": "Laman Utama",
"nav_search": "Cari",
"nav_library": "Perpustakaan",
"nav_history": "Sejarah",
"nav_profile": "Profil",
"quiz_popular": "Kuiz Popular",
"see_all": "Lihat Semua",
"library_title": "Perpustakaan Kuiz",
"library_description": "Koleksi soalan kuiz untuk pembelajaran.",
"no_quiz_available": "Tiada kuiz tersedia lagi.",
"quiz_count_label": "Kuiz",
"quiz_count_named": "{total} Kuiz",
"history_title": "Sejarah Kuiz",
"history_subtitle": "Semak semula kuiz yang telah anda jawab",
"no_history": "Anda belum mempunyai sejarah kuiz",
"score_label": "Skor: {correct}/{total}",
"duration_minutes": "{minute} minit",
"edit_profile": "Edit Profil",
"logout": "Log Keluar",
"total_quiz": "Jumlah Kuiz",
"avg_score": "Skor Purata",
"history_detail_title": "Butiran Kuiz",
"correct_answer": "Betul",
"score": "Skor",
"time_taken": "Masa",
"duration_seconds": "{second} saat",
"your_answer": "Jawapan anda: {answer}",
"question_type_option": "Pilihan Berganda",
"question_type_fill": "Isi Tempat Kosong",
"question_type_true_false": "Betul / Salah",
"question_type_unknown": "Jenis Tidak Diketahui",
"enter_room_code": "Masukkan Kod Bilik",
"room_code_hint": "AB123C",
"join_now": "Sertai Sekarang",
"create_quiz_title": "Cipta Kuiz",
"save_all": "Simpan Semua",
"mode_generate": "Jana",
"mode_manual": "Manual",
"quiz_play_title": "Jawab Kuiz",
"ready_in": "Bersedia dalam {second}",
"question_indicator": "Soalan {current} daripada {total}",
"yes": "Ya",
"no": "Tidak",
"next": "Seterusnya",
"quiz_preview_title": "Pratonton Kuiz",
"quiz_title_label": "Tajuk",
"quiz_description_label": "Deskripsi Ringkas",
"quiz_subject_label": "Subjek",
"make_quiz_public": "Jadikan Kuiz Umum",
"save_quiz": "Simpan Kuiz"
}

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart'; import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:quiz_app/app/bindings/initial_bindings.dart'; import 'package:quiz_app/app/bindings/initial_bindings.dart';
@ -9,7 +10,10 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GetMaterialApp( return GetMaterialApp(
localizationsDelegates: context.localizationDelegates,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
supportedLocales: context.supportedLocales,
locale: context.locale,
title: 'Quiz App', title: 'Quiz App',
initialBinding: InitialBindings(), initialBinding: InitialBindings(),
initialRoute: AppRoutes.splashScreen, initialRoute: AppRoutes.splashScreen,

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart';
@ -54,7 +55,7 @@ class QuizItemWAComponent extends StatelessWidget {
const SizedBox(height: 16), const SizedBox(height: 16),
if (isOptionType && options != null) _buildOptions(), if (isOptionType && options != null) _buildOptions(),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildAnswerIndicator(), _buildAnswerIndicator(context),
const SizedBox(height: 16), const SizedBox(height: 16),
const Divider(height: 24, color: AppColors.shadowPrimary), const Divider(height: 24, color: AppColors.shadowPrimary),
_buildMetadata(), _buildMetadata(),
@ -109,7 +110,7 @@ class QuizItemWAComponent extends StatelessWidget {
); );
} }
Widget _buildAnswerIndicator() { Widget _buildAnswerIndicator(BuildContext context) {
final icon = isCorrect ? LucideIcons.checkCircle2 : LucideIcons.xCircle; final icon = isCorrect ? LucideIcons.checkCircle2 : LucideIcons.xCircle;
final color = isCorrect ? AppColors.primaryBlue : Colors.red; final color = isCorrect ? AppColors.primaryBlue : Colors.red;
@ -124,7 +125,7 @@ class QuizItemWAComponent extends StatelessWidget {
Icon(icon, color: color, size: 18), Icon(icon, color: color, size: 18),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Jawabanmu: $userAnswerText', context.tr('your_answer', namedArgs: {'answer': userAnswerText}),
style: AppTextStyles.statValue, style: AppTextStyles.statValue,
), ),
], ],

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:quiz_app/component/quiz_container_component.dart'; import 'package:quiz_app/component/quiz_container_component.dart';
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
@ -21,7 +22,7 @@ class RecomendationComponent extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildSectionTitle(title), _buildSectionTitle(context, title),
const SizedBox(height: 10), const SizedBox(height: 10),
datas.isNotEmpty datas.isNotEmpty
// ? Text("yeay ${datas.length}") // ? Text("yeay ${datas.length}")
@ -53,7 +54,7 @@ class RecomendationComponent extends StatelessWidget {
// ); // );
// } // }
Widget _buildSectionTitle(String title) { Widget _buildSectionTitle(BuildContext context, String title) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0), padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row( child: Row(
@ -66,7 +67,7 @@ class RecomendationComponent extends StatelessWidget {
GestureDetector( GestureDetector(
onTap: allOnTap, onTap: allOnTap,
child: Text( child: Text(
"Lihat semua", context.tr('see_all'),
style: TextStyle(fontSize: 14, color: Colors.blue.shade700), style: TextStyle(fontSize: 14, color: Colors.blue.shade700),
), ),
), ),

View File

@ -1,6 +1,6 @@
class APIEndpoint { class APIEndpoint {
// static const String baseUrl = "http://192.168.1.9:5000"; static const String baseUrl = "http://192.168.1.9:5000";
static const String baseUrl = "http://172.16.106.133:5000"; // static const String baseUrl = "http://172.16.106.133:5000";
static const String api = "$baseUrl/api"; static const String api = "$baseUrl/api";
static const String login = "/login"; static const String login = "/login";

View File

@ -1,3 +1,4 @@
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/colors/app_colors.dart';
@ -81,6 +82,7 @@ class DetailQuizView extends GetView<DetailQuizController> {
const SizedBox(height: 20), const SizedBox(height: 20),
const Divider(thickness: 1.2, color: AppColors.borderLight), const Divider(thickness: 1.2, color: AppColors.borderLight),
const SizedBox(height: 20), const SizedBox(height: 20),
// Soal Section // Soal Section
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
@ -169,13 +171,13 @@ class DetailQuizView extends GetView<DetailQuizController> {
String _mapQuestionTypeToText(String? type) { String _mapQuestionTypeToText(String? type) {
switch (type) { switch (type) {
case 'option': case 'option':
return 'Pilihan Ganda'; return tr('question_type_option');
case 'fill_the_blank': case 'fill_the_blank':
return 'Isian Kosong'; return tr('question_type_fill');
case 'true_false': case 'true_false':
return 'Benar / Salah'; return tr('question_type_true_false');
default: default:
return 'Tipe Tidak Diketahui'; return tr('question_type_unknown');
} }
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get_state_manager/get_state_manager.dart'; import 'package:get/get_state_manager/get_state_manager.dart';
import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
@ -18,7 +19,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
backgroundColor: AppColors.background, backgroundColor: AppColors.background,
elevation: 0, elevation: 0,
title: Text( title: Text(
'Detail history', context.tr('history_detail_title'),
style: AppTextStyles.title.copyWith(fontSize: 24), style: AppTextStyles.title.copyWith(fontSize: 24),
), ),
centerTitle: true, centerTitle: true,
@ -31,7 +32,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
} }
return ListView( return ListView(
children: [ children: [
quizMetaInfo(), quizMetaInfo(context),
...quizListings(), ...quizListings(),
], ],
); );
@ -55,7 +56,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
.toList(); .toList();
} }
Widget quizMetaInfo() { Widget quizMetaInfo(BuildContext context) {
final quiz = controller.quizAnswer; final quiz = controller.quizAnswer;
return Container( return Container(
@ -106,20 +107,20 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
children: [ children: [
_buildStatItem( _buildStatItem(
icon: LucideIcons.checkCircle2, icon: LucideIcons.checkCircle2,
label: 'Benar', label: context.tr('correct_answer'),
value: "${quiz.totalCorrect}/${quiz.questionListings.length}", value: "${quiz.totalCorrect}/${quiz.questionListings.length}",
color: Colors.green, color: Colors.green,
), ),
_buildStatItem( _buildStatItem(
icon: LucideIcons.award, icon: LucideIcons.award,
label: 'Skor', label: context.tr('score'),
value: quiz.totalScore.toString(), value: quiz.totalScore.toString(),
color: Colors.blueAccent, color: Colors.blueAccent,
), ),
_buildStatItem( _buildStatItem(
icon: LucideIcons.clock3, icon: LucideIcons.clock3,
label: 'Waktu', label: context.tr('time_taken'),
value: '${quiz.totalSolveTime}s', value: tr('duration_seconds', namedArgs: {"second": quiz.totalSolveTime.toString()}),
color: Colors.orange, color: Colors.orange,
), ),
], ],

View File

@ -1,3 +1,4 @@
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/colors/app_colors.dart';
@ -19,10 +20,10 @@ class HistoryView extends GetView<HistoryController> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Riwayat Kuis", style: AppTextStyles.title.copyWith(fontSize: 24)), Text(context.tr("history_title"), style: AppTextStyles.title.copyWith(fontSize: 24)),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
"Lihat kembali hasil kuis yang telah kamu kerjakan", context.tr("history_subtitle"),
style: AppTextStyles.subtitle, style: AppTextStyles.subtitle,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
@ -38,7 +39,7 @@ class HistoryView extends GetView<HistoryController> {
if (historyList.isEmpty) { if (historyList.isEmpty) {
return Expanded( return Expanded(
child: Center( child: Center(
child: Text("You don't have any quiz history yet", style: AppTextStyles.body), child: Text(context.tr("no_history"), style: AppTextStyles.body),
), ),
); );
} }
@ -102,13 +103,13 @@ class HistoryView extends GetView<HistoryController> {
const Icon(Icons.check_circle, size: 14, color: Colors.green), const Icon(Icons.check_circle, size: 14, color: Colors.green),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
"Skor: ${item.totalCorrect}/${item.totalQuestion}", tr('score_label', namedArgs: {'correct': item.totalCorrect.toString(), 'total': item.totalQuestion.toString()}),
style: AppTextStyles.caption, style: AppTextStyles.caption,
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
const Icon(Icons.timer, size: 14, color: Colors.grey), const Icon(Icons.timer, size: 14, color: Colors.grey),
const SizedBox(width: 4), const SizedBox(width: 4),
Text("3 menit", style: AppTextStyles.caption), Text(tr("duration_minutes", namedArgs: {"minute": "3"}), style: AppTextStyles.caption),
], ],
), ),
], ],

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ButtonOption extends StatelessWidget { class ButtonOption extends StatelessWidget {
@ -19,22 +20,22 @@ class ButtonOption extends StatelessWidget {
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 20), margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
child: Row( child: Row(
children: [ children: [
Expanded(child: _buildCreateButton()), Expanded(child: _buildCreateButton(context)),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded(child: _buildRoomButtons()), Expanded(child: _buildRoomButtons(context)),
], ],
), ),
); );
} }
Widget _buildCreateButton() { Widget _buildCreateButton(BuildContext context) {
return InkWell( return InkWell(
onTap: onCreate, onTap: onCreate,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: SizedBox( child: SizedBox(
height: double.infinity, height: double.infinity,
child: _buildButtonContainer( child: _buildButtonContainer(
label: 'Buat Quiz', label: context.tr("create_quiz"),
gradientColors: [Color(0xFF0052CC), Color(0xFF0367D3)], gradientColors: [Color(0xFF0052CC), Color(0xFF0367D3)],
icon: Icons.create, icon: Icons.create,
), ),
@ -42,7 +43,7 @@ class ButtonOption extends StatelessWidget {
); );
} }
Widget _buildRoomButtons() { Widget _buildRoomButtons(BuildContext context) {
return Column( return Column(
children: [ children: [
Expanded( Expanded(
@ -50,7 +51,7 @@ class ButtonOption extends StatelessWidget {
onTap: onCreateRoom, onTap: onCreateRoom,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: _buildButtonContainer( child: _buildButtonContainer(
label: 'Buat Room', label: context.tr("create_room"),
gradientColors: [Color(0xFF36B37E), Color(0xFF22C39F)], gradientColors: [Color(0xFF36B37E), Color(0xFF22C39F)],
icon: Icons.meeting_room, icon: Icons.meeting_room,
), ),
@ -62,7 +63,7 @@ class ButtonOption extends StatelessWidget {
onTap: onJoinRoom, onTap: onJoinRoom,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: _buildButtonContainer( child: _buildButtonContainer(
label: 'Join Room', label: context.tr("join_room"),
gradientColors: [Color(0xFFFFAB00), Color(0xFFFFC107)], gradientColors: [Color(0xFFFFAB00), Color(0xFFFFC107)],
icon: Icons.group, icon: Icons.group,
), ),

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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/data/models/subject/subject_model.dart'; import 'package:quiz_app/data/models/subject/subject_model.dart';
@ -27,22 +28,22 @@ class SearchComponent extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildTitleSection(), _buildTitleSection(context),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildCategoryRow(), _buildCategoryRow(),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildSearchInput(), _buildSearchInput(context),
], ],
), ),
); );
} }
Widget _buildTitleSection() { Widget _buildTitleSection(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: const [ children: [
Text( Text(
"Ready for a new challenge?", context.tr("ready_new_challenge"),
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -51,7 +52,7 @@ class SearchComponent extends StatelessWidget {
), ),
SizedBox(height: 5), SizedBox(height: 5),
Text( Text(
"Search or select by category", context.tr("search_or_select_category"),
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Color(0xFF6B778C), // Soft gray text color: Color(0xFF6B778C), // Soft gray text
@ -98,7 +99,7 @@ class SearchComponent extends StatelessWidget {
); );
} }
Widget _buildSearchInput() { Widget _buildSearchInput(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () => onSearchTap(), onTap: () => onSearchTap(),
child: Container( child: Container(
@ -115,11 +116,11 @@ class SearchComponent extends StatelessWidget {
], ],
), ),
child: Row( child: Row(
children: const [ children: [
Icon(Icons.search, color: Color(0xFF6B778C)), Icon(Icons.search, color: Color(0xFF6B778C)),
SizedBox(width: 8), SizedBox(width: 8),
Text( Text(
"Search for quizzes...", context.tr("search_for_quizzes"),
style: TextStyle( style: TextStyle(
color: Color(0xFF6B778C), color: Color(0xFF6B778C),
fontSize: 16, fontSize: 16,

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class UserGretingsComponent extends StatelessWidget { class UserGretingsComponent extends StatelessWidget {
@ -34,11 +35,11 @@ class UserGretingsComponent extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Selamat Siang", context.tr("greeting_time"),
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
Text( Text(
"Hello $userName", context.tr("greeting_user", namedArgs: {"user": userName}),
style: TextStyle(fontWeight: FontWeight.w500), style: TextStyle(fontWeight: FontWeight.w500),
), ),
], ],

View File

@ -1,3 +1,4 @@
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/colors/app_colors.dart';
@ -50,7 +51,7 @@ class HomeView extends GetView<HomeController> {
const SizedBox(height: 20), const SizedBox(height: 20),
Obx( Obx(
() => RecomendationComponent( () => RecomendationComponent(
title: "Quiz Rekomendasi", title: context.tr("quiz_recommendation"),
datas: controller.data.toList(), datas: controller.data.toList(),
itemOnTap: controller.onRecommendationTap, itemOnTap: controller.onRecommendationTap,
allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation), allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation),

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:easy_localization/easy_localization.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/component/global_button.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';
@ -41,9 +41,9 @@ class JoinRoomView extends GetView<JoinRoomController> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
"Masukkan Kode Room", context.tr("enter_room_code"),
style: TextStyle( style: const TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: Colors.black87, color: Colors.black87,
@ -52,18 +52,12 @@ class JoinRoomView extends GetView<JoinRoomController> {
const SizedBox(height: 16), const SizedBox(height: 16),
GlobalTextField( GlobalTextField(
controller: controller.codeController, controller: controller.codeController,
hintText: "AB123C", hintText: context.tr("room_code_hint"),
textInputType: TextInputType.text, textInputType: TextInputType.text,
// Uncomment if needed:
// maxLength: 6,
// inputFormatters: [
// FilteringTextInputFormatter.allow(RegExp(r'[A-Z0-9]')),
// UpperCaseTextFormatter(),
// ],
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
GlobalButton( GlobalButton(
text: "Gabung Sekarang", text: context.tr("join_now"),
onPressed: controller.joinRoom, onPressed: controller.joinRoom,
), ),
], ],

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:quiz_app/component/widget/loading_widget.dart'; import 'package:quiz_app/component/widget/loading_widget.dart';
import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart'; import 'package:quiz_app/data/models/quiz/quiz_listing_model.dart';
import 'package:quiz_app/feature/library/controller/library_controller.dart'; import 'package:quiz_app/feature/library/controller/library_controller.dart';
@ -17,18 +18,18 @@ class LibraryView extends GetView<LibraryController> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Library Soal', context.tr('library_title'),
style: TextStyle( style: const TextStyle(
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 24, fontSize: 24,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
const Text( Text(
"Kumpulan soal-soal kuis yang sudah dibuat untuk dipelajari.", context.tr('library_description'),
style: TextStyle( style: const TextStyle(
color: Colors.grey, color: Colors.grey,
fontSize: 14, fontSize: 14,
), ),
@ -41,10 +42,10 @@ class LibraryView extends GetView<LibraryController> {
} }
if (controller.quizs.isEmpty) { if (controller.quizs.isEmpty) {
return const Center( return Center(
child: Text( child: Text(
"Belum ada soal tersedia.", context.tr('no_quiz_available'),
style: TextStyle(color: Colors.grey, fontSize: 14), style: const TextStyle(color: Colors.grey, fontSize: 14),
), ),
); );
} }
@ -53,7 +54,10 @@ class LibraryView extends GetView<LibraryController> {
itemCount: controller.quizs.length, itemCount: controller.quizs.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final quiz = controller.quizs[index]; final quiz = controller.quizs[index];
return InkWell(onTap: () => controller.goToDetail(index), child: _buildQuizCard(quiz)); return InkWell(
onTap: () => controller.goToDetail(index),
child: _buildQuizCard(context, quiz),
);
}, },
); );
}), }),
@ -65,7 +69,7 @@ class LibraryView extends GetView<LibraryController> {
); );
} }
Widget _buildQuizCard(QuizListingModel quiz) { Widget _buildQuizCard(BuildContext context, QuizListingModel quiz) {
return Container( return Container(
margin: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
@ -74,7 +78,7 @@ class LibraryView extends GetView<LibraryController> {
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.05), color: Colors.black.withOpacity(0.05),
blurRadius: 6, blurRadius: 6,
offset: const Offset(0, 2), offset: const Offset(0, 2),
), ),
@ -129,7 +133,7 @@ class LibraryView extends GetView<LibraryController> {
const Icon(Icons.list, size: 14, color: Colors.grey), const Icon(Icons.list, size: 14, color: Colors.grey),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
'${quiz.totalQuiz} Quizzes', context.tr('quiz_count_named', namedArgs: {'total': quiz.totalQuiz.toString()}),
style: const TextStyle(fontSize: 12, color: Colors.grey), style: const TextStyle(fontSize: 12, color: Colors.grey),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),

View File

@ -1,3 +1,4 @@
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/colors/app_colors.dart';
@ -27,7 +28,12 @@ class ListingsQuizView extends GetView<ListingQuizController> {
} }
if (controller.quizzes.isEmpty) { if (controller.quizzes.isEmpty) {
return const Center(child: Text('Tidak ada kuis tersedia.')); return Center(
child: Text(
context.tr('no_quiz_available'),
textAlign: TextAlign.center,
),
);
} }
return ListView.builder( return ListView.builder(

View File

@ -1,3 +1,4 @@
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/colors/app_colors.dart';
@ -24,26 +25,26 @@ class LoginView extends GetView<LoginController> {
const SizedBox(height: 40), const SizedBox(height: 40),
const AppName(), const AppName(),
const SizedBox(height: 40), const SizedBox(height: 40),
const LabelTextField( LabelTextField(
label: "Log In", label: context.tr("log_in"),
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Color(0xFF172B4D), color: Color(0xFF172B4D),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
const LabelTextField( LabelTextField(
label: "Email", label: context.tr("email"),
color: Color(0xFF6B778C), color: Color(0xFF6B778C),
fontSize: 14, fontSize: 14,
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
GlobalTextField( GlobalTextField(
controller: controller.emailController, controller: controller.emailController,
hintText: "Masukkan email anda", hintText: context.tr("enter_your_email"),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
const LabelTextField( LabelTextField(
label: "Password", label: context.tr("password"),
color: Color(0xFF6B778C), color: Color(0xFF6B778C),
fontSize: 14, fontSize: 14,
), ),
@ -54,18 +55,18 @@ class LoginView extends GetView<LoginController> {
isPassword: true, isPassword: true,
obscureText: controller.isPasswordHidden.value, obscureText: controller.isPasswordHidden.value,
onToggleVisibility: controller.togglePasswordVisibility, onToggleVisibility: controller.togglePasswordVisibility,
hintText: "Masukkan password anda", hintText: context.tr("enter_your_password"),
), ),
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
Obx(() => GlobalButton( Obx(() => GlobalButton(
onPressed: controller.loginWithEmail, onPressed: controller.loginWithEmail,
text: "Masuk", text: context.tr("sign_in"),
type: controller.isButtonEnabled.value, type: controller.isButtonEnabled.value,
)), )),
const SizedBox(height: 24), const SizedBox(height: 24),
const LabelTextField( LabelTextField(
label: "OR", label: context.tr("or"),
alignment: Alignment.center, alignment: Alignment.center,
color: Color(0xFF6B778C), color: Color(0xFF6B778C),
), ),

View File

@ -1,3 +1,4 @@
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/feature/history/view/history_view.dart'; import 'package:quiz_app/feature/history/view/history_view.dart';
@ -31,29 +32,29 @@ class NavbarView extends GetView<NavigationController> {
}), }),
bottomNavigationBar: Obx( bottomNavigationBar: Obx(
() => BottomNavigationBar( () => BottomNavigationBar(
type: BottomNavigationBarType.fixed, // <=== ini tambahan penting! type: BottomNavigationBarType.fixed,
currentIndex: controller.selectedIndex.value, currentIndex: controller.selectedIndex.value,
onTap: controller.changePage, onTap: controller.changePage,
items: const [ items: [
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.home), icon: const Icon(Icons.home),
label: 'Home', label: context.tr('nav_home'),
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.search), icon: const Icon(Icons.search),
label: 'Search', label: context.tr('nav_search'),
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.menu_book), icon: const Icon(Icons.menu_book),
label: 'Library', label: context.tr('nav_library'),
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.history), icon: const Icon(Icons.history),
label: 'History', label: context.tr('nav_history'),
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: Icon(Icons.person), icon: const Icon(Icons.person),
label: 'Profile', label: context.tr('nav_profile'),
), ),
], ],
), ),

View File

@ -54,7 +54,6 @@ class PlayQuizMultiplayerView extends GetView<PlayQuizMultiplayerController> {
if (question.type == 'option') _buildOptionQuestion(), if (question.type == 'option') _buildOptionQuestion(),
if (question.type == 'fill_in_the_blank') _buildFillInBlankQuestion(), if (question.type == 'fill_in_the_blank') _buildFillInBlankQuestion(),
if (question.type == 'true_false') _buildTrueFalseQuestion(), if (question.type == 'true_false') _buildTrueFalseQuestion(),
const Spacer(), const Spacer(),
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,

View File

@ -1,3 +1,4 @@
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/feature/profile/controller/profile_controller.dart'; import 'package:quiz_app/feature/profile/controller/profile_controller.dart';
@ -28,11 +29,20 @@ class ProfileView extends GetView<ProfileController> {
style: const TextStyle(fontSize: 14, color: Colors.grey), style: const TextStyle(fontSize: 14, color: Colors.grey),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
_buildStats(), _buildStats(context),
const SizedBox(height: 32), const SizedBox(height: 32),
_buildActionButton("Edit Profil", Icons.edit, controller.editProfile), _buildActionButton(
context.tr("edit_profile"),
Icons.edit,
controller.editProfile,
),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildActionButton("Logout", Icons.logout, controller.logout, isDestructive: true), _buildActionButton(
context.tr("logout"),
Icons.logout,
controller.logout,
isDestructive: true,
),
], ],
); );
}), }),
@ -57,7 +67,7 @@ class ProfileView extends GetView<ProfileController> {
} }
} }
Widget _buildStats() { Widget _buildStats(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -74,9 +84,15 @@ class ProfileView extends GetView<ProfileController> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildStatItem("Total Quiz", controller.totalQuizzes.value.toString()), _buildStatItem(
context.tr("total_quiz"),
controller.totalQuizzes.value.toString(),
),
const SizedBox(width: 16), const SizedBox(width: 16),
_buildStatItem("Skor Rata-rata", "${controller.avgScore.value}%"), _buildStatItem(
context.tr("avg_score"),
"${controller.avgScore.value}%",
),
], ],
), ),
); );

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:easy_localization/easy_localization.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/component/global_button.dart'; import 'package:quiz_app/component/global_button.dart';
import 'package:quiz_app/feature/quiz_creation/controller/quiz_creation_controller.dart'; import 'package:quiz_app/feature/quiz_creation/controller/quiz_creation_controller.dart';
@ -16,9 +17,9 @@ class QuizCreationView extends GetView<QuizCreationController> {
appBar: AppBar( appBar: AppBar(
backgroundColor: AppColors.background, backgroundColor: AppColors.background,
elevation: 0, elevation: 0,
title: const Text( title: Text(
'Create Quiz', context.tr('create_quiz_title'),
style: TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.darkText, color: AppColors.darkText,
), ),
@ -36,13 +37,14 @@ class QuizCreationView extends GetView<QuizCreationController> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildModeSelector(), _buildModeSelector(context),
const SizedBox(height: 20), const SizedBox(height: 20),
Obx( Obx(() => controller.isGenerate.value ? const GenerateComponent() : const CustomQuestionComponent()),
() => controller.isGenerate.value ? GenerateComponent() : CustomQuestionComponent(),
),
const SizedBox(height: 30), const SizedBox(height: 30),
GlobalButton(text: "simpan semua", onPressed: controller.onDone) GlobalButton(
text: context.tr('save_all'),
onPressed: controller.onDone,
)
], ],
), ),
), ),
@ -51,7 +53,7 @@ class QuizCreationView extends GetView<QuizCreationController> {
); );
} }
Widget _buildModeSelector() { Widget _buildModeSelector(BuildContext context) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.background, color: AppColors.background,
@ -60,8 +62,8 @@ class QuizCreationView extends GetView<QuizCreationController> {
), ),
child: Row( child: Row(
children: [ children: [
_buildModeButton('Generate', controller.isGenerate, true), _buildModeButton(context.tr('mode_generate'), controller.isGenerate, true),
_buildModeButton('Manual', controller.isGenerate, false), _buildModeButton(context.tr('mode_manual'), controller.isGenerate, false),
], ],
), ),
); );
@ -71,17 +73,18 @@ class QuizCreationView extends GetView<QuizCreationController> {
return Expanded( return Expanded(
child: InkWell( child: InkWell(
onTap: () => controller.onCreationTypeChange(base), onTap: () => controller.onCreationTypeChange(base),
child: Obx( child: Obx(() {
() => Container( final selected = isSelected.value == base;
return Container(
padding: const EdgeInsets.symmetric(vertical: 14), padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected.value == base ? AppColors.primaryBlue : Colors.transparent, color: selected ? AppColors.primaryBlue : Colors.transparent,
borderRadius: base borderRadius: base
? BorderRadius.only( ? const BorderRadius.only(
topLeft: Radius.circular(10), topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10), bottomLeft: Radius.circular(10),
) )
: BorderRadius.only( : const BorderRadius.only(
topRight: Radius.circular(10), topRight: Radius.circular(10),
bottomRight: Radius.circular(10), bottomRight: Radius.circular(10),
), ),
@ -90,12 +93,12 @@ class QuizCreationView extends GetView<QuizCreationController> {
child: Text( child: Text(
label, label,
style: TextStyle( style: TextStyle(
color: isSelected.value == base ? Colors.white : AppColors.softGrayText, color: selected ? Colors.white : AppColors.softGrayText,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
), );
), }),
), ),
); );
} }

View File

@ -1,3 +1,4 @@
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/colors/app_colors.dart';
@ -18,27 +19,26 @@ class QuizPlayView extends GetView<QuizPlayController> {
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Obx(() { child: Obx(() {
if (!controller.isStarting.value) { if (!controller.isStarting.value) {
return Center( return Text(
child: Text( context.tr('ready_in', namedArgs: {'second': controller.prepareDuration.toString()}),
"Ready in ${controller.prepareDuration}", style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), );
));
} }
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildCustomAppBar(), _buildCustomAppBar(context),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildProgressBar(), _buildProgressBar(),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildQuestionIndicator(), _buildQuestionIndicator(context),
const SizedBox(height: 12), const SizedBox(height: 12),
_buildQuestionText(), _buildQuestionText(),
const SizedBox(height: 30), const SizedBox(height: 30),
_buildAnswerSection(), _buildAnswerSection(context),
const Spacer(), const Spacer(),
_buildNextButton(), _buildNextButton(context),
], ],
); );
}), }),
@ -47,7 +47,7 @@ class QuizPlayView extends GetView<QuizPlayController> {
); );
} }
Widget _buildCustomAppBar() { Widget _buildCustomAppBar(BuildContext context) {
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: const BoxDecoration( decoration: const BoxDecoration(
@ -55,9 +55,9 @@ class QuizPlayView extends GetView<QuizPlayController> {
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: const [ children: [
Text( Text(
'Kerjakan Soal', context.tr('quiz_play_title'),
style: TextStyle( style: TextStyle(
color: Colors.black, color: Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@ -79,9 +79,15 @@ class QuizPlayView extends GetView<QuizPlayController> {
); );
} }
Widget _buildQuestionIndicator() { Widget _buildQuestionIndicator(BuildContext context) {
return Text( return Text(
'Soal ${controller.currentIndex.value + 1} dari ${controller.quizData.questionListings.length}', context.tr(
'question_indicator',
namedArgs: {
'current': (controller.currentIndex.value + 1).toString(),
'total': controller.quizData.questionListings.length.toString(),
},
),
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
color: Colors.grey, color: Colors.grey,
@ -101,7 +107,7 @@ class QuizPlayView extends GetView<QuizPlayController> {
); );
} }
Widget _buildAnswerSection() { Widget _buildAnswerSection(BuildContext context) {
final question = controller.currentQuestion; final question = controller.currentQuestion;
if (question is OptionQuestion) { if (question is OptionQuestion) {
@ -131,8 +137,8 @@ class QuizPlayView extends GetView<QuizPlayController> {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
_buildTrueFalseButton('Ya', true, controller.choosenAnswerTOF), _buildTrueFalseButton(context.tr('yes'), true, controller.choosenAnswerTOF),
_buildTrueFalseButton('Tidak', false, controller.choosenAnswerTOF), _buildTrueFalseButton(context.tr('no'), false, controller.choosenAnswerTOF),
], ],
); );
} else { } else {
@ -159,7 +165,7 @@ class QuizPlayView extends GetView<QuizPlayController> {
}); });
} }
Widget _buildNextButton() { Widget _buildNextButton(BuildContext context) {
return Obx(() { return Obx(() {
final isEnabled = controller.isAnswerSelected.value; final isEnabled = controller.isAnswerSelected.value;
@ -171,8 +177,8 @@ class QuizPlayView extends GetView<QuizPlayController> {
minimumSize: const Size(double.infinity, 50), minimumSize: const Size(double.infinity, 50),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
), ),
child: const Text( child: Text(
'Next', context.tr('next'),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
), ),
); );

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:easy_localization/easy_localization.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/component/global_button.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';
@ -15,25 +16,25 @@ class QuizPreviewPage extends GetView<QuizPreviewController> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: AppColors.background, backgroundColor: AppColors.background,
appBar: _buildAppBar(), appBar: _buildAppBar(context),
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
child: _buildContent(), child: _buildContent(context),
), ),
), ),
); );
} }
PreferredSizeWidget _buildAppBar() { PreferredSizeWidget _buildAppBar(BuildContext context) {
return AppBar( return AppBar(
backgroundColor: AppColors.background, backgroundColor: AppColors.background,
elevation: 0, elevation: 0,
centerTitle: true, centerTitle: true,
iconTheme: const IconThemeData(color: AppColors.darkText), iconTheme: const IconThemeData(color: AppColors.darkText),
title: const Text( title: Text(
'Preview Quiz', context.tr('quiz_preview_title'),
style: TextStyle( style: const TextStyle(
color: AppColors.darkText, color: AppColors.darkText,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@ -41,25 +42,25 @@ class QuizPreviewPage extends GetView<QuizPreviewController> {
); );
} }
Widget _buildContent() { Widget _buildContent(BuildContext context) {
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const LabelTextField(label: "Judul"), LabelTextField(label: context.tr("quiz_title_label")),
GlobalTextField(controller: controller.titleController), GlobalTextField(controller: controller.titleController),
const SizedBox(height: 20), const SizedBox(height: 20),
const LabelTextField(label: "Deskripsi Singkat"), LabelTextField(label: context.tr("quiz_description_label")),
GlobalTextField(controller: controller.descriptionController), GlobalTextField(controller: controller.descriptionController),
const SizedBox(height: 20), const SizedBox(height: 20),
const LabelTextField(label: "Mata Pelajaran"), LabelTextField(label: context.tr("quiz_subject_label")),
Obx(() => SubjectDropdownComponent( Obx(() => SubjectDropdownComponent(
data: controller.subjects.toList(), data: controller.subjects.toList(),
onItemTap: controller.onSubjectTap, onItemTap: controller.onSubjectTap,
selectedIndex: controller.subjectIndex.value, selectedIndex: controller.subjectIndex.value,
)), )),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildPublicCheckbox(), _buildPublicCheckbox(context),
const SizedBox(height: 30), const SizedBox(height: 30),
const Divider(thickness: 1.2, color: AppColors.borderLight), const Divider(thickness: 1.2, color: AppColors.borderLight),
const SizedBox(height: 20), const SizedBox(height: 20),
@ -67,7 +68,7 @@ class QuizPreviewPage extends GetView<QuizPreviewController> {
const SizedBox(height: 30), const SizedBox(height: 30),
GlobalButton( GlobalButton(
onPressed: controller.onSaveQuiz, onPressed: controller.onSaveQuiz,
text: "Simpan Kuis", text: context.tr("save_quiz"),
), ),
], ],
), ),
@ -83,7 +84,7 @@ class QuizPreviewPage extends GetView<QuizPreviewController> {
); );
} }
Widget _buildPublicCheckbox() { Widget _buildPublicCheckbox(BuildContext context) {
return Obx(() => GestureDetector( return Obx(() => GestureDetector(
onTap: controller.isPublic.toggle, onTap: controller.isPublic.toggle,
child: Row( child: Row(
@ -96,9 +97,9 @@ class QuizPreviewPage extends GetView<QuizPreviewController> {
onChanged: (val) => controller.isPublic.value = val ?? false, onChanged: (val) => controller.isPublic.value = val ?? false,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
const Text( Text(
"Buat Kuis Public", context.tr("make_quiz_public"),
style: TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
color: AppColors.darkText, color: AppColors.darkText,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:easy_localization/easy_localization.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/component/app_name.dart'; import 'package:quiz_app/component/app_name.dart';
import 'package:quiz_app/component/global_button.dart'; import 'package:quiz_app/component/global_button.dart';
@ -9,6 +10,7 @@ import 'package:quiz_app/feature/register/controller/register_controller.dart';
class RegisterView extends GetView<RegisterController> { class RegisterView extends GetView<RegisterController> {
const RegisterView({super.key}); const RegisterView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -22,47 +24,52 @@ class RegisterView extends GetView<RegisterController> {
padding: const EdgeInsets.symmetric(vertical: 40), padding: const EdgeInsets.symmetric(vertical: 40),
child: AppName(), child: AppName(),
), ),
LabelTextField(label: "Register", fontSize: 24), LabelTextField(
label: context.tr('register_title'),
fontSize: 24,
),
const SizedBox(height: 10), const SizedBox(height: 10),
LabelTextField(label: "Full Name"), LabelTextField(label: context.tr('full_name')),
GlobalTextField(controller: controller.nameController), GlobalTextField(controller: controller.nameController),
const SizedBox(height: 10), const SizedBox(height: 10),
LabelTextField(label: "Email"), LabelTextField(label: context.tr('email')),
GlobalTextField(controller: controller.emailController), GlobalTextField(controller: controller.emailController),
const SizedBox(height: 10), const SizedBox(height: 10),
LabelTextField(label: "Birth Date"), LabelTextField(label: context.tr('birth_date')),
GlobalTextField( GlobalTextField(
controller: controller.bDateController, controller: controller.bDateController,
hintText: "12-08-2001", hintText: "12-08-2001",
), ),
LabelTextField(label: "Nomer Telepon (Opsional)"), LabelTextField(label: context.tr('phone_optional')),
GlobalTextField( GlobalTextField(
controller: controller.phoneController, controller: controller.phoneController,
hintText: "085708570857", hintText: "085708570857",
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
LabelTextField(label: "Password"), LabelTextField(label: context.tr('password')),
Obx( Obx(
() => GlobalTextField( () => GlobalTextField(
controller: controller.passwordController, controller: controller.passwordController,
isPassword: true, isPassword: true,
obscureText: controller.isPasswordHidden.value, obscureText: controller.isPasswordHidden.value,
onToggleVisibility: controller.togglePasswordVisibility), onToggleVisibility: controller.togglePasswordVisibility,
),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
LabelTextField(label: "Verify Password"), LabelTextField(label: context.tr('verify_password')),
Obx( Obx(
() => GlobalTextField( () => GlobalTextField(
controller: controller.confirmPasswordController, controller: controller.confirmPasswordController,
isPassword: true, isPassword: true,
obscureText: controller.isConfirmPasswordHidden.value, obscureText: controller.isConfirmPasswordHidden.value,
onToggleVisibility: controller.toggleConfirmPasswordVisibility), onToggleVisibility: controller.toggleConfirmPasswordVisibility,
),
), ),
const SizedBox(height: 40), const SizedBox(height: 40),
GlobalButton( GlobalButton(
onPressed: controller.onRegister, onPressed: controller.onRegister,
text: "Register", text: context.tr('register_button'),
) ),
], ],
), ),
), ),

View File

@ -1,3 +1,4 @@
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/enums/listing_type.dart'; import 'package:quiz_app/app/const/enums/listing_type.dart';
@ -31,7 +32,7 @@ class SearchView extends GetView<SearchQuizController> {
] else ...[ ] else ...[
Obx( Obx(
() => RecomendationComponent( () => RecomendationComponent(
title: "Quiz Rekomendasi", title: context.tr('quiz_recommendation'),
datas: controller.recommendationQData.toList(), datas: controller.recommendationQData.toList(),
itemOnTap: controller.goToDetailPage, itemOnTap: controller.goToDetailPage,
allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation), allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation),
@ -40,7 +41,7 @@ class SearchView extends GetView<SearchQuizController> {
const SizedBox(height: 30), const SizedBox(height: 30),
Obx( Obx(
() => RecomendationComponent( () => RecomendationComponent(
title: "Quiz Populer", title: context.tr('quiz_popular'),
datas: controller.recommendationQData.toList(), datas: controller.recommendationQData.toList(),
itemOnTap: controller.goToDetailPage, itemOnTap: controller.goToDetailPage,
allOnTap: () => controller.goToListingsQuizPage(ListingType.populer), allOnTap: () => controller.goToListingsQuizPage(ListingType.populer),

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:quiz_app/app/app.dart'; import 'package:quiz_app/app/app.dart';
@ -14,7 +15,21 @@ void main() {
DeviceOrientation.portraitDown, DeviceOrientation.portraitDown,
]); ]);
runApp(MyApp()); WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
runApp(
EasyLocalization(
supportedLocales: [
Locale('en', 'US'),
Locale('id', 'ID'),
],
path: 'assets/translations',
fallbackLocale: Locale('en', 'US'),
child: MyApp(),
),
);
}, (e, stackTrace) { }, (e, stackTrace) {
logC.e("issue message $e || $stackTrace"); logC.e("issue message $e || $stackTrace");
}); });

View File

@ -1,6 +1,14 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -73,6 +81,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
easy_localization:
dependency: "direct main"
description:
name: easy_localization
sha256: "0f5239c7b8ab06c66440cfb0e9aa4b4640429c6668d5a42fe389c5de42220b12"
url: "https://pub.dev"
source: hosted
version: "3.0.7+1"
easy_logger:
dependency: transitive
description:
name: easy_logger
sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7
url: "https://pub.dev"
source: hosted
version: "0.0.2"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -118,6 +142,11 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.0"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -208,6 +237,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.2" version: "4.1.2"
intl:
dependency: transitive
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:

View File

@ -43,6 +43,7 @@ dependencies:
lucide_icons: ^0.257.0 lucide_icons: ^0.257.0
google_fonts: ^6.1.0 google_fonts: ^6.1.0
socket_io_client: ^3.1.2 socket_io_client: ^3.1.2
easy_localization: ^3.0.7+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -69,6 +70,7 @@ flutter:
assets: assets:
- assets/ - assets/
- assets/logo/ - assets/logo/
- assets/translations/
# - images/a_dot_burr.jpeg # - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg