develop #1
|
@ -0,0 +1,55 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||
|
||||
class AppTextStyles {
|
||||
/// Title: strong and modern using Roboto
|
||||
static final TextStyle title = GoogleFonts.roboto(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.darkText,
|
||||
);
|
||||
|
||||
/// Subtitle: clean and readable using Inter
|
||||
static final TextStyle subtitle = GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.softGrayText,
|
||||
);
|
||||
|
||||
/// Body: neutral and easy-to-read using Inter
|
||||
static final TextStyle body = GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.darkText,
|
||||
);
|
||||
|
||||
/// Caption: friendly and soft using Nunito
|
||||
static final TextStyle caption = GoogleFonts.nunito(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.softGrayText,
|
||||
);
|
||||
|
||||
/// Stat value: bold and standout using Poppins
|
||||
static final TextStyle statValue = GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.darkText,
|
||||
);
|
||||
|
||||
/// Option text: clean and consistent using Inter
|
||||
static final TextStyle optionText = GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.darkText,
|
||||
);
|
||||
|
||||
/// DateTime: subtle and elegant using Nunito Italic
|
||||
static final TextStyle dateTime = GoogleFonts.nunito(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontStyle: FontStyle.italic,
|
||||
color: AppColors.softGrayText,
|
||||
);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||
import 'package:quiz_app/app/const/text/text_style.dart';
|
||||
import 'package:quiz_app/data/models/history/detail_quiz_history.dart';
|
||||
|
||||
class QuizItemComponent extends StatelessWidget {
|
||||
|
@ -36,10 +37,7 @@ class QuizItemComponent extends StatelessWidget {
|
|||
const SizedBox(height: 12),
|
||||
_buildAnswerIndicator(),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(
|
||||
height: 24,
|
||||
color: AppColors.shadowPrimary,
|
||||
),
|
||||
const Divider(height: 24, color: AppColors.shadowPrimary),
|
||||
_buildMetadata(),
|
||||
],
|
||||
),
|
||||
|
@ -49,10 +47,7 @@ class QuizItemComponent extends StatelessWidget {
|
|||
Widget _buildQuestionText() {
|
||||
return Text(
|
||||
'${item.index}. ${item.question}',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
),
|
||||
style: AppTextStyles.title.copyWith(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -90,17 +85,10 @@ class QuizItemComponent extends StatelessWidget {
|
|||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: iconColor,
|
||||
),
|
||||
Icon(icon, size: 16, color: iconColor),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
child: Text(text, style: AppTextStyles.optionText),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -126,7 +114,7 @@ class QuizItemComponent extends StatelessWidget {
|
|||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Jawabanmu: $userAnswerText',
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||
style: AppTextStyles.statValue,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -134,10 +122,10 @@ class QuizItemComponent extends StatelessWidget {
|
|||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 26), // offset icon size + spacing
|
||||
const SizedBox(width: 26), // offset for icon + spacing
|
||||
Text(
|
||||
'Jawaban benar: $correctAnswerText',
|
||||
style: const TextStyle(fontSize: 14, color: Colors.black54),
|
||||
style: AppTextStyles.caption,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -161,10 +149,7 @@ class QuizItemComponent extends StatelessWidget {
|
|||
children: [
|
||||
Icon(icon, size: 16, color: AppColors.primaryBlue),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 13, color: Colors.black54),
|
||||
),
|
||||
Text(label, style: AppTextStyles.caption),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:get/get_state_manager/get_state_manager.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||
import 'package:quiz_app/app/const/text/text_style.dart';
|
||||
import 'package:quiz_app/component/widget/loading_widget.dart';
|
||||
import 'package:quiz_app/feature/history/controller/detail_history_controller.dart';
|
||||
import 'package:quiz_app/feature/history/view/component/quiz_item_component.dart';
|
||||
|
@ -16,12 +17,9 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
|
|||
appBar: AppBar(
|
||||
backgroundColor: AppColors.background,
|
||||
elevation: 0,
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Detail history',
|
||||
style: TextStyle(
|
||||
color: AppColors.darkText,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: AppTextStyles.title.copyWith(fontSize: 24),
|
||||
),
|
||||
centerTitle: true,
|
||||
iconTheme: const IconThemeData(color: AppColors.darkText),
|
||||
|
@ -29,11 +27,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
|
|||
body: SafeArea(
|
||||
child: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return Expanded(
|
||||
child: Center(
|
||||
child: LoadingWidget(),
|
||||
),
|
||||
);
|
||||
return const Center(child: LoadingWidget());
|
||||
}
|
||||
return ListView(
|
||||
children: [
|
||||
|
@ -62,7 +56,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
|
|||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
|
@ -71,43 +65,31 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
quiz.title,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Text(quiz.title, style: AppTextStyles.title),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
quiz.description,
|
||||
style: const TextStyle(fontSize: 14, color: Colors.black54),
|
||||
textAlign: TextAlign.justify,
|
||||
style: AppTextStyles.caption,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(LucideIcons.calendar, size: 16, color: Colors.black45),
|
||||
const Icon(LucideIcons.calendar, size: 16, color: AppColors.softGrayText),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
quiz.answeredAt,
|
||||
style: const TextStyle(fontSize: 13, color: Colors.black54),
|
||||
),
|
||||
Text(quiz.answeredAt, style: AppTextStyles.dateTime),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(LucideIcons.clock, size: 16, color: Colors.black45),
|
||||
const Icon(LucideIcons.clock, size: 16, color: AppColors.softGrayText),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'12:00', // tanggal dan jam dipisahkan titik tengah
|
||||
style: const TextStyle(fontSize: 13, color: Colors.black54),
|
||||
),
|
||||
Text('12:00', style: AppTextStyles.dateTime), // Replace with quiz.timeAnswered if available
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
const Divider(
|
||||
height: 24,
|
||||
thickness: 1,
|
||||
color: AppColors.shadowPrimary,
|
||||
),
|
||||
const Divider(height: 24, thickness: 1, color: AppColors.shadowPrimary),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
|
@ -146,17 +128,8 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
|
|||
children: [
|
||||
Icon(icon, color: color, size: 28),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.black54),
|
||||
),
|
||||
Text(value, style: AppTextStyles.statValue),
|
||||
Text(label, style: AppTextStyles.caption),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,51 +1,45 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:quiz_app/app/const/colors/app_colors.dart';
|
||||
import 'package:quiz_app/app/const/text/text_style.dart';
|
||||
import 'package:quiz_app/component/widget/loading_widget.dart';
|
||||
import 'package:quiz_app/data/models/history/quiz_history.dart';
|
||||
import 'package:quiz_app/feature/history/controller/history_controller.dart';
|
||||
|
||||
class HistoryView extends GetView<HistoryController> {
|
||||
const HistoryView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF8F9FB),
|
||||
backgroundColor: AppColors.background,
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"Riwayat Kuis",
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text("Riwayat Kuis", style: AppTextStyles.title.copyWith(fontSize: 24)),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
Text(
|
||||
"Lihat kembali hasil kuis yang telah kamu kerjakan",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
),
|
||||
style: AppTextStyles.subtitle,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return Expanded(
|
||||
child: Center(
|
||||
child: LoadingWidget(),
|
||||
),
|
||||
return const Expanded(
|
||||
child: Center(child: LoadingWidget()),
|
||||
);
|
||||
}
|
||||
|
||||
final historyList = controller.historyList;
|
||||
|
||||
if (historyList.isEmpty) {
|
||||
return const Expanded(
|
||||
child: Center(child: Text("you still doesnt have quiz history")),
|
||||
return Expanded(
|
||||
child: Center(
|
||||
child: Text("You don't have any quiz history yet", style: AppTextStyles.body),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -99,15 +93,9 @@ class HistoryView extends GetView<HistoryController> {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.title,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
Text(item.title, style: AppTextStyles.statValue),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
item.date,
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
Text(item.date, style: AppTextStyles.caption),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
|
@ -115,15 +103,12 @@ class HistoryView extends GetView<HistoryController> {
|
|||
const SizedBox(width: 4),
|
||||
Text(
|
||||
"Skor: ${item.totalCorrect}/${item.totalQuestion}",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
style: AppTextStyles.caption,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
const Icon(Icons.timer, size: 14, color: Colors.grey),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
"3 menit",
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
Text("3 menit", style: AppTextStyles.caption),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
40
pubspec.lock
40
pubspec.lock
|
@ -41,6 +41,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -128,6 +136,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.6.6"
|
||||
google_fonts:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: b1ac0fe2832c9cc95e5e88b57d627c5e68c223b9657f4b96e1487aa9098c7b82
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.1"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -272,6 +288,30 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.17"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -41,6 +41,7 @@ dependencies:
|
|||
dio: ^5.8.0+1
|
||||
shared_preferences: ^2.5.3
|
||||
lucide_icons: ^0.257.0
|
||||
google_fonts: ^6.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue