Perbarui tema dan styling dashboard dengan komponen kustom

- Tambahkan tema aplikasi di AppTheme
- Ganti font default dengan DM Sans dari aset
- Buat komponen kustom seperti StatisticCard, NavigationButton, dan StatusPill
- Perbarui desain drawer, bottom navigation, dan elemen dashboard
- Gunakan gradient dan warna tema yang konsisten
- Sederhanakan struktur kode dan styling
This commit is contained in:
Khafidh Fuadi
2025-03-08 15:54:24 +07:00
parent 9690764cf4
commit 539fad3cda
10 changed files with 643 additions and 341 deletions

View File

@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
class NavigationButton extends StatelessWidget {
final String label;
final IconData? icon;
final Widget? iconWidget;
final VoidCallback onPressed;
const NavigationButton({
Key? key,
required this.label,
this.icon,
this.iconWidget,
required this.onPressed,
}) : assert(icon != null || iconWidget != null,
'Either icon or iconWidget must be provided'),
super(key: key);
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return ConstrainedBox(
constraints: BoxConstraints(minWidth: 70), // Set a minimum width
child: GestureDetector(
onTap: onPressed,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
decoration: BoxDecoration(
color: AppTheme.primaryColor,
borderRadius: BorderRadius.circular(8),
),
child: Row(
mainAxisSize:
MainAxisSize.min, // Important for preventing layout issues
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
label,
style: textTheme.bodySmall?.copyWith(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Color(0xFFAFF8FF),
),
),
const SizedBox(width: 4),
if (icon != null)
Icon(
icon,
size: 12,
color: Color(0xFFAFF8FF),
)
else if (iconWidget != null)
SizedBox(
width: 12,
height: 12,
child: iconWidget,
),
],
),
),
),
);
}
}
// Data class for navigation button
class NavigationButtonData {
final String label;
final IconData? icon;
final Widget? iconWidget;
NavigationButtonData({
required this.label,
this.icon,
this.iconWidget,
}) : assert(icon != null || iconWidget != null,
'Either icon or iconWidget must be provided');
}

View File

@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
import '../theme/app_theme.dart';
class StatisticCard extends StatelessWidget {
final String title;
final String count;
final String subtitle;
final double height;
const StatisticCard({
Key? key,
required this.title,
required this.count,
required this.subtitle,
required this.height,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Container(
width: double.infinity,
height: height,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: AppTheme.primaryGradient,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: textTheme.bodyMedium?.copyWith(
fontSize: 14,
color: Colors.white.withAlpha(204), // 0.8 * 255 ≈ 204
),
),
const SizedBox(height: 4),
Text(
count,
style: textTheme.headlineSmall?.copyWith(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: textTheme.bodySmall?.copyWith(
fontSize: 12,
color: Colors.white,
),
),
],
),
);
}
}

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:penyaluran_app/app/theme/app_theme.dart';
class StatusPill extends StatelessWidget {
final String status;
final Color? backgroundColor;
final TextStyle? textStyle;
const StatusPill({
super.key,
required this.status,
this.backgroundColor,
this.textStyle,
});
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
decoration: BoxDecoration(
color: backgroundColor ?? AppTheme.verifiedColor,
borderRadius: BorderRadius.circular(12),
),
child: Text(
status,
style: textStyle ??
textTheme.bodySmall?.copyWith(
fontSize: 10,
),
),
);
}
}