feat: adding global text style

This commit is contained in:
akhdanre 2025-05-04 03:07:15 +07:00
parent 572808a40d
commit 140b8f103c
6 changed files with 137 additions and 98 deletions

View File

@ -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,
);
}

View File

@ -1,6 +1,7 @@
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';
import 'package:quiz_app/app/const/text/text_style.dart';
import 'package:quiz_app/data/models/history/detail_quiz_history.dart'; import 'package:quiz_app/data/models/history/detail_quiz_history.dart';
class QuizItemComponent extends StatelessWidget { class QuizItemComponent extends StatelessWidget {
@ -36,10 +37,7 @@ class QuizItemComponent extends StatelessWidget {
const SizedBox(height: 12), const SizedBox(height: 12),
_buildAnswerIndicator(), _buildAnswerIndicator(),
const SizedBox(height: 16), const SizedBox(height: 16),
const Divider( const Divider(height: 24, color: AppColors.shadowPrimary),
height: 24,
color: AppColors.shadowPrimary,
),
_buildMetadata(), _buildMetadata(),
], ],
), ),
@ -49,10 +47,7 @@ class QuizItemComponent extends StatelessWidget {
Widget _buildQuestionText() { Widget _buildQuestionText() {
return Text( return Text(
'${item.index}. ${item.question}', '${item.index}. ${item.question}',
style: const TextStyle( style: AppTextStyles.title.copyWith(fontSize: 16, fontWeight: FontWeight.w600),
fontWeight: FontWeight.w600,
fontSize: 16,
),
); );
} }
@ -90,17 +85,10 @@ class QuizItemComponent extends StatelessWidget {
), ),
child: Row( child: Row(
children: [ children: [
Icon( Icon(icon, size: 16, color: iconColor),
icon,
size: 16,
color: iconColor,
),
const SizedBox(width: 8), const SizedBox(width: 8),
Flexible( Flexible(
child: Text( child: Text(text, style: AppTextStyles.optionText),
text,
style: const TextStyle(fontSize: 14),
),
), ),
], ],
), ),
@ -126,7 +114,7 @@ class QuizItemComponent extends StatelessWidget {
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Jawabanmu: $userAnswerText', '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), const SizedBox(height: 6),
Row( Row(
children: [ children: [
const SizedBox(width: 26), // offset icon size + spacing const SizedBox(width: 26), // offset for icon + spacing
Text( Text(
'Jawaban benar: $correctAnswerText', 'Jawaban benar: $correctAnswerText',
style: const TextStyle(fontSize: 14, color: Colors.black54), style: AppTextStyles.caption,
), ),
], ],
), ),
@ -161,10 +149,7 @@ class QuizItemComponent extends StatelessWidget {
children: [ children: [
Icon(icon, size: 16, color: AppColors.primaryBlue), Icon(icon, size: 16, color: AppColors.primaryBlue),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(label, style: AppTextStyles.caption),
label,
style: const TextStyle(fontSize: 13, color: Colors.black54),
),
], ],
); );
} }

View File

@ -2,6 +2,7 @@ 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';
import 'package:quiz_app/app/const/colors/app_colors.dart'; import 'package:quiz_app/app/const/colors/app_colors.dart';
import 'package:quiz_app/app/const/text/text_style.dart';
import 'package:quiz_app/component/widget/loading_widget.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/controller/detail_history_controller.dart';
import 'package:quiz_app/feature/history/view/component/quiz_item_component.dart'; import 'package:quiz_app/feature/history/view/component/quiz_item_component.dart';
@ -16,12 +17,9 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
appBar: AppBar( appBar: AppBar(
backgroundColor: AppColors.background, backgroundColor: AppColors.background,
elevation: 0, elevation: 0,
title: const Text( title: Text(
'Detail history', 'Detail history',
style: TextStyle( style: AppTextStyles.title.copyWith(fontSize: 24),
color: AppColors.darkText,
fontWeight: FontWeight.bold,
),
), ),
centerTitle: true, centerTitle: true,
iconTheme: const IconThemeData(color: AppColors.darkText), iconTheme: const IconThemeData(color: AppColors.darkText),
@ -29,11 +27,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
body: SafeArea( body: SafeArea(
child: Obx(() { child: Obx(() {
if (controller.isLoading.value) { if (controller.isLoading.value) {
return Expanded( return const Center(child: LoadingWidget());
child: Center(
child: LoadingWidget(),
),
);
} }
return ListView( return ListView(
children: [ children: [
@ -62,7 +56,7 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: Colors.black.withValues(alpha: 0.05),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@ -71,43 +65,31 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(quiz.title, style: AppTextStyles.title),
quiz.title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
quiz.description, quiz.description,
style: const TextStyle(fontSize: 14, color: Colors.black54), textAlign: TextAlign.justify,
style: AppTextStyles.caption,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Row( Row(
children: [ children: [
const Icon(LucideIcons.calendar, size: 16, color: Colors.black45), const Icon(LucideIcons.calendar, size: 16, color: AppColors.softGrayText),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text(quiz.answeredAt, style: AppTextStyles.dateTime),
quiz.answeredAt,
style: const TextStyle(fontSize: 13, color: Colors.black54),
),
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
Row( Row(
children: [ children: [
const Icon(LucideIcons.clock, size: 16, color: Colors.black45), const Icon(LucideIcons.clock, size: 16, color: AppColors.softGrayText),
const SizedBox(width: 6), const SizedBox(width: 6),
Text( Text('12:00', style: AppTextStyles.dateTime), // Replace with quiz.timeAnswered if available
'12:00', // tanggal dan jam dipisahkan titik tengah
style: const TextStyle(fontSize: 13, color: Colors.black54),
),
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),
const Divider( const Divider(height: 24, thickness: 1, color: AppColors.shadowPrimary),
height: 24,
thickness: 1,
color: AppColors.shadowPrimary,
),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
@ -146,17 +128,8 @@ class DetailHistoryView extends GetView<DetailHistoryController> {
children: [ children: [
Icon(icon, color: color, size: 28), Icon(icon, color: color, size: 28),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(value, style: AppTextStyles.statValue),
value, Text(label, style: AppTextStyles.caption),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
label,
style: const TextStyle(fontSize: 12, color: Colors.black54),
),
], ],
); );
} }

View File

@ -1,51 +1,45 @@
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/text/text_style.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/history/quiz_history.dart'; import 'package:quiz_app/data/models/history/quiz_history.dart';
import 'package:quiz_app/feature/history/controller/history_controller.dart'; import 'package:quiz_app/feature/history/controller/history_controller.dart';
class HistoryView extends GetView<HistoryController> { class HistoryView extends GetView<HistoryController> {
const HistoryView({super.key}); const HistoryView({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: const Color(0xFFF8F9FB), backgroundColor: AppColors.background,
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text("Riwayat Kuis", style: AppTextStyles.title.copyWith(fontSize: 24)),
"Riwayat Kuis",
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8), const SizedBox(height: 8),
const Text( Text(
"Lihat kembali hasil kuis yang telah kamu kerjakan", "Lihat kembali hasil kuis yang telah kamu kerjakan",
style: TextStyle( style: AppTextStyles.subtitle,
fontSize: 14,
color: Colors.grey,
),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
Obx(() { Obx(() {
if (controller.isLoading.value) { if (controller.isLoading.value) {
return Expanded( return const Expanded(
child: Center( child: Center(child: LoadingWidget()),
child: LoadingWidget(),
),
); );
} }
final historyList = controller.historyList; final historyList = controller.historyList;
if (historyList.isEmpty) { if (historyList.isEmpty) {
return const Expanded( return Expanded(
child: Center(child: Text("you still doesnt have quiz history")), 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( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(item.title, style: AppTextStyles.statValue),
item.title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(item.date, style: AppTextStyles.caption),
item.date,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
children: [ children: [
@ -115,15 +103,12 @@ class HistoryView extends GetView<HistoryController> {
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
"Skor: ${item.totalCorrect}/${item.totalQuestion}", "Skor: ${item.totalCorrect}/${item.totalQuestion}",
style: const TextStyle(fontSize: 12), 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( Text("3 menit", style: AppTextStyles.caption),
"3 menit",
style: const TextStyle(fontSize: 12),
),
], ],
), ),
], ],

View File

@ -41,6 +41,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.0" version: "1.19.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -128,6 +136,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.6.6" 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: google_identity_services_web:
dependency: transitive dependency: transitive
description: description:
@ -272,6 +288,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.0" 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: path_provider_linux:
dependency: transitive dependency: transitive
description: description:

View File

@ -41,6 +41,7 @@ dependencies:
dio: ^5.8.0+1 dio: ^5.8.0+1
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
lucide_icons: ^0.257.0 lucide_icons: ^0.257.0
google_fonts: ^6.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: