import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_datepicker/datepicker.dart'; import '../../../common/extension/extension.dart'; import '../../../common/theme/theme.dart'; import '../bottom_sheet/date_range_bottom_sheet.dart'; class DateRangePickerField extends StatefulWidget { final String? label; final String placeholder; final DateTime? startDate; final DateTime? endDate; final DateTime? minDate; final DateTime? maxDate; final Function(DateTime? startDate, DateTime? endDate)? onChanged; final Color primaryColor; final bool enabled; final String? errorText; final EdgeInsetsGeometry? padding; final TextStyle? textStyle; final TextStyle? placeholderStyle; final BoxDecoration? decoration; final double height; const DateRangePickerField({ super.key, this.label, this.placeholder = 'Pilih rentang tanggal', this.startDate, this.endDate, this.minDate, this.maxDate, this.onChanged, this.primaryColor = AppColor.primary, this.enabled = true, this.errorText, this.padding, this.textStyle, this.placeholderStyle, this.decoration, this.height = 52.0, }); @override State createState() => _DateRangePickerFieldState(); } class _DateRangePickerFieldState extends State { bool _isPressed = false; String get _displayText { if (widget.startDate != null && widget.endDate != null) { return '${_formatDate(widget.startDate!)} - ${_formatDate(widget.endDate!)}'; } else if (widget.startDate != null) { return _formatDate(widget.startDate!); } return widget.placeholder; } bool get _hasValue { return widget.startDate != null || widget.endDate != null; } String _formatDate(DateTime date) { final months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des', ]; return '${date.day} ${months[date.month - 1]} ${date.year}'; } Future _showDateRangePicker() async { if (!widget.enabled) return; final result = await DateRangePickerBottomSheet.show( context: context, title: widget.label ?? context.lang.select_date_range, initialStartDate: widget.startDate, initialEndDate: widget.endDate, minDate: widget.minDate, maxDate: widget.maxDate, primaryColor: widget.primaryColor, onChanged: widget.onChanged, ); if (result != null && widget.onChanged != null) { if (result.value is PickerDateRange) { final PickerDateRange range = result.value; widget.onChanged!(range.startDate, range.endDate); } } } @override Widget build(BuildContext context) { final hasError = widget.errorText != null && widget.errorText!.isNotEmpty; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Label if (widget.label != null) ...[ Text( widget.label!, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: hasError ? AppColor.error : AppColor.textSecondary, ), ), const SizedBox(height: 8), ], // Input Field GestureDetector( onTap: _showDateRangePicker, onTapDown: widget.enabled ? (_) => setState(() => _isPressed = true) : null, onTapUp: widget.enabled ? (_) => setState(() => _isPressed = false) : null, onTapCancel: widget.enabled ? () => setState(() => _isPressed = false) : null, child: AnimatedContainer( duration: const Duration(milliseconds: 150), height: widget.height, padding: widget.padding ?? const EdgeInsets.symmetric(horizontal: 16), decoration: widget.decoration ?? BoxDecoration( color: widget.enabled ? (_isPressed ? AppColor.backgroundLight : AppColor.white) : AppColor.background, borderRadius: BorderRadius.circular(12), border: Border.all( color: hasError ? AppColor.error : (_isPressed ? widget.primaryColor : AppColor.border), width: _isPressed ? 2 : 1, ), boxShadow: _isPressed && widget.enabled ? [ BoxShadow( color: widget.primaryColor.withOpacity(0.1), blurRadius: 8, offset: const Offset(0, 2), ), ] : null, ), child: Row( children: [ // Date Text Expanded( child: Text( _displayText, style: widget.textStyle ?? TextStyle( fontSize: 15, fontWeight: _hasValue ? FontWeight.w500 : FontWeight.w400, color: widget.enabled ? (_hasValue ? AppColor.textPrimary : AppColor.textSecondary) : AppColor.textLight, ), ), ), // Calendar Icon Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( color: widget.primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.calendar_today_rounded, size: 20, color: widget.enabled ? widget.primaryColor : AppColor.textLight, ), ), ], ), ), ), // Error Text if (hasError) ...[ const SizedBox(height: 6), Text( widget.errorText!, style: TextStyle( fontSize: 12, color: AppColor.error, fontWeight: FontWeight.w400, ), ), ], ], ); } } // Variasi dengan style yang berbeda class DateRangePickerFieldOutlined extends StatefulWidget { final String? label; final String placeholder; final DateTime? startDate; final DateTime? endDate; final DateTime? minDate; final DateTime? maxDate; final Function(DateTime? startDate, DateTime? endDate)? onChanged; final Color primaryColor; final bool enabled; final String? errorText; const DateRangePickerFieldOutlined({ Key? key, this.label, this.placeholder = 'Pilih rentang tanggal', this.startDate, this.endDate, this.minDate, this.maxDate, this.onChanged, this.primaryColor = AppColor.primary, this.enabled = true, this.errorText, }) : super(key: key); @override State createState() => _DateRangePickerFieldOutlinedState(); } class _DateRangePickerFieldOutlinedState extends State { bool _isFocused = false; String get _displayText { if (widget.startDate != null && widget.endDate != null) { return '${_formatDate(widget.startDate!)} - ${_formatDate(widget.endDate!)}'; } else if (widget.startDate != null) { return _formatDate(widget.startDate!); } return widget.placeholder; } bool get _hasValue { return widget.startDate != null || widget.endDate != null; } String _formatDate(DateTime date) { final months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des', ]; return '${date.day} ${months[date.month - 1]} ${date.year}'; } Future _showDateRangePicker() async { if (!widget.enabled) return; setState(() => _isFocused = true); final result = await DateRangePickerBottomSheet.show( context: context, title: widget.label ?? context.lang.select_date_range, initialStartDate: widget.startDate, initialEndDate: widget.endDate, minDate: widget.minDate, maxDate: widget.maxDate, primaryColor: widget.primaryColor, onChanged: widget.onChanged, ); setState(() => _isFocused = false); if (result != null && widget.onChanged != null) { if (result.value is PickerDateRange) { final PickerDateRange range = result.value; widget.onChanged!(range.startDate, range.endDate); } } } @override Widget build(BuildContext context) { final hasError = widget.errorText != null && widget.errorText!.isNotEmpty; return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ GestureDetector( onTap: _showDateRangePicker, child: Container( height: 56, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all( color: hasError ? AppColor.error : (_isFocused || _hasValue ? widget.primaryColor : AppColor.border), width: _isFocused ? 2 : 1, ), ), child: Row( children: [ const SizedBox(width: 16), // Date Text Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ if (widget.label != null && (_isFocused || _hasValue)) Text( widget.label!, style: TextStyle( fontSize: 12, color: hasError ? AppColor.error : widget.primaryColor, fontWeight: FontWeight.w500, ), ), Text( _hasValue ? _displayText : (widget.label ?? widget.placeholder), style: TextStyle( fontSize: _hasValue ? 16 : 16, fontWeight: FontWeight.w400, color: widget.enabled ? (_hasValue ? AppColor.textPrimary : AppColor.textSecondary) : AppColor.textLight, ), ), ], ), ), // Calendar Icon Padding( padding: const EdgeInsets.only(right: 16), child: Icon( Icons.calendar_today_rounded, size: 24, color: widget.enabled ? (_isFocused ? widget.primaryColor : AppColor.textSecondary) : AppColor.textLight, ), ), ], ), ), ), // Error Text if (hasError) ...[ const SizedBox(height: 6), Padding( padding: const EdgeInsets.only(left: 16), child: Text( widget.errorText!, style: TextStyle( fontSize: 12, color: AppColor.error, fontWeight: FontWeight.w400, ), ), ), ], ], ); } }