f_datetimerangepicker.dart 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/cupertino.dart';
  3. typedef PickerConfirmCallback = void Function(DateTime start, DateTime end);
  4. enum DateTimeRangePickerMode {
  5. time,
  6. date,
  7. dateAndTime,
  8. }
  9. class DateTimeRangePicker {
  10. final startText;
  11. final endText;
  12. final doneText;
  13. final cancelText;
  14. final bool use24hFormat;
  15. final DateTimeRangePickerMode mode;
  16. DateTime? initialStartTime;
  17. DateTime? initialEndTime;
  18. DateTime? minimumTime;
  19. DateTime? maximumTime;
  20. final VoidCallback? onCancel;
  21. final PickerConfirmCallback? onConfirm;
  22. final int interval;
  23. DateTimeRangePicker({
  24. Key? key,
  25. this.startText = "Start",
  26. this.endText = "End",
  27. this.doneText = "Done",
  28. this.cancelText = "Cancel",
  29. this.mode = DateTimeRangePickerMode.dateAndTime,
  30. this.interval = 15,
  31. this.use24hFormat = false,
  32. this.minimumTime,
  33. this.maximumTime,
  34. this.initialStartTime,
  35. this.initialEndTime,
  36. this.onCancel,
  37. this.onConfirm,
  38. });
  39. void showPicker(BuildContext context) {
  40. if (initialStartTime == null) {
  41. initialStartTime = DateTime.now();
  42. }
  43. // Remove minutes and seconds to prevent exception of cupertino picker: initial minute is not divisible by minute interval
  44. initialStartTime = initialStartTime!.subtract(Duration(
  45. minutes: initialStartTime!.minute, seconds: initialStartTime!.second));
  46. if (initialEndTime == null) {
  47. initialEndTime = initialStartTime!.add(Duration(
  48. days: mode == DateTimeRangePickerMode.time ? 0 : 1,
  49. hours: mode == DateTimeRangePickerMode.time ? 2 : 0));
  50. }
  51. initialEndTime = initialEndTime!.subtract(Duration(
  52. minutes: initialEndTime!.minute, seconds: initialEndTime!.second));
  53. if (minimumTime != null) {
  54. minimumTime = minimumTime!.subtract(
  55. Duration(minutes: minimumTime!.minute, seconds: minimumTime!.second));
  56. }
  57. if (maximumTime != null) {
  58. maximumTime = maximumTime!.subtract(
  59. Duration(minutes: maximumTime!.minute, seconds: maximumTime!.second));
  60. }
  61. var pickerMode = CupertinoDatePickerMode.dateAndTime;
  62. switch (mode) {
  63. case DateTimeRangePickerMode.date:
  64. {
  65. pickerMode = CupertinoDatePickerMode.date;
  66. }
  67. break;
  68. case DateTimeRangePickerMode.time:
  69. {
  70. pickerMode = CupertinoDatePickerMode.time;
  71. }
  72. break;
  73. default:
  74. {
  75. pickerMode = CupertinoDatePickerMode.dateAndTime;
  76. }
  77. break;
  78. }
  79. showDialog(
  80. context: context,
  81. builder: (BuildContext context) {
  82. return FractionallySizedBox(
  83. widthFactor: 0.8,
  84. heightFactor: 0.5,
  85. child: PickerWidget([
  86. Tab(text: startText),
  87. Tab(text: endText),
  88. ],
  89. initialStartTime!,
  90. initialEndTime!,
  91. interval,
  92. this.onCancel,
  93. this.onConfirm,
  94. pickerMode,
  95. this.doneText,
  96. this.cancelText,
  97. this.minimumTime!,
  98. this.maximumTime!,
  99. this.use24hFormat),
  100. );
  101. });
  102. }
  103. }
  104. class PickerWidget extends StatefulWidget {
  105. final List<Tab> _tabs;
  106. final int _interval;
  107. final VoidCallback? _onCancel;
  108. final PickerConfirmCallback? _onConfirm;
  109. final DateTime _initStart;
  110. final DateTime _initEnd;
  111. final CupertinoDatePickerMode _mode;
  112. final String _doneText;
  113. final String _cancelText;
  114. final DateTime _minimumTime;
  115. final DateTime _maximumTime;
  116. final bool _use24hFormat;
  117. PickerWidget(
  118. this._tabs,
  119. this._initStart,
  120. this._initEnd,
  121. this._interval,
  122. this._onCancel,
  123. this._onConfirm,
  124. this._mode,
  125. this._doneText,
  126. this._cancelText,
  127. this._minimumTime,
  128. this._maximumTime,
  129. this._use24hFormat,
  130. {Key? key})
  131. : super(key: key);
  132. _PickerWidgetState createState() => _PickerWidgetState();
  133. }
  134. class _PickerWidgetState extends State<PickerWidget>
  135. with SingleTickerProviderStateMixin {
  136. TabController? _tabController;
  137. DateTime? _start;
  138. DateTime? _end;
  139. @override
  140. void initState() {
  141. super.initState();
  142. _start = widget._initStart;
  143. _end = widget._initEnd;
  144. _tabController = TabController(vsync: this, length: widget._tabs.length);
  145. }
  146. @override
  147. void dispose() {
  148. _tabController?.dispose();
  149. super.dispose();
  150. }
  151. @override
  152. Widget build(BuildContext context) {
  153. return Scaffold(
  154. backgroundColor: Colors.white,
  155. appBar: AppBar(
  156. automaticallyImplyLeading: false,
  157. backgroundColor: Colors.white,
  158. title: Container(
  159. child: TabBar(
  160. controller: _tabController,
  161. tabs: widget._tabs,
  162. labelColor: Theme.of(context).primaryColor,
  163. ),
  164. ),
  165. ),
  166. body: Stack(
  167. children: <Widget>[
  168. Container(
  169. height: 320,
  170. alignment: Alignment.topCenter,
  171. child: TabBarView(
  172. controller: _tabController,
  173. children: widget._tabs.map((Tab tab) {
  174. return CupertinoDatePicker(
  175. mode: widget._mode,
  176. use24hFormat: widget._use24hFormat,
  177. minuteInterval: widget._interval,
  178. minimumDate: widget._minimumTime != null &&
  179. tab.text == widget._tabs.first.text
  180. ? widget._minimumTime
  181. : DateTime.now(),
  182. maximumDate: widget._maximumTime != null &&
  183. tab.text == widget._tabs.last.text
  184. ? widget._maximumTime
  185. : null,
  186. initialDateTime:
  187. tab.text == widget._tabs.first.text ? _start : _end,
  188. onDateTimeChanged: (DateTime newDateTime) {
  189. if (tab.text == widget._tabs.first.text) {
  190. setState(() {
  191. _start = newDateTime;
  192. });
  193. } else {
  194. setState(() {
  195. _end = newDateTime;
  196. });
  197. }
  198. },
  199. );
  200. }).toList(),
  201. ),
  202. ),
  203. Container(
  204. alignment: Alignment.bottomCenter,
  205. child: Row(
  206. mainAxisAlignment: MainAxisAlignment.end,
  207. children: [
  208. TextButton(
  209. onPressed: () {
  210. Navigator.pop(context);
  211. if (widget._onCancel != null) {
  212. widget._onCancel!();
  213. }
  214. },
  215. child: Text(widget._cancelText),
  216. ),
  217. TextButton(
  218. onPressed: () {
  219. Navigator.of(context).pop();
  220. if (widget._onConfirm != null) {
  221. widget._onConfirm!(_start!, _end!);
  222. }
  223. },
  224. child: Text(widget._doneText),
  225. )
  226. ],
  227. ),
  228. )
  229. ],
  230. ));
  231. }
  232. }