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:
164
lib/app/widgets/inputs/dropdown_input.dart
Normal file
164
lib/app/widgets/inputs/dropdown_input.dart
Normal 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(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
193
lib/app/widgets/inputs/text_input.dart
Normal file
193
lib/app/widgets/inputs/text_input.dart
Normal file
@ -0,0 +1,193 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:penyaluran_app/app/theme/app_colors.dart';
|
||||
|
||||
/// Input teks yang digunakan di seluruh aplikasi
|
||||
///
|
||||
/// Input ini dapat dikonfigurasi untuk berbagai jenis input dan validasi.
|
||||
class TextInput extends StatelessWidget {
|
||||
/// Label yang ditampilkan di atas input
|
||||
final String label;
|
||||
|
||||
/// Hint yang ditampilkan di dalam input
|
||||
final String? hint;
|
||||
|
||||
/// Controller untuk input
|
||||
final TextEditingController? controller;
|
||||
|
||||
/// Fungsi yang dipanggil ketika nilai input berubah
|
||||
final Function(String)? onChanged;
|
||||
|
||||
/// Fungsi yang dipanggil ketika input selesai diedit
|
||||
final Function(String)? onSubmitted;
|
||||
|
||||
/// Apakah input dinonaktifkan
|
||||
final bool enabled;
|
||||
|
||||
/// Apakah input bersifat wajib
|
||||
final bool required;
|
||||
|
||||
/// Apakah input bersifat rahasia (password)
|
||||
final bool obscureText;
|
||||
|
||||
/// Pesan kesalahan yang ditampilkan di bawah input
|
||||
final String? errorText;
|
||||
|
||||
/// Ikon yang ditampilkan di sebelah kanan input
|
||||
final IconData? suffixIcon;
|
||||
|
||||
/// Fungsi yang dipanggil ketika ikon di sebelah kanan input ditekan
|
||||
final VoidCallback? onSuffixIconPressed;
|
||||
|
||||
/// Jenis keyboard yang digunakan
|
||||
final TextInputType keyboardType;
|
||||
|
||||
/// Daftar pemformatan input
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
|
||||
/// Jumlah baris input (untuk input multiline)
|
||||
final int? maxLines;
|
||||
|
||||
/// Jumlah karakter maksimum
|
||||
final int? maxLength;
|
||||
|
||||
/// Apakah input otomatis mendapatkan fokus
|
||||
final bool autofocus;
|
||||
|
||||
/// Fokus node untuk input
|
||||
final FocusNode? focusNode;
|
||||
|
||||
/// Fungsi validasi input
|
||||
final String? Function(String?)? validator;
|
||||
|
||||
/// Konstruktor untuk TextInput
|
||||
const TextInput({
|
||||
super.key,
|
||||
required this.label,
|
||||
this.hint,
|
||||
this.controller,
|
||||
this.onChanged,
|
||||
this.onSubmitted,
|
||||
this.enabled = true,
|
||||
this.required = false,
|
||||
this.obscureText = false,
|
||||
this.errorText,
|
||||
this.suffixIcon,
|
||||
this.onSuffixIconPressed,
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.inputFormatters,
|
||||
this.maxLines = 1,
|
||||
this.maxLength,
|
||||
this.autofocus = false,
|
||||
this.focusNode,
|
||||
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),
|
||||
|
||||
// Input
|
||||
TextFormField(
|
||||
controller: controller,
|
||||
onChanged: onChanged,
|
||||
onFieldSubmitted: onSubmitted,
|
||||
enabled: enabled,
|
||||
obscureText: obscureText,
|
||||
keyboardType: keyboardType,
|
||||
inputFormatters: inputFormatters,
|
||||
maxLines: maxLines,
|
||||
maxLength: maxLength,
|
||||
autofocus: autofocus,
|
||||
focusNode: focusNode,
|
||||
validator: validator,
|
||||
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,
|
||||
),
|
||||
),
|
||||
suffixIcon: suffixIcon != null
|
||||
? IconButton(
|
||||
icon: Icon(suffixIcon),
|
||||
onPressed: onSuffixIconPressed,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user