date_model.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. import 'package:flutter_datetime_picker/src/date_format.dart';
  2. import 'package:flutter_datetime_picker/src/i18n_model.dart';
  3. import 'datetime_util.dart';
  4. import 'dart:math';
  5. //interface for picker data model
  6. abstract class BasePickerModel {
  7. //a getter method for left column data, return null to end list
  8. String leftStringAtIndex(int index);
  9. //a getter method for middle column data, return null to end list
  10. String middleStringAtIndex(int index);
  11. //a getter method for right column data, return null to end list
  12. String rightStringAtIndex(int index);
  13. //set selected left index
  14. void setLeftIndex(int index);
  15. //set selected middle index
  16. void setMiddleIndex(int index);
  17. //set selected right index
  18. void setRightIndex(int index);
  19. //return current left index
  20. int currentLeftIndex();
  21. //return current middle index
  22. int currentMiddleIndex();
  23. //return current right index
  24. int currentRightIndex();
  25. //return final time
  26. DateTime finalTime();
  27. //return left divider string
  28. String leftDivider();
  29. //return right divider string
  30. String rightDivider();
  31. //layout proportions for 3 columns
  32. List<int> layoutProportions();
  33. }
  34. //a base class for picker data model
  35. class CommonPickerModel extends BasePickerModel {
  36. List<String> leftList;
  37. List<String> middleList;
  38. List<String> rightList;
  39. DateTime currentTime;
  40. int _currentLeftIndex;
  41. int _currentMiddleIndex;
  42. int _currentRightIndex;
  43. LocaleType locale;
  44. CommonPickerModel({this.currentTime, locale}) : this.locale = locale ?? LocaleType.en;
  45. @override
  46. String leftStringAtIndex(int index) {
  47. return null;
  48. }
  49. @override
  50. String middleStringAtIndex(int index) {
  51. return null;
  52. }
  53. @override
  54. String rightStringAtIndex(int index) {
  55. return null;
  56. }
  57. @override
  58. int currentLeftIndex() {
  59. return _currentLeftIndex;
  60. }
  61. @override
  62. int currentMiddleIndex() {
  63. return _currentMiddleIndex;
  64. }
  65. @override
  66. int currentRightIndex() {
  67. return _currentRightIndex;
  68. }
  69. @override
  70. void setLeftIndex(int index) {
  71. _currentLeftIndex = index;
  72. }
  73. @override
  74. void setMiddleIndex(int index) {
  75. _currentMiddleIndex = index;
  76. }
  77. @override
  78. void setRightIndex(int index) {
  79. _currentRightIndex = index;
  80. }
  81. @override
  82. String leftDivider() {
  83. return "";
  84. }
  85. @override
  86. String rightDivider() {
  87. return "";
  88. }
  89. @override
  90. List<int> layoutProportions() {
  91. return [1, 1, 1];
  92. }
  93. @override
  94. DateTime finalTime() {
  95. return null;
  96. }
  97. }
  98. //a date picker model
  99. class DatePickerModel extends CommonPickerModel {
  100. DateTime maxTime;
  101. DateTime minTime;
  102. DatePickerModel({DateTime currentTime, DateTime maxTime, DateTime minTime, LocaleType locale})
  103. : super(locale: locale) {
  104. this.maxTime = maxTime ?? DateTime(2049, 12, 31);
  105. this.minTime = minTime ?? DateTime(1970, 1, 1);
  106. currentTime = currentTime ?? DateTime.now();
  107. if (currentTime != null) {
  108. if (currentTime.compareTo(this.maxTime) > 0) {
  109. currentTime = this.maxTime;
  110. } else if (currentTime.compareTo(this.minTime) < 0) {
  111. currentTime = this.minTime;
  112. }
  113. }
  114. this.currentTime = currentTime;
  115. _fillLeftLists();
  116. _fillMiddleLists();
  117. _fillRightLists();
  118. int minMonth = _minMonthOfCurrentYear();
  119. int minDay = _minDayOfCurrentMonth();
  120. _currentLeftIndex = this.currentTime.year - this.minTime.year;
  121. _currentMiddleIndex = this.currentTime.month - minMonth;
  122. _currentRightIndex = this.currentTime.day - minDay;
  123. }
  124. void _fillLeftLists() {
  125. this.leftList = List.generate(maxTime.year - minTime.year + 1, (int index) {
  126. // print('LEFT LIST... ${minTime.year + index}${_localeYear()}');
  127. return '${minTime.year + index}${_localeYear()}';
  128. });
  129. }
  130. int _maxMonthOfCurrentYear() {
  131. return currentTime.year == maxTime.year ? maxTime.month : 12;
  132. }
  133. int _minMonthOfCurrentYear() {
  134. return currentTime.year == minTime.year ? minTime.month : 1;
  135. }
  136. int _maxDayOfCurrentMonth() {
  137. int dayCount = calcDateCount(currentTime.year, currentTime.month);
  138. return currentTime.year == maxTime.year && currentTime.month == maxTime.month
  139. ? maxTime.day
  140. : dayCount;
  141. }
  142. int _minDayOfCurrentMonth() {
  143. return currentTime.year == minTime.year && currentTime.month == minTime.month ? minTime.day : 1;
  144. }
  145. void _fillMiddleLists() {
  146. int minMonth = _minMonthOfCurrentYear();
  147. int maxMonth = _maxMonthOfCurrentYear();
  148. this.middleList = List.generate(maxMonth - minMonth + 1, (int index) {
  149. return '${_localeMonth(minMonth + index)}';
  150. });
  151. }
  152. void _fillRightLists() {
  153. int maxDay = _maxDayOfCurrentMonth();
  154. int minDay = _minDayOfCurrentMonth();
  155. this.rightList = List.generate(maxDay - minDay + 1, (int index) {
  156. return '${minDay + index}${_localeDay()}';
  157. });
  158. }
  159. @override
  160. void setLeftIndex(int index) {
  161. super.setLeftIndex(index);
  162. //adjust middle
  163. int destYear = index + minTime.year;
  164. int minMonth = _minMonthOfCurrentYear();
  165. DateTime newTime;
  166. //change date time
  167. if (currentTime.month == 2 && currentTime.day == 29) {
  168. newTime = currentTime.isUtc
  169. ? DateTime.utc(
  170. destYear,
  171. currentTime.month,
  172. calcDateCount(destYear, 2),
  173. )
  174. : DateTime(
  175. destYear,
  176. currentTime.month,
  177. calcDateCount(destYear, 2),
  178. );
  179. } else {
  180. newTime = currentTime.isUtc
  181. ? DateTime.utc(
  182. destYear,
  183. currentTime.month,
  184. currentTime.day,
  185. )
  186. : DateTime(
  187. destYear,
  188. currentTime.month,
  189. currentTime.day,
  190. );
  191. }
  192. //min/max check
  193. if (newTime.isAfter(maxTime)) {
  194. currentTime = maxTime;
  195. } else if (newTime.isBefore(minTime)) {
  196. currentTime = minTime;
  197. } else {
  198. currentTime = newTime;
  199. }
  200. _fillMiddleLists();
  201. _fillRightLists();
  202. minMonth = _minMonthOfCurrentYear();
  203. int minDay = _minDayOfCurrentMonth();
  204. _currentMiddleIndex = currentTime.month - minMonth;
  205. _currentRightIndex = currentTime.day - minDay;
  206. }
  207. @override
  208. void setMiddleIndex(int index) {
  209. super.setMiddleIndex(index);
  210. //adjust right
  211. int minMonth = _minMonthOfCurrentYear();
  212. int destMonth = minMonth + index;
  213. DateTime newTime;
  214. //change date time
  215. int dayCount = calcDateCount(currentTime.year, destMonth);
  216. newTime = currentTime.isUtc
  217. ? DateTime.utc(
  218. currentTime.year,
  219. destMonth,
  220. currentTime.day <= dayCount ? currentTime.day : dayCount,
  221. )
  222. : DateTime(
  223. currentTime.year,
  224. destMonth,
  225. currentTime.day <= dayCount ? currentTime.day : dayCount,
  226. );
  227. //min/max check
  228. if (newTime.isAfter(maxTime)) {
  229. currentTime = maxTime;
  230. } else if (newTime.isBefore(minTime)) {
  231. currentTime = minTime;
  232. } else {
  233. currentTime = newTime;
  234. }
  235. _fillRightLists();
  236. int minDay = _minDayOfCurrentMonth();
  237. _currentRightIndex = currentTime.day - minDay;
  238. }
  239. @override
  240. void setRightIndex(int index) {
  241. super.setRightIndex(index);
  242. int minDay = _minDayOfCurrentMonth();
  243. currentTime = currentTime.isUtc
  244. ? DateTime.utc(
  245. currentTime.year,
  246. currentTime.month,
  247. minDay + index,
  248. )
  249. : DateTime(
  250. currentTime.year,
  251. currentTime.month,
  252. minDay + index,
  253. );
  254. }
  255. @override
  256. String leftStringAtIndex(int index) {
  257. if (index >= 0 && index < leftList.length) {
  258. return leftList[index];
  259. } else {
  260. return null;
  261. }
  262. }
  263. @override
  264. String middleStringAtIndex(int index) {
  265. if (index >= 0 && index < middleList.length) {
  266. return middleList[index];
  267. } else {
  268. return null;
  269. }
  270. }
  271. @override
  272. String rightStringAtIndex(int index) {
  273. if (index >= 0 && index < rightList.length) {
  274. return rightList[index];
  275. } else {
  276. return null;
  277. }
  278. }
  279. String _localeYear() {
  280. if (locale == LocaleType.zh || locale == LocaleType.jp) {
  281. return '年';
  282. } else if (locale == LocaleType.ko) {
  283. return '년';
  284. } else {
  285. return '';
  286. }
  287. }
  288. String _localeMonth(int month) {
  289. if (locale == LocaleType.zh || locale == LocaleType.jp) {
  290. return '$month月';
  291. } else if (locale == LocaleType.ko) {
  292. return '$month월';
  293. } else {
  294. List monthStrings = i18nObjInLocale(locale)['monthLong'];
  295. return monthStrings[month - 1];
  296. }
  297. }
  298. String _localeDay() {
  299. if (locale == LocaleType.zh || locale == LocaleType.jp) {
  300. return '日';
  301. } else if (locale == LocaleType.ko) {
  302. return '일';
  303. } else {
  304. return '';
  305. }
  306. }
  307. @override
  308. DateTime finalTime() {
  309. return currentTime;
  310. }
  311. }
  312. //a time picker model
  313. class TimePickerModel extends CommonPickerModel {
  314. bool showSecondsColumn;
  315. TimePickerModel({DateTime currentTime, LocaleType locale, this.showSecondsColumn: true})
  316. : super(locale: locale) {
  317. this.currentTime = currentTime ?? DateTime.now();
  318. _currentLeftIndex = this.currentTime.hour;
  319. _currentMiddleIndex = this.currentTime.minute;
  320. _currentRightIndex = this.currentTime.second;
  321. }
  322. @override
  323. String leftStringAtIndex(int index) {
  324. if (index >= 0 && index < 24) {
  325. return digits(index, 2);
  326. } else {
  327. return null;
  328. }
  329. }
  330. @override
  331. String middleStringAtIndex(int index) {
  332. if (index >= 0 && index < 60) {
  333. return digits(index, 2);
  334. } else {
  335. return null;
  336. }
  337. }
  338. @override
  339. String rightStringAtIndex(int index) {
  340. if (index >= 0 && index < 60) {
  341. return digits(index, 2);
  342. } else {
  343. return null;
  344. }
  345. }
  346. @override
  347. String leftDivider() {
  348. return ":";
  349. }
  350. @override
  351. String rightDivider() {
  352. if (showSecondsColumn)
  353. return ":";
  354. else
  355. return "";
  356. }
  357. @override
  358. List<int> layoutProportions() {
  359. if (showSecondsColumn)
  360. return [1, 1, 1];
  361. else
  362. return [1, 1, 0];
  363. }
  364. @override
  365. DateTime finalTime() {
  366. return currentTime.isUtc
  367. ? DateTime.utc(currentTime.year, currentTime.month, currentTime.day, _currentLeftIndex,
  368. _currentMiddleIndex, _currentRightIndex)
  369. : DateTime(currentTime.year, currentTime.month, currentTime.day, _currentLeftIndex,
  370. _currentMiddleIndex, _currentRightIndex);
  371. }
  372. }
  373. //a time picker model
  374. class Time12hPickerModel extends CommonPickerModel {
  375. Time12hPickerModel({DateTime currentTime, LocaleType locale}) : super(locale: locale) {
  376. this.currentTime = currentTime ?? DateTime.now();
  377. _currentLeftIndex = this.currentTime.hour % 12;
  378. _currentMiddleIndex = this.currentTime.minute;
  379. _currentRightIndex = this.currentTime.hour < 12 ? 0 : 1;
  380. }
  381. @override
  382. String leftStringAtIndex(int index) {
  383. if (index >= 0 && index < 12) {
  384. if (index == 0) {
  385. return digits(12, 2);
  386. } else {
  387. return digits(index, 2);
  388. }
  389. } else {
  390. return null;
  391. }
  392. }
  393. @override
  394. String middleStringAtIndex(int index) {
  395. if (index >= 0 && index < 60) {
  396. return digits(index, 2);
  397. } else {
  398. return null;
  399. }
  400. }
  401. @override
  402. String rightStringAtIndex(int index) {
  403. if (index == 0) {
  404. return i18nObjInLocale(this.locale)["am"];
  405. } else if (index == 1) {
  406. return i18nObjInLocale(this.locale)["pm"];
  407. } else {
  408. return null;
  409. }
  410. }
  411. @override
  412. String leftDivider() {
  413. return ":";
  414. }
  415. @override
  416. String rightDivider() {
  417. return ":";
  418. }
  419. @override
  420. List<int> layoutProportions() {
  421. return [1, 1, 1];
  422. }
  423. @override
  424. DateTime finalTime() {
  425. int hour = _currentLeftIndex + 12 * _currentRightIndex;
  426. return currentTime.isUtc
  427. ? DateTime.utc(
  428. currentTime.year, currentTime.month, currentTime.day, hour, _currentMiddleIndex, 0)
  429. : DateTime(
  430. currentTime.year, currentTime.month, currentTime.day, hour, _currentMiddleIndex, 0);
  431. }
  432. }
  433. // a date&time picker model
  434. class DateTimePickerModel extends CommonPickerModel {
  435. DateTime maxTime;
  436. DateTime minTime;
  437. DateTimePickerModel({DateTime currentTime, DateTime maxTime, DateTime minTime, LocaleType locale})
  438. : super(locale: locale) {
  439. if (currentTime != null) {
  440. this.currentTime = currentTime;
  441. if (maxTime != null &&
  442. (currentTime.isBefore(maxTime) || currentTime.isAtSameMomentAs(maxTime))) {
  443. this.maxTime = maxTime;
  444. }
  445. if (minTime != null &&
  446. (currentTime.isAfter(minTime) || currentTime.isAtSameMomentAs(minTime))) {
  447. this.minTime = minTime;
  448. }
  449. } else {
  450. this.maxTime = maxTime;
  451. this.minTime = minTime;
  452. var now = DateTime.now();
  453. if (this.minTime != null && this.minTime.isAfter(now)) {
  454. this.currentTime = this.minTime;
  455. } else if (this.maxTime != null && this.maxTime.isBefore(now)) {
  456. this.currentTime = this.maxTime;
  457. } else {
  458. this.currentTime = now;
  459. }
  460. }
  461. if (this.minTime != null && this.maxTime != null && this.maxTime.isBefore(this.minTime)) {
  462. // invalid
  463. this.minTime = null;
  464. this.maxTime = null;
  465. }
  466. _currentLeftIndex = 0;
  467. _currentMiddleIndex = this.currentTime.hour;
  468. _currentRightIndex = this.currentTime.minute;
  469. if (this.minTime != null && isAtSameDay(this.minTime, this.currentTime)) {
  470. _currentMiddleIndex = this.currentTime.hour - this.minTime.hour;
  471. if (_currentMiddleIndex == 0) {
  472. _currentRightIndex = this.currentTime.minute - this.minTime.minute;
  473. }
  474. }
  475. }
  476. bool isAtSameDay(DateTime day1, DateTime day2) {
  477. return day1 != null &&
  478. day2 != null &&
  479. day1.difference(day2).inDays == 0 &&
  480. day1.day == day2.day;
  481. }
  482. @override
  483. void setLeftIndex(int index) {
  484. // TODO: implement setLeftIndex
  485. super.setLeftIndex(index);
  486. DateTime time = currentTime.add(Duration(days: index));
  487. if (isAtSameDay(minTime, time)) {
  488. var index = min(24 - minTime.hour - 1, _currentMiddleIndex);
  489. this.setMiddleIndex(index);
  490. } else if (isAtSameDay(maxTime, time)) {
  491. var index = min(maxTime.hour, _currentMiddleIndex);
  492. this.setMiddleIndex(index);
  493. }
  494. }
  495. @override
  496. void setMiddleIndex(int index) {
  497. // TODO: implement setMiddleIndex
  498. super.setMiddleIndex(index);
  499. DateTime time = currentTime.add(Duration(days: _currentLeftIndex));
  500. if (isAtSameDay(minTime, time) && index == 0) {
  501. var maxIndex = 60 - minTime.minute - 1;
  502. if (_currentRightIndex > maxIndex) {
  503. _currentRightIndex = maxIndex;
  504. }
  505. } else if (isAtSameDay(maxTime, time) && _currentMiddleIndex == maxTime.hour) {
  506. var maxIndex = maxTime.minute;
  507. if (_currentRightIndex > maxIndex) {
  508. _currentRightIndex = maxIndex;
  509. }
  510. }
  511. }
  512. @override
  513. String leftStringAtIndex(int index) {
  514. DateTime time = currentTime.add(Duration(days: index));
  515. if (minTime != null && time.isBefore(minTime) && !isAtSameDay(minTime, time)) {
  516. return null;
  517. } else if (maxTime != null && time.isAfter(maxTime) && !isAtSameDay(maxTime, time)) {
  518. return null;
  519. }
  520. return formatDate(time, [ymdw], locale);
  521. }
  522. @override
  523. String middleStringAtIndex(int index) {
  524. if (index >= 0 && index < 24) {
  525. DateTime time = currentTime.add(Duration(days: _currentLeftIndex));
  526. if (isAtSameDay(minTime, time)) {
  527. if (index >= 0 && index < 24 - minTime.hour) {
  528. return digits(minTime.hour + index, 2);
  529. } else {
  530. return null;
  531. }
  532. } else if (isAtSameDay(maxTime, time)) {
  533. if (index >= 0 && index <= maxTime.hour) {
  534. return digits(index, 2);
  535. } else {
  536. return null;
  537. }
  538. }
  539. return digits(index, 2);
  540. }
  541. return null;
  542. }
  543. @override
  544. String rightStringAtIndex(int index) {
  545. if (index >= 0 && index < 60) {
  546. DateTime time = currentTime.add(Duration(days: _currentLeftIndex));
  547. if (isAtSameDay(minTime, time) && _currentMiddleIndex == 0) {
  548. if (index >= 0 && index < 60 - minTime.minute) {
  549. return digits(minTime.minute + index, 2);
  550. } else {
  551. return null;
  552. }
  553. } else if (isAtSameDay(maxTime, time) && _currentMiddleIndex >= maxTime.hour) {
  554. if (index >= 0 && index <= maxTime.minute) {
  555. return digits(index, 2);
  556. } else {
  557. return null;
  558. }
  559. }
  560. return digits(index, 2);
  561. }
  562. return null;
  563. }
  564. @override
  565. DateTime finalTime() {
  566. DateTime time = currentTime.add(Duration(days: _currentLeftIndex));
  567. var hour = _currentMiddleIndex;
  568. var minute = _currentRightIndex;
  569. if (isAtSameDay(minTime, time)) {
  570. hour += minTime.hour;
  571. if (minTime.hour == hour) {
  572. minute += minTime.minute;
  573. }
  574. }
  575. return currentTime.isUtc
  576. ? DateTime.utc(time.year, time.month, time.day, hour, minute)
  577. : DateTime(time.year, time.month, time.day, hour, minute);
  578. }
  579. @override
  580. List<int> layoutProportions() {
  581. return [4, 1, 1];
  582. }
  583. @override
  584. String rightDivider() {
  585. return ':';
  586. }
  587. }