Perbarui struktur dan referensi file di dashboard_view.dart dan detail_donatur_view.dart. Tambahkan dokumentasi pada kelas DateTimeHelper dan perkenalan fungsi baru untuk format tanggal relatif serta nama hari dan bulan. Hapus widget yang tidak digunakan seperti detail_penitipan_dialog.dart, loading_indicator.dart, navigation_button.dart, statistic_card.dart, dan status_pill.dart untuk menyederhanakan kode.

This commit is contained in:
Khafidh Fuadi
2025-03-16 16:30:23 +07:00
parent 5814b19546
commit 078d74aad3
22 changed files with 1639 additions and 509 deletions

View File

@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import 'package:penyaluran_app/app/theme/app_colors.dart';
/// Item untuk dropdown
class DropdownItem<T> {
/// Nilai item
final T value;
/// Label yang ditampilkan
final String label;
/// Konstruktor untuk DropdownItem
const DropdownItem({
required this.value,
required this.label,
});
}
/// Input dropdown yang digunakan di seluruh aplikasi
///
/// Input ini dapat dikonfigurasi untuk berbagai jenis dropdown dan validasi.
class DropdownInput<T> extends StatelessWidget {
/// Label yang ditampilkan di atas input
final String label;
/// Hint yang ditampilkan di dalam input
final String? hint;
/// Daftar item dropdown
final List<DropdownItem<T>> items;
/// Nilai yang dipilih
final T? value;
/// Fungsi yang dipanggil ketika nilai dropdown berubah
final Function(T?)? onChanged;
/// Apakah input dinonaktifkan
final bool enabled;
/// Apakah input bersifat wajib
final bool required;
/// Pesan kesalahan yang ditampilkan di bawah input
final String? errorText;
/// Fungsi validasi input
final String? Function(T?)? validator;
/// Konstruktor untuk DropdownInput
const DropdownInput({
super.key,
required this.label,
this.hint,
required this.items,
this.value,
this.onChanged,
this.enabled = true,
this.required = false,
this.errorText,
this.validator,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Label
Row(
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.textPrimary,
),
),
if (required)
Text(
' *',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.error,
),
),
],
),
const SizedBox(height: 8),
// Dropdown
DropdownButtonFormField<T>(
value: value,
onChanged: enabled ? onChanged : null,
validator: validator,
isExpanded: true,
icon: const Icon(Icons.arrow_drop_down),
style: TextStyle(
fontSize: 14,
color: enabled ? AppColors.textPrimary : AppColors.textSecondary,
),
decoration: InputDecoration(
hintText: hint,
errorText: errorText,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: AppColors.divider,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: AppColors.divider,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: AppColors.primary,
width: 1.5,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: AppColors.error,
width: 1,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: AppColors.error,
width: 1.5,
),
),
disabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: AppColors.disabled,
width: 1,
),
),
),
items: items.map((DropdownItem<T> item) {
return DropdownMenuItem<T>(
value: item.value,
child: Text(item.label),
);
}).toList(),
),
],
);
}
}