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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
class APIEndpoint {
// 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://192.168.1.9:5000";
// static const String baseUrl = "http://172.16.106.133:5000";
static const String api = "$baseUrl/api";
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:get/get.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 Divider(thickness: 1.2, color: AppColors.borderLight),
const SizedBox(height: 20),
// Soal Section
ListView.builder(
shrinkWrap: true,
@ -169,13 +171,13 @@ class DetailQuizView extends GetView<DetailQuizController> {
String _mapQuestionTypeToText(String? type) {
switch (type) {
case 'option':
return 'Pilihan Ganda';
return tr('question_type_option');
case 'fill_the_blank':
return 'Isian Kosong';
return tr('question_type_fill');
case 'true_false':
return 'Benar / Salah';
return tr('question_type_true_false');
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:get/get_state_manager/get_state_manager.dart';
import 'package:lucide_icons/lucide_icons.dart';
@ -18,7 +19,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
backgroundColor: AppColors.background,
elevation: 0,
title: Text(
'Detail history',
context.tr('history_detail_title'),
style: AppTextStyles.title.copyWith(fontSize: 24),
),
centerTitle: true,
@ -31,7 +32,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
}
return ListView(
children: [
quizMetaInfo(),
quizMetaInfo(context),
...quizListings(),
],
);
@ -55,7 +56,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
.toList();
}
Widget quizMetaInfo() {
Widget quizMetaInfo(BuildContext context) {
final quiz = controller.quizAnswer;
return Container(
@ -106,20 +107,20 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
children: [
_buildStatItem(
icon: LucideIcons.checkCircle2,
label: 'Benar',
label: context.tr('correct_answer'),
value: "${quiz.totalCorrect}/${quiz.questionListings.length}",
color: Colors.green,
),
_buildStatItem(
icon: LucideIcons.award,
label: 'Skor',
label: context.tr('score'),
value: quiz.totalScore.toString(),
color: Colors.blueAccent,
),
_buildStatItem(
icon: LucideIcons.clock3,
label: 'Waktu',
value: '${quiz.totalSolveTime}s',
label: context.tr('time_taken'),
value: tr('duration_seconds', namedArgs: {"second": quiz.totalSolveTime.toString()}),
color: Colors.orange,
),
],

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:quiz_app/app/const/colors/app_colors.dart';
@ -19,10 +20,10 @@ class HistoryView extends GetView<HistoryController> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
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),
Text(
"Lihat kembali hasil kuis yang telah kamu kerjakan",
context.tr("history_subtitle"),
style: AppTextStyles.subtitle,
),
const SizedBox(height: 20),
@ -38,7 +39,7 @@ class HistoryView extends GetView<HistoryController> {
if (historyList.isEmpty) {
return Expanded(
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 SizedBox(width: 4),
Text(
"Skor: ${item.totalCorrect}/${item.totalQuestion}",
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("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';
class ButtonOption extends StatelessWidget {
@ -19,22 +20,22 @@ class ButtonOption extends StatelessWidget {
margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
child: Row(
children: [
Expanded(child: _buildCreateButton()),
Expanded(child: _buildCreateButton(context)),
const SizedBox(width: 12),
Expanded(child: _buildRoomButtons()),
Expanded(child: _buildRoomButtons(context)),
],
),
);
}
Widget _buildCreateButton() {
Widget _buildCreateButton(BuildContext context) {
return InkWell(
onTap: onCreate,
borderRadius: BorderRadius.circular(16),
child: SizedBox(
height: double.infinity,
child: _buildButtonContainer(
label: 'Buat Quiz',
label: context.tr("create_quiz"),
gradientColors: [Color(0xFF0052CC), Color(0xFF0367D3)],
icon: Icons.create,
),
@ -42,7 +43,7 @@ class ButtonOption extends StatelessWidget {
);
}
Widget _buildRoomButtons() {
Widget _buildRoomButtons(BuildContext context) {
return Column(
children: [
Expanded(
@ -50,7 +51,7 @@ class ButtonOption extends StatelessWidget {
onTap: onCreateRoom,
borderRadius: BorderRadius.circular(16),
child: _buildButtonContainer(
label: 'Buat Room',
label: context.tr("create_room"),
gradientColors: [Color(0xFF36B37E), Color(0xFF22C39F)],
icon: Icons.meeting_room,
),
@ -62,7 +63,7 @@ class ButtonOption extends StatelessWidget {
onTap: onJoinRoom,
borderRadius: BorderRadius.circular(16),
child: _buildButtonContainer(
label: 'Join Room',
label: context.tr("join_room"),
gradientColors: [Color(0xFFFFAB00), Color(0xFFFFC107)],
icon: Icons.group,
),

View File

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

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class UserGretingsComponent extends StatelessWidget {
@ -34,11 +35,11 @@ class UserGretingsComponent extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Selamat Siang",
context.tr("greeting_time"),
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(
"Hello $userName",
context.tr("greeting_user", namedArgs: {"user": userName}),
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:get/get.dart';
import 'package:quiz_app/app/const/colors/app_colors.dart';
@ -50,7 +51,7 @@ class HomeView extends GetView<HomeController> {
const SizedBox(height: 20),
Obx(
() => RecomendationComponent(
title: "Quiz Rekomendasi",
title: context.tr("quiz_recommendation"),
datas: controller.data.toList(),
itemOnTap: controller.onRecommendationTap,
allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation),

View File

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

View File

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

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:quiz_app/app/const/colors/app_colors.dart';
@ -27,7 +28,12 @@ class ListingsQuizView extends GetView<ListingQuizController> {
}
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(

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:get/get.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),
),
const SizedBox(height: 24),
_buildStats(),
_buildStats(context),
const SizedBox(height: 32),
_buildActionButton("Edit Profil", Icons.edit, controller.editProfile),
_buildActionButton(
context.tr("edit_profile"),
Icons.edit,
controller.editProfile,
),
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(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
decoration: BoxDecoration(
@ -74,9 +84,15 @@ class ProfileView extends GetView<ProfileController> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildStatItem("Total Quiz", controller.totalQuizzes.value.toString()),
_buildStatItem(
context.tr("total_quiz"),
controller.totalQuizzes.value.toString(),
),
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:get/get.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:quiz_app/app/const/colors/app_colors.dart';
import 'package:quiz_app/component/global_button.dart';
import 'package:quiz_app/feature/quiz_creation/controller/quiz_creation_controller.dart';
@ -16,9 +17,9 @@ class QuizCreationView extends GetView<QuizCreationController> {
appBar: AppBar(
backgroundColor: AppColors.background,
elevation: 0,
title: const Text(
'Create Quiz',
style: TextStyle(
title: Text(
context.tr('create_quiz_title'),
style: const TextStyle(
fontWeight: FontWeight.bold,
color: AppColors.darkText,
),
@ -36,13 +37,14 @@ class QuizCreationView extends GetView<QuizCreationController> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildModeSelector(),
_buildModeSelector(context),
const SizedBox(height: 20),
Obx(
() => controller.isGenerate.value ? GenerateComponent() : CustomQuestionComponent(),
),
Obx(() => controller.isGenerate.value ? const GenerateComponent() : const CustomQuestionComponent()),
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(
decoration: BoxDecoration(
color: AppColors.background,
@ -60,8 +62,8 @@ class QuizCreationView extends GetView<QuizCreationController> {
),
child: Row(
children: [
_buildModeButton('Generate', controller.isGenerate, true),
_buildModeButton('Manual', controller.isGenerate, false),
_buildModeButton(context.tr('mode_generate'), controller.isGenerate, true),
_buildModeButton(context.tr('mode_manual'), controller.isGenerate, false),
],
),
);
@ -71,17 +73,18 @@ class QuizCreationView extends GetView<QuizCreationController> {
return Expanded(
child: InkWell(
onTap: () => controller.onCreationTypeChange(base),
child: Obx(
() => Container(
child: Obx(() {
final selected = isSelected.value == base;
return Container(
padding: const EdgeInsets.symmetric(vertical: 14),
decoration: BoxDecoration(
color: isSelected.value == base ? AppColors.primaryBlue : Colors.transparent,
color: selected ? AppColors.primaryBlue : Colors.transparent,
borderRadius: base
? BorderRadius.only(
? const BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10),
)
: BorderRadius.only(
: const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
),
@ -90,12 +93,12 @@ class QuizCreationView extends GetView<QuizCreationController> {
child: Text(
label,
style: TextStyle(
color: isSelected.value == base ? Colors.white : AppColors.softGrayText,
color: selected ? Colors.white : AppColors.softGrayText,
fontWeight: FontWeight.w600,
),
),
),
),
);
}),
),
);
}

View File

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

View File

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

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.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/component/app_name.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> {
const RegisterView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
@ -22,47 +24,52 @@ class RegisterView extends GetView<RegisterController> {
padding: const EdgeInsets.symmetric(vertical: 40),
child: AppName(),
),
LabelTextField(label: "Register", fontSize: 24),
LabelTextField(
label: context.tr('register_title'),
fontSize: 24,
),
const SizedBox(height: 10),
LabelTextField(label: "Full Name"),
LabelTextField(label: context.tr('full_name')),
GlobalTextField(controller: controller.nameController),
const SizedBox(height: 10),
LabelTextField(label: "Email"),
LabelTextField(label: context.tr('email')),
GlobalTextField(controller: controller.emailController),
const SizedBox(height: 10),
LabelTextField(label: "Birth Date"),
LabelTextField(label: context.tr('birth_date')),
GlobalTextField(
controller: controller.bDateController,
hintText: "12-08-2001",
),
LabelTextField(label: "Nomer Telepon (Opsional)"),
LabelTextField(label: context.tr('phone_optional')),
GlobalTextField(
controller: controller.phoneController,
hintText: "085708570857",
),
const SizedBox(height: 10),
LabelTextField(label: "Password"),
LabelTextField(label: context.tr('password')),
Obx(
() => GlobalTextField(
controller: controller.passwordController,
isPassword: true,
obscureText: controller.isPasswordHidden.value,
onToggleVisibility: controller.togglePasswordVisibility),
onToggleVisibility: controller.togglePasswordVisibility,
),
),
const SizedBox(height: 10),
LabelTextField(label: "Verify Password"),
LabelTextField(label: context.tr('verify_password')),
Obx(
() => GlobalTextField(
controller: controller.confirmPasswordController,
isPassword: true,
obscureText: controller.isConfirmPasswordHidden.value,
onToggleVisibility: controller.toggleConfirmPasswordVisibility),
onToggleVisibility: controller.toggleConfirmPasswordVisibility,
),
),
const SizedBox(height: 40),
GlobalButton(
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:get/get.dart';
import 'package:quiz_app/app/const/enums/listing_type.dart';
@ -31,7 +32,7 @@ class SearchView extends GetView<SearchQuizController> {
] else ...[
Obx(
() => RecomendationComponent(
title: "Quiz Rekomendasi",
title: context.tr('quiz_recommendation'),
datas: controller.recommendationQData.toList(),
itemOnTap: controller.goToDetailPage,
allOnTap: () => controller.goToListingsQuizPage(ListingType.recomendation),
@ -40,7 +41,7 @@ class SearchView extends GetView<SearchQuizController> {
const SizedBox(height: 30),
Obx(
() => RecomendationComponent(
title: "Quiz Populer",
title: context.tr('quiz_popular'),
datas: controller.recommendationQData.toList(),
itemOnTap: controller.goToDetailPage,
allOnTap: () => controller.goToListingsQuizPage(ListingType.populer),

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:quiz_app/app/app.dart';
@ -14,7 +15,21 @@ void main() {
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) {
logC.e("issue message $e || $stackTrace");
});

View File

@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
@ -73,6 +81,22 @@ packages:
url: "https://pub.dev"
source: hosted
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:
dependency: transitive
description:
@ -118,6 +142,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
@ -208,6 +237,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
intl:
dependency: transitive
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
leak_tracker:
dependency: transitive
description:

View File

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