import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../../../models/day_time.dart'; import '../../../utils/date_time.dart'; class CustomCupertinoDatePicker extends StatefulWidget { final double itemExtent; final void Function(DayTime) onSelectedItemChanged; // Text style of selected item final TextStyle? selectedStyle; // Text style of unselected item final TextStyle? unselectedStyle; // Text style of disabled item final TextStyle? disabledStyle; // Minimum selectable date final DayTime? selectedTime; final bool disable; const CustomCupertinoDatePicker({ Key? key, required this.itemExtent, required this.onSelectedItemChanged, this.selectedTime, this.selectedStyle, this.unselectedStyle, this.disabledStyle, this.disable = false, }) : super(key: key); @override State createState() => _CustomCupertinoDatePickerState(); } class _CustomCupertinoDatePickerState extends State { late DayTime selectedTime = DateTimeUtils.handleDayTime( "${DateTime.now().hour.toString()}:${DateTime.now().minute.toString()}"); late int _selectedMeridiemIndex; late int _selectedHourIndex; late int _selectedMinuteIndex; late final FixedExtentScrollController _meridiemScrollController; late final FixedExtentScrollController _hourScrollController; late final FixedExtentScrollController _minuteScrollController; final _meridiem = [ 'قبل از ظهر', 'بعد از ظهر', ]; final _timeH = []; final _timeM = []; @override void initState() { super.initState(); for (int i = 0; i < 12 + 1; i++) { String twoDigitNumber = i.toString().padLeft(2, '0'); _timeH.add(twoDigitNumber); } for (int i = 1; i < 11 + 1; i++) { String twoDigitNumber = i.toString().padLeft(2, '0'); _timeH.add(twoDigitNumber); } for (int i = 0; i < 59 + 1; i++) { String twoDigitNumber = i.toString().padLeft(2, '0'); _timeM.add(twoDigitNumber); } _meridiemScrollController = FixedExtentScrollController(); _hourScrollController = FixedExtentScrollController(); _minuteScrollController = FixedExtentScrollController(); if (widget.selectedTime != null) { selectedTime = widget.selectedTime!; } _initDates(); } void _initDates() { _selectedMeridiemIndex = 0; switch (selectedTime.meridiem) { case Meridiem.AM: _selectedMeridiemIndex = 0; _selectedHourIndex = int.parse(selectedTime.hour) - 1; break; case Meridiem.PM: _selectedMeridiemIndex = 1; _selectedHourIndex = (int.parse(selectedTime.hour) + 12) - 1; break; } _selectedMinuteIndex = int.parse(selectedTime.minute); WidgetsBinding.instance.addPostFrameCallback((_) { _scrollList(_meridiemScrollController, _selectedMeridiemIndex); _scrollList(_hourScrollController, _selectedHourIndex); _scrollList(_minuteScrollController, _selectedMinuteIndex); }); } void _scrollList(FixedExtentScrollController controller, int index) { controller.animateToItem( index, curve: Curves.easeIn, duration: const Duration(milliseconds: 300), ); } @override void dispose() { _meridiemScrollController.dispose(); _hourScrollController.dispose(); _minuteScrollController.dispose(); super.dispose(); } void _onSelectedItemChanged(int index, SelectorType type) { switch (type) { case SelectorType.meridiem: _selectedMeridiemIndex = index; // if month is changed to february & if (_selectedMeridiemIndex == 0) { if (_selectedHourIndex > 12 - 1) { _selectedHourIndex -= 12; _hourScrollController.jumpToItem(_selectedHourIndex); } } else { if (_selectedHourIndex < 12 - 1) { _selectedHourIndex += 12; _hourScrollController.jumpToItem(_selectedHourIndex); } } if (index == 0) { selectedTime.meridiem = Meridiem.AM; } else { selectedTime.meridiem = Meridiem.PM; } break; case SelectorType.hour: _selectedHourIndex = index; // if month is changed to february & if (_selectedHourIndex > 12 - 1) { _selectedMeridiemIndex = 1; _meridiemScrollController.jumpToItem(_selectedMeridiemIndex); } else { _selectedMeridiemIndex = 0; _meridiemScrollController.jumpToItem(_selectedMeridiemIndex); } selectedTime.hour = _timeH[index]; break; case SelectorType.minute: _selectedMinuteIndex = index; selectedTime.minute = _timeM[index]; break; } setState(() {}); widget.onSelectedItemChanged(selectedTime); } /// check if the given day, month or year index is disabled bool _isDisabled(int index, SelectorType type) { // switch (type) { // case SelectorType.meridiem: // break; // // case SelectorType.hour: // break; // // case SelectorType.minute: // break; // } return false; } @override Widget build(BuildContext context) { Color shadow = Theme.of(context).colorScheme.background; return IgnorePointer( ignoring: widget.disable, child: Stack( children: [ Positioned.fill( child: Center( child: Container( height: 64, decoration: BoxDecoration( color: Theme.of(context) .colorScheme .primary .withOpacity(widget.disable ? 0.2 : 1), borderRadius: BorderRadius.circular(18)), child: const Padding( padding: EdgeInsets.symmetric(vertical: 12), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ SizedBox(), VerticalDivider( color: Colors.white, ), VerticalDivider( color: Colors.white, ), SizedBox(), ], ), ), ), ), ), Row( children: [ Expanded(child: _meridiemSelector()), Expanded(child: _minuteSelector()), Expanded(child: _hourSelector()), ], ), Positioned( top: 0, left: 0, right: 0, child: Container( height: 32, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ shadow.withOpacity(1), shadow.withOpacity(0.9), shadow.withOpacity(0.8), shadow.withOpacity(0.6), shadow.withOpacity(0.5), shadow.withOpacity(0.4), ])), )), Positioned( bottom: 0, left: 0, right: 0, child: Container( height: 32, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [ shadow.withOpacity(1), shadow.withOpacity(0.9), shadow.withOpacity(0.8), shadow.withOpacity(0.6), shadow.withOpacity(0.5), shadow.withOpacity(0.4), ])), )), ], ), ); } Widget _selector({ required List values, required int selectedValueIndex, required bool Function(int) isDisabled, required void Function(int) onSelectedItemChanged, required FixedExtentScrollController scrollController, }) { return CupertinoPicker.builder( childCount: values.length, itemExtent: widget.itemExtent, scrollController: scrollController, squeeze: 1.45, useMagnifier: false, diameterRatio: 2.3, magnification: 1.0, offAxisFraction: 0.0, selectionOverlay: const SizedBox(), onSelectedItemChanged: onSelectedItemChanged, itemBuilder: (context, index) => Container( height: widget.itemExtent, alignment: Alignment.center, decoration: const BoxDecoration(), child: Text( '${values[index]}', style: index == selectedValueIndex ? widget.selectedStyle : isDisabled(index) ? widget.disabledStyle : widget.unselectedStyle, ), ), ); } Widget _meridiemSelector() { return _selector( values: _meridiem, selectedValueIndex: _selectedMeridiemIndex, scrollController: _meridiemScrollController, isDisabled: (index) => _isDisabled(index, SelectorType.meridiem), onSelectedItemChanged: (v) => _onSelectedItemChanged( v, SelectorType.meridiem, ), ); } Widget _hourSelector() { return _selector( values: _timeH, selectedValueIndex: _selectedHourIndex, scrollController: _hourScrollController, isDisabled: (index) => _isDisabled(index, SelectorType.hour), onSelectedItemChanged: (v) => _onSelectedItemChanged( v, SelectorType.hour, ), ); } Widget _minuteSelector() { return _selector( values: _timeM, selectedValueIndex: _selectedMinuteIndex, scrollController: _minuteScrollController, isDisabled: (index) => _isDisabled(index, SelectorType.minute), onSelectedItemChanged: (v) => _onSelectedItemChanged( v, SelectorType.minute, ), ); } }