record.dart 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189
  1. // ignore_for_file: unnecessary_brace_in_string_interps
  2. import 'dart:async';
  3. import 'dart:math';
  4. import 'package:fast/constants.dart';
  5. import 'package:fast/extension/layout.dart';
  6. import 'package:fast/model/model.dart';
  7. import 'package:fast/utils/global.dart';
  8. import 'package:fast/utils/size_fit.dart';
  9. import 'package:fast/utils/util.dart';
  10. import 'package:fast/view/component/fast_btn.dart';
  11. import 'package:fast/view/component/fast.dart';
  12. import 'package:fast/view/component/toast.dart';
  13. import 'package:flutter/material.dart';
  14. import 'alert_widget.dart';
  15. // ignore: must_be_immutable
  16. class Record extends Dialog {
  17. FastState fast;
  18. int begin;
  19. int end;
  20. int target;
  21. Record(
  22. {Key? key,
  23. required this.begin,
  24. required this.target,
  25. required this.end,
  26. required this.fast})
  27. : super(key: key);
  28. @override
  29. Widget build(BuildContext context) {
  30. EdgeInsets safePadding = MediaQuery.of(context).padding;
  31. return Container(
  32. decoration: const BoxDecoration(
  33. image: DecorationImage(
  34. image: AssetImage('assets/images/bg.png'), fit: BoxFit.cover)),
  35. alignment: Alignment.center,
  36. child: Stack(
  37. children: [
  38. GestureDetector(
  39. onTap: () {
  40. fast.closeRecordConfirm();
  41. Navigator.of(context).pop();
  42. },
  43. child: Container(
  44. padding: EdgeInsets.fromLTRB(12.px, 5.px, 12.px, 5.px),
  45. margin: EdgeInsets.only(left: 8.px, top: 54.px),
  46. child: Image.asset(
  47. 'assets/images/close.png',
  48. width: 20.px,
  49. height: 20.px,
  50. ),
  51. ),
  52. ),
  53. RecordTip(
  54. begin: begin,
  55. end: end,
  56. target: target,
  57. fast: fast,
  58. ),
  59. RecordDetail(
  60. top: safePadding.top,
  61. bottom: safePadding.bottom,
  62. begin: begin,
  63. end: end,
  64. target: target,
  65. fast: fast,
  66. )
  67. ],
  68. ),
  69. );
  70. }
  71. }
  72. // ignore: must_be_immutable
  73. class RecordTip extends StatefulWidget {
  74. FastState fast;
  75. int begin;
  76. int end;
  77. int target;
  78. RecordTip(
  79. {Key? key,
  80. required this.begin,
  81. required this.target,
  82. required this.end,
  83. required this.fast})
  84. : super(key: key);
  85. @override
  86. State<RecordTip> createState() => _RecordTipState();
  87. }
  88. class _RecordTipState extends State<RecordTip> with TickerProviderStateMixin {
  89. late AnimationController controller;
  90. late AnimationController controller2;
  91. late Animation<double> animation;
  92. late Animation moveAnimation;
  93. bool isRightTime = false;
  94. bool isOverTime = false;
  95. bool isOver24 = false;
  96. String rightStr = '';
  97. String overTimeStr = '';
  98. @override
  99. void initState() {
  100. super.initState();
  101. controller = AnimationController(
  102. vsync: this, duration: const Duration(milliseconds: 300));
  103. controller2 = AnimationController(
  104. vsync: this, duration: const Duration(milliseconds: 300));
  105. animation = Tween(begin: 0.0, end: 1.0).animate(controller);
  106. moveAnimation = Tween(begin: 0.0, end: 250.0).animate(controller2);
  107. controller.addListener(() {
  108. setState(() {});
  109. });
  110. controller2.addListener(() {
  111. setState(() {});
  112. });
  113. if ((widget.end - widget.target).abs() < 60) {
  114. isRightTime = true;
  115. int seconds = widget.target - widget.begin;
  116. int hour = seconds ~/ 3600;
  117. int minute = seconds % 3600 ~/ 60;
  118. rightStr = '时长 ${hour}小时${minute}分钟';
  119. }
  120. if (widget.end - widget.target > 60) {
  121. isOverTime = true;
  122. }
  123. if (widget.end - widget.begin > 3600 * 24) {
  124. isOver24 = true;
  125. }
  126. if (isOverTime) {
  127. DateTime time = DateTime.fromMillisecondsSinceEpoch(widget.target * 1000);
  128. String hour = time.hour.toString().padLeft(2, '0');
  129. String minute = time.minute.toString().padLeft(2, '0');
  130. int seconds = widget.end - widget.target;
  131. int hour2 = seconds ~/ 3600;
  132. int minute2 = seconds % 3600 ~/ 60;
  133. overTimeStr =
  134. '由于您未能在 ${hour}:${minute}断食结束后进行记\n录,目前已超时 ${hour2}小时 ${minute2}分钟';
  135. }
  136. Timer(const Duration(milliseconds: 300), () {
  137. controller.forward();
  138. });
  139. Timer(const Duration(milliseconds: 2300), () {
  140. controller2.forward();
  141. controller.reverse();
  142. });
  143. }
  144. @override
  145. void dispose() {
  146. controller.dispose();
  147. controller2.dispose();
  148. super.dispose();
  149. }
  150. @override
  151. Widget build(BuildContext context) {
  152. return Container(
  153. width: double.infinity,
  154. height: double.infinity,
  155. alignment: Alignment.center,
  156. child: Container(
  157. margin: EdgeInsets.only(bottom: moveAnimation.value + 150.px),
  158. height: 200,
  159. alignment: Alignment.center,
  160. child: ScaleTransition(
  161. scale: animation,
  162. child: Opacity(
  163. opacity: animation.value,
  164. child: Column(
  165. mainAxisAlignment: MainAxisAlignment.center,
  166. children: [
  167. if (isRightTime)
  168. const Text(
  169. '断食结束咯 🎉',
  170. //'恭喜您\n完成了预计断食',
  171. textAlign: TextAlign.center,
  172. style: TextStyle(
  173. color: kThemeColor,
  174. fontSize: 36,
  175. fontWeight: FontWeight.bold),
  176. ),
  177. if (isOverTime)
  178. Text(
  179. isOver24 ? '断食超过24小时咯❗️' : '断食超时咯❗️',
  180. // isOver24 ? '您断食超24小时了1' : '您断食超时了2',
  181. style: const TextStyle(
  182. color: kThemeColor,
  183. fontSize: 36,
  184. fontWeight: FontWeight.bold),
  185. ),
  186. SizedBox(
  187. height: 24.px,
  188. ),
  189. if (isRightTime)
  190. Text(
  191. // rightStr,
  192. '欢迎如约而至的你!你果然是个守时靠谱的小伙伴❤️ \n接下来,你可按原计划结束断食,也可提前或延后结束\n找到适合自己的断食时长后,就去参与断食挑战吧~',
  193. style: TextStyle(
  194. color: Colors.white, fontSize: 12.px, height: 1.5),
  195. ),
  196. if (isOverTime)
  197. Text(
  198. // overTimeStr,
  199. isOver24
  200. ? '等你等得好辛苦,下次别再迟到啦😂\n准时记录,将有助于你参与挑战时也能准时打卡哦'
  201. : '等你等得好辛苦,下次别再迟到啦😂\n准时记录,将有助于你参与断食挑战时也能准时打卡哦',
  202. textAlign: TextAlign.center,
  203. style: TextStyle(
  204. color: Colors.white, fontSize: 12.px, height: 1.5),
  205. ),
  206. ],
  207. ),
  208. ),
  209. ),
  210. ),
  211. );
  212. }
  213. }
  214. // ignore: must_be_immutable
  215. class RecordDetail extends StatefulWidget {
  216. FastState fast;
  217. double top;
  218. double bottom;
  219. int begin;
  220. int end;
  221. int target;
  222. RecordDetail(
  223. {Key? key,
  224. required this.top,
  225. required this.bottom,
  226. required this.begin,
  227. required this.end,
  228. required this.target,
  229. required this.fast})
  230. : super(key: key);
  231. @override
  232. State<RecordDetail> createState() => _RecordDetailState();
  233. }
  234. class _RecordDetailState extends State<RecordDetail>
  235. with SingleTickerProviderStateMixin {
  236. late AnimationController controller;
  237. late Animation alphaAnimation, topAnimation, bottomAnimation;
  238. bool isRightTime = false;
  239. bool isOverTime = false;
  240. bool isOver24 = false;
  241. bool isEarly = false;
  242. bool isExpandTime = false;
  243. int enterTime = 0;
  244. DateTime? endTime;
  245. int? expandSeconds;
  246. @override
  247. void initState() {
  248. enterTime = widget.end;
  249. controller = AnimationController(
  250. vsync: this, duration: const Duration(milliseconds: 300));
  251. alphaAnimation = Tween(begin: 0.0, end: 1.0).animate(controller);
  252. topAnimation =
  253. Tween(begin: 0.0, end: widget.top + 66.px).animate(controller);
  254. bottomAnimation = Tween(begin: 0.0, end: widget.bottom).animate(controller);
  255. controller.addListener(() {
  256. setState(() {});
  257. });
  258. if ((widget.end - widget.target).abs() < 60) {
  259. isRightTime = true;
  260. }
  261. if (widget.end - widget.target > 60) {
  262. isOverTime = true;
  263. }
  264. if (widget.end - widget.begin > 3600 * 24) {
  265. isOver24 = true;
  266. }
  267. if (widget.end - widget.target < -30) {
  268. isEarly = true;
  269. }
  270. Timer(Duration(milliseconds: isEarly ? 500 : 2500), () {
  271. controller.forward();
  272. });
  273. super.initState();
  274. }
  275. @override
  276. void dispose() {
  277. controller.dispose();
  278. super.dispose();
  279. }
  280. @override
  281. Widget build(BuildContext context) {
  282. return Container(
  283. alignment: Alignment.center,
  284. child: Flex(
  285. direction: Axis.vertical,
  286. mainAxisAlignment: MainAxisAlignment.center,
  287. children: [
  288. SizedBox(
  289. height: topAnimation.value,
  290. ),
  291. Opacity(
  292. opacity: alphaAnimation.value,
  293. child: Column(
  294. crossAxisAlignment: CrossAxisAlignment.center,
  295. children: [
  296. Image.asset(
  297. 'assets/images/newer.png',
  298. width: 298.px,
  299. height: 28.px,
  300. ),
  301. SizedBox(
  302. height: 6.px,
  303. ),
  304. Image.asset(
  305. 'assets/images/single_record.png',
  306. width: 179.px,
  307. height: 48.px,
  308. ),
  309. SizedBox(
  310. height: 40.px,
  311. ),
  312. RecordContent(
  313. detailState: this,
  314. ),
  315. ],
  316. ),
  317. ),
  318. const Expanded(child: SizedBox()),
  319. Opacity(
  320. opacity: alphaAnimation.value,
  321. child: Column(
  322. children: [
  323. FastBtn(
  324. title: '确定',
  325. disable: false,
  326. width: 228.px,
  327. height: 48.px,
  328. callback: () {
  329. if (Global().allowNotification == false &&
  330. Global().pushEnable) {
  331. Util().showNotificationStatus(context);
  332. return;
  333. }
  334. if (endTime != null) {
  335. if (isExpandTime) {
  336. widget.fast.expandTime(expandSeconds);
  337. } else {
  338. // Global().mainPage.showMe();
  339. widget.fast.confirmRecord(
  340. endTime!.millisecondsSinceEpoch ~/ 1000,
  341. isRightTime,
  342. enterTime);
  343. }
  344. widget.fast.closeRecordConfirm();
  345. Navigator.of(context).pop();
  346. }
  347. }),
  348. SizedBox(
  349. height: 16.px,
  350. ),
  351. GestureDetector(
  352. onTap: () {
  353. abandon();
  354. },
  355. child: Container(
  356. width: 104.px,
  357. height: 48.px,
  358. alignment: Alignment.center,
  359. child: Text(
  360. '放弃记录',
  361. style: TextStyle(
  362. color: const Color(0x99C4CCDA), fontSize: 14.px),
  363. ),
  364. ),
  365. )
  366. ],
  367. ),
  368. ),
  369. SizedBox(
  370. height: bottomAnimation.value,
  371. ),
  372. ],
  373. ),
  374. );
  375. }
  376. void updateEndTime(DateTime dateTime) {
  377. endTime = dateTime;
  378. }
  379. void updateExpandSeconds(int secs) {
  380. expandSeconds = secs;
  381. }
  382. void updateStatus(bool isExpand) {
  383. setState(() {
  384. isExpandTime = isExpand;
  385. });
  386. }
  387. void abandon() {
  388. showDialog(
  389. context: context,
  390. barrierDismissible: false,
  391. barrierColor: const Color(0xF2000D1F),
  392. builder: (BuildContext context) {
  393. return AlertWidget(
  394. title: '确定要放弃本次记录吗?',
  395. confirm: '确定',
  396. confirmCallback: () {
  397. widget.fast.closeRecordConfirm();
  398. Navigator.of(context).pop();
  399. giveupAndQuitPage();
  400. });
  401. });
  402. }
  403. void giveupAndQuitPage() {
  404. widget.fast.closeRecordConfirm();
  405. widget.fast.giveupRecord();
  406. Navigator.of(context).pop();
  407. }
  408. }
  409. // ignore: must_be_immutable
  410. class RecordContent extends StatefulWidget {
  411. _RecordDetailState detailState;
  412. RecordContent({Key? key, required this.detailState}) : super(key: key);
  413. @override
  414. State<RecordContent> createState() => _RecordContentState();
  415. }
  416. class _RecordContentState extends State<RecordContent>
  417. with TickerProviderStateMixin {
  418. late AnimationController ctrl1, ctrl2, ctrl3;
  419. late Animation expand1, expand2, expand3;
  420. late String startTime;
  421. late String endTime;
  422. late String betweenTime;
  423. late DateTime tempDate;
  424. bool allowMinus1 = false;
  425. bool allowAdd1 = false; //超时按钮调整时间
  426. int expandSeconds = 15 * 60;
  427. bool allowMinus2 = false;
  428. bool allowAdd2 = true;
  429. int selIndex = 0;
  430. @override
  431. void initState() {
  432. super.initState();
  433. ctrl1 = AnimationController(
  434. vsync: this, duration: const Duration(milliseconds: 300));
  435. expand1 = Tween(begin: 56.px, end: 153.px).animate(ctrl1);
  436. ctrl2 = AnimationController(
  437. vsync: this, duration: const Duration(milliseconds: 300));
  438. expand2 = Tween(begin: 56.px, end: 191.px).animate(ctrl2);
  439. ctrl3 = AnimationController(
  440. vsync: this, duration: const Duration(milliseconds: 300));
  441. expand3 = Tween(begin: 56.px, end: 161.px).animate(ctrl3);
  442. DateTime dtBegin = DateTime.fromMillisecondsSinceEpoch(
  443. widget.detailState.widget.begin * 1000);
  444. DateTime dtEnd;
  445. if (widget.detailState.widget.end - widget.detailState.widget.begin >
  446. 23 * 3600) {
  447. dtEnd = DateTime.fromMillisecondsSinceEpoch(
  448. widget.detailState.widget.begin * 1000 + 23 * 3600 * 1000);
  449. } else {
  450. dtEnd = DateTime.fromMillisecondsSinceEpoch(
  451. widget.detailState.widget.end * 1000);
  452. }
  453. DateTime dtTarget = DateTime.fromMillisecondsSinceEpoch(
  454. widget.detailState.widget.target * 1000);
  455. int minEnd =
  456. min(dtTarget.millisecondsSinceEpoch, dtEnd.millisecondsSinceEpoch);
  457. DateTime minEndDate = DateTime.fromMillisecondsSinceEpoch(minEnd);
  458. tempDate = dtEnd;
  459. startTime =
  460. '${dtBegin.hour.toString().padLeft(2, '0')}:${dtBegin.minute.toString().padLeft(2, '0')}';
  461. if (widget.detailState.isRightTime) {
  462. endTime =
  463. '${dtTarget.hour.toString().padLeft(2, '0')}:${dtTarget.minute.toString().padLeft(2, '0')}';
  464. int seconds =
  465. widget.detailState.widget.target - widget.detailState.widget.begin;
  466. betweenTime = '断食${seconds ~/ 3600}小时${(seconds % 3600) ~/ 60}分钟';
  467. tempDate = dtTarget;
  468. } else /*if (widget.detailState.isOverTime)*/ {
  469. endTime =
  470. '${dtEnd.hour.toString().padLeft(2, '0')}:${dtEnd.minute.toString().padLeft(2, '0')}';
  471. int seconds =
  472. widget.detailState.widget.end - widget.detailState.widget.begin;
  473. if (seconds > 23 * 3600) {
  474. seconds = 23 * 3600;
  475. }
  476. betweenTime = '断食${seconds ~/ 3600}小时${(seconds % 3600) ~/ 60}分钟';
  477. }
  478. if (serverTime().day == dtBegin.day) {
  479. startTime = '今天 ' + startTime;
  480. } else if (DateTime.fromMillisecondsSinceEpoch(
  481. serverTime().millisecondsSinceEpoch - 23 * 3600 * 1000)
  482. .day ==
  483. dtBegin.day) {
  484. startTime = '昨天 ' + startTime;
  485. } else {
  486. startTime = dtBegin.month.toString() +
  487. '.' +
  488. dtBegin.day.toString() +
  489. ' ' +
  490. startTime;
  491. }
  492. if (serverTime().day == dtEnd.day) {
  493. endTime = '今天 ' + endTime;
  494. } else if (DateTime.fromMillisecondsSinceEpoch(
  495. serverTime().millisecondsSinceEpoch - 23 * 3600 * 1000)
  496. .day ==
  497. dtEnd.day) {
  498. endTime = '昨天 ' + endTime;
  499. } else {
  500. endTime =
  501. dtEnd.month.toString() + '.' + dtEnd.day.toString() + ' ' + endTime;
  502. }
  503. ctrl1.addListener(() {
  504. setState(() {});
  505. });
  506. ctrl2.addListener(() {
  507. setState(() {});
  508. });
  509. ctrl3.addListener(() {
  510. setState(() {});
  511. });
  512. ctrl1.forward();
  513. tempDate = minEndDate;
  514. calulate();
  515. }
  516. DateTime serverTime() {
  517. int milliseconds = DateTime.now().millisecondsSinceEpoch;
  518. milliseconds = milliseconds + Global().timeSeconds * 1000;
  519. return DateTime.fromMillisecondsSinceEpoch(milliseconds);
  520. }
  521. calulate() {
  522. endTime =
  523. '${tempDate.hour.toString().padLeft(2, '0')}:${tempDate.minute.toString().padLeft(2, '0')}';
  524. // if (tempDate.day == DateTime.now().day) {
  525. // endTime = '今天' + endTime;
  526. // } else {
  527. // endTime = '昨天' + endTime;
  528. // }
  529. if (serverTime().day == tempDate.day) {
  530. endTime = '今天 ' + endTime;
  531. } else if (DateTime.fromMillisecondsSinceEpoch(
  532. serverTime().millisecondsSinceEpoch - 23 * 3600 * 1000)
  533. .day ==
  534. tempDate.day) {
  535. endTime = '昨天 ' + endTime;
  536. } else {
  537. endTime = tempDate.month.toString() +
  538. '.' +
  539. tempDate.day.toString() +
  540. ' ' +
  541. endTime;
  542. }
  543. int seconds = tempDate.millisecondsSinceEpoch ~/ 1000 -
  544. widget.detailState.widget.begin;
  545. if (seconds > 23 * 3600) {
  546. seconds = 23 * 3600;
  547. }
  548. betweenTime = '断食${seconds ~/ 3600}小时${(seconds % 3600) ~/ 60}分钟';
  549. allowAdd1 = false;
  550. allowMinus1 = false;
  551. if (seconds - 15 * 60 > 3600) {
  552. allowMinus1 = true;
  553. }
  554. if (widget.detailState.widget.end -
  555. tempDate.millisecondsSinceEpoch ~/ 1000 -
  556. 15 * 60 >=
  557. 0 &&
  558. seconds < 23 * 3600) {
  559. allowAdd1 = true;
  560. }
  561. widget.detailState.updateEndTime(tempDate);
  562. widget.detailState.updateExpandSeconds(expandSeconds);
  563. allowMinus2 = expandSeconds <= 15 * 60 ? false : true;
  564. FastBean currentFast = Global().currentFast!;
  565. int count =
  566. currentFast.endTime! - currentFast.startTime! + expandSeconds + 15 * 60;
  567. if (count >= 24 * 3600 || seconds >= 23 * 3600) {
  568. allowAdd2 = false;
  569. } else {
  570. allowAdd2 = true;
  571. }
  572. }
  573. @override
  574. void dispose() {
  575. ctrl1.dispose();
  576. ctrl2.dispose();
  577. ctrl3.dispose();
  578. super.dispose();
  579. }
  580. @override
  581. Widget build(BuildContext context) {
  582. if (widget.detailState.isRightTime) {
  583. return Column(
  584. children: [rightEnd(), earlyEnd(), delayEnd()],
  585. );
  586. }
  587. return otherEnd(); //otherEnd();
  588. }
  589. rightEnd() {
  590. FastBean currentFast = Global().currentFast!;
  591. int seconds = currentFast.endTime! - currentFast.startTime!;
  592. int hours = seconds ~/ 3600;
  593. int minutes = seconds ~/ 3600 ~/ 60;
  594. DateTime beginDateTime =
  595. DateTime.fromMillisecondsSinceEpoch(currentFast.startTime! * 1000);
  596. DateTime endDateTime =
  597. DateTime.fromMillisecondsSinceEpoch(currentFast.endTime! * 1000);
  598. return GestureDetector(
  599. onTap: () {
  600. ctrl1.forward();
  601. ctrl2.reverse();
  602. ctrl3.reverse();
  603. selIndex = 0;
  604. widget.detailState.updateStatus(false);
  605. },
  606. child: Container(
  607. height: expand1.value,
  608. width: 296.px,
  609. decoration: BoxDecoration(
  610. color: const Color(0xFF142133),
  611. borderRadius: BorderRadius.all(Radius.circular(36.px)),
  612. border: Border.all(
  613. color: selIndex == 0
  614. ? const Color(0x99AAFF00)
  615. : Colors.transparent,
  616. width: 2.px)),
  617. constraints: BoxConstraints(maxHeight: expand1.value),
  618. child: Stack(clipBehavior: Clip.antiAlias, children: [
  619. ListView(
  620. padding: EdgeInsets.zero,
  621. physics: const NeverScrollableScrollPhysics(),
  622. children: [
  623. Container(
  624. margin: EdgeInsets.only(left: 16.px, right: 16.px),
  625. decoration: BoxDecoration(
  626. border: Border(
  627. bottom: BorderSide(
  628. color: selIndex == 0
  629. ? const Color(0x1AC4CCDA)
  630. : Colors.transparent))),
  631. child: Row(
  632. children: [
  633. Container(
  634. margin: EdgeInsets.only(bottom: 2.px),
  635. child: Image.asset(
  636. selIndex == 0
  637. ? 'assets/images/checked.png'
  638. : 'assets/images/check.png',
  639. width: 24.px,
  640. height: 24.px,
  641. )),
  642. Expanded(
  643. child: Text('按计划结束断食',
  644. textAlign: TextAlign.center,
  645. style: TextStyle(
  646. color: selIndex == 0
  647. ? const Color(0xFFC4CCDA)
  648. : const Color(0x99C4CCDA),
  649. fontSize: 16.px,
  650. fontWeight: selIndex == 0
  651. ? FontWeight.bold
  652. : FontWeight.normal,
  653. height: 1.0)))
  654. ],
  655. ).height(56.px).margin(right: 24.px),
  656. ),
  657. Text(
  658. '持续${hours}小时${minutes}分钟',
  659. style: TextStyle(
  660. color: kThemeColor,
  661. fontSize: 28.px,
  662. fontWeight: FontWeight.w600,
  663. fontFamily: 'Exo2',
  664. height: 1.0),
  665. textAlign: TextAlign.center,
  666. ).marginTop(16.px),
  667. SizedBox(height: 12.px),
  668. Text(
  669. "${beginDateTime.hour.toString().padLeft(2, '0')}:${beginDateTime.minute.toString().padLeft(2, '0')}~${endDateTime.hour.toString().padLeft(2, '0')}:${endDateTime.minute.toString().padLeft(2, '0')}",
  670. style: TextStyle(
  671. color: const Color(0x99C4CCDA),
  672. fontSize: 14.px,
  673. fontFamily: 'Exo2',
  674. height: 1.0),
  675. textAlign: TextAlign.center,
  676. )
  677. ])
  678. ]),
  679. ),
  680. );
  681. }
  682. earlyEnd() {
  683. return GestureDetector(
  684. onTap: () {
  685. ctrl2.forward();
  686. ctrl1.reverse();
  687. ctrl3.reverse();
  688. selIndex = 1;
  689. widget.detailState.updateStatus(false);
  690. },
  691. child: Container(
  692. margin: EdgeInsets.only(top: 10.px),
  693. height: expand2.value,
  694. width: 296.px,
  695. decoration: BoxDecoration(
  696. color: const Color(0xFF142133),
  697. borderRadius: BorderRadius.all(Radius.circular(36.px)),
  698. border: Border.all(
  699. color: selIndex == 1
  700. ? const Color(0x99AAFF00)
  701. : Colors.transparent,
  702. width: 2.px)),
  703. constraints: BoxConstraints(maxHeight: expand2.value),
  704. child: Stack(clipBehavior: Clip.antiAlias, children: [
  705. ListView(
  706. padding: EdgeInsets.zero,
  707. physics: const NeverScrollableScrollPhysics(),
  708. children: [
  709. Container(
  710. margin: EdgeInsets.only(left: 16.px, right: 16.px),
  711. decoration: BoxDecoration(
  712. border: Border(
  713. bottom: BorderSide(
  714. color: selIndex == 1
  715. ? const Color(0x1AC4CCDA)
  716. : Colors.transparent))),
  717. child: Row(
  718. children: [
  719. Container(
  720. margin: EdgeInsets.only(bottom: 2.px),
  721. child: Image.asset(
  722. selIndex == 1
  723. ? 'assets/images/checked.png'
  724. : 'assets/images/check.png',
  725. width: 24.px,
  726. height: 24.px,
  727. )),
  728. Expanded(
  729. child: Text('已提前结束断食',
  730. textAlign: TextAlign.center,
  731. style: TextStyle(
  732. color: selIndex == 1
  733. ? const Color(0xFFC4CCDA)
  734. : const Color(0x99C4CCDA),
  735. fontWeight: selIndex == 1
  736. ? FontWeight.bold
  737. : FontWeight.normal,
  738. fontSize: 16.px,
  739. height: 1.0)))
  740. ],
  741. ).height(56.px).margin(right: 24.px),
  742. ),
  743. SizedBox(
  744. height: 16.px,
  745. ),
  746. Text(
  747. startTime,
  748. textAlign: TextAlign.center,
  749. style: TextStyle(
  750. color: const Color(0x99C4CCDA),
  751. fontSize: 16.px,
  752. fontFamily: 'Exo2',
  753. height: 1.0,
  754. fontWeight: FontWeight.w600),
  755. ),
  756. SizedBox(
  757. height: 4.px,
  758. ),
  759. // Text('~',
  760. // style: TextStyle(
  761. // color: const Color(0x99C4CCDA),
  762. // fontSize: 16.px,
  763. // fontFamily: 'Exo2',
  764. // height: 1.0,
  765. // fontWeight: FontWeight.w600)),
  766. Row(
  767. children: [
  768. SizedBox(
  769. width: 8.px,
  770. ),
  771. GestureDetector(
  772. onTap: () {
  773. if (allowMinus1) {
  774. setState(() {
  775. tempDate = DateTime.fromMillisecondsSinceEpoch(
  776. tempDate.millisecondsSinceEpoch -
  777. 15 * 60 * 1000);
  778. calulate();
  779. });
  780. }
  781. },
  782. child: Image.asset(
  783. allowMinus1
  784. ? 'assets/images/minus.png'
  785. : 'assets/images/minus_disable.png',
  786. width: 36.px,
  787. height: 36.px,
  788. ),
  789. ),
  790. Expanded(
  791. child: Text(
  792. endTime,
  793. textAlign: TextAlign.center,
  794. style: TextStyle(
  795. color: kThemeColor,
  796. fontSize: 28.px,
  797. fontFamily: 'Exo2',
  798. fontWeight: FontWeight.w600),
  799. )),
  800. GestureDetector(
  801. onTap: () {
  802. if (allowAdd1) {
  803. setState(() {
  804. tempDate = DateTime.fromMillisecondsSinceEpoch(
  805. tempDate.millisecondsSinceEpoch +
  806. 15 * 60 * 1000);
  807. calulate();
  808. });
  809. }
  810. },
  811. child: Image.asset(
  812. allowAdd1
  813. ? 'assets/images/plus.png'
  814. : 'assets/images/plus_disable.png',
  815. width: 36.px,
  816. height: 36.px,
  817. ),
  818. ),
  819. SizedBox(
  820. width: 8.px,
  821. ),
  822. ],
  823. ),
  824. Text(betweenTime,
  825. textAlign: TextAlign.center,
  826. style: TextStyle(
  827. color: const Color(0x99C4CCDA), fontSize: 14.px))
  828. ])
  829. ]),
  830. ),
  831. );
  832. }
  833. delayEnd() {
  834. FastBean currentFast = Global().currentFast!;
  835. // int seconds = currentFast.endTime! - currentFast.startTime!;
  836. // int hours = seconds ~/ 3600;
  837. // int minutes = seconds ~/ 3600 ~/ 60;
  838. // DateTime beginDateTime =
  839. // DateTime.fromMillisecondsSinceEpoch(currentFast.startTime! * 1000);
  840. DateTime endDateTime = DateTime.fromMillisecondsSinceEpoch(
  841. currentFast.endTime! * 1000 + expandSeconds * 1000);
  842. return GestureDetector(
  843. onTap: () {
  844. if (!allowAdd2 && !allowMinus1) {
  845. return;
  846. }
  847. ctrl3.forward();
  848. ctrl2.reverse();
  849. ctrl1.reverse();
  850. selIndex = 2;
  851. widget.detailState.updateStatus(true);
  852. },
  853. child: Opacity(
  854. opacity: !allowAdd2 && !allowMinus1 ? 0.7 : 1.0,
  855. child: Container(
  856. height: expand3.value,
  857. width: 296.px,
  858. margin: EdgeInsets.only(top: 10.px),
  859. decoration: BoxDecoration(
  860. color: const Color(0xFF142133),
  861. borderRadius: BorderRadius.all(Radius.circular(36.px)),
  862. border: Border.all(
  863. color: selIndex == 2
  864. ? const Color(0x99AAFF00)
  865. : Colors.transparent,
  866. width: 2.px)),
  867. constraints: BoxConstraints(maxHeight: expand3.value),
  868. child: Stack(clipBehavior: Clip.antiAlias, children: [
  869. ListView(
  870. padding: EdgeInsets.zero,
  871. physics: const NeverScrollableScrollPhysics(),
  872. children: [
  873. Container(
  874. margin: EdgeInsets.only(left: 16.px, right: 16.px),
  875. decoration: BoxDecoration(
  876. border: Border(
  877. bottom: BorderSide(
  878. color: selIndex == 2
  879. ? const Color(0x1AC4CCDA)
  880. : Colors.transparent))),
  881. child: Row(
  882. children: [
  883. Container(
  884. margin: EdgeInsets.only(bottom: 2.px),
  885. child: Image.asset(
  886. selIndex == 2
  887. ? 'assets/images/checked.png'
  888. : 'assets/images/check.png',
  889. width: 24.px,
  890. height: 24.px,
  891. ),
  892. ),
  893. Expanded(
  894. child: Text('要延后结束断食',
  895. textAlign: TextAlign.center,
  896. style: TextStyle(
  897. color: selIndex == 2
  898. ? const Color(0xFFC4CCDA)
  899. : const Color(0x99C4CCDA),
  900. fontSize: 16.px,
  901. fontWeight: selIndex == 2
  902. ? FontWeight.bold
  903. : FontWeight.normal,
  904. height: 1.0)))
  905. ],
  906. ).height(56.px).margin(right: 24.px),
  907. ),
  908. Row(
  909. children: [
  910. SizedBox(
  911. width: 8.px,
  912. ),
  913. GestureDetector(
  914. onTap: () {
  915. if (allowMinus2) {
  916. setState(() {
  917. expandSeconds = expandSeconds - 15 * 60;
  918. calulate();
  919. });
  920. }
  921. },
  922. child: Image.asset(
  923. allowMinus2
  924. ? 'assets/images/minus.png'
  925. : 'assets/images/minus_disable.png',
  926. width: 36.px,
  927. height: 36.px,
  928. ),
  929. ),
  930. Expanded(
  931. child: Text(
  932. '${expandSeconds ~/ 3600}小时${expandSeconds % 3600 ~/ 60}分钟',
  933. textAlign: TextAlign.center,
  934. style: TextStyle(
  935. color: kThemeColor,
  936. fontSize: 24.px,
  937. fontFamily: 'Exo2',
  938. fontWeight: FontWeight.w600),
  939. )),
  940. GestureDetector(
  941. onTap: () {
  942. if (allowAdd2) {
  943. setState(() {
  944. expandSeconds = expandSeconds + 15 * 60;
  945. calulate();
  946. });
  947. }
  948. },
  949. child: Image.asset(
  950. allowAdd2
  951. ? 'assets/images/plus.png'
  952. : 'assets/images/plus_disable.png',
  953. width: 36.px,
  954. height: 36.px,
  955. ),
  956. ),
  957. SizedBox(
  958. width: 8.px,
  959. ),
  960. ],
  961. ).marginTop(16.px),
  962. Text(
  963. "预计${endDateTime.hour.toString().padLeft(2, '0')}:${endDateTime.minute.toString().padLeft(2, '0')}结束断食",
  964. textAlign: TextAlign.center,
  965. style: TextStyle(
  966. color: const Color(0x99C4CCDA), fontSize: 14.px))
  967. ])
  968. ]),
  969. ),
  970. ),
  971. );
  972. }
  973. otherEnd() {
  974. return Container(
  975. width: 296.px,
  976. height: 191.px,
  977. constraints: BoxConstraints(maxHeight: 191.px),
  978. padding: EdgeInsets.only(left: 16.px, right: 16.px),
  979. decoration: BoxDecoration(
  980. borderRadius: BorderRadius.all(Radius.circular(32.px)),
  981. color: const Color(0xFF142133)),
  982. child: Stack(
  983. clipBehavior: Clip.hardEdge,
  984. children: [
  985. Column(
  986. children: [
  987. Container(
  988. height: 54.px,
  989. alignment: Alignment.center,
  990. child: Text(
  991. '结束断食时间',
  992. style: TextStyle(
  993. color: const Color(0xFFC4CCDA), fontSize: 16.px),
  994. ),
  995. decoration: const BoxDecoration(
  996. border: Border(
  997. bottom: BorderSide(color: Color(0x1AC4CCDA))))),
  998. SizedBox(
  999. height: 16.px,
  1000. ),
  1001. Text(
  1002. startTime,
  1003. style: TextStyle(
  1004. color: const Color(0x99C4CCDA),
  1005. fontSize: 16.px,
  1006. fontFamily: 'Exo2',
  1007. height: 1.0,
  1008. fontWeight: FontWeight.w600),
  1009. ),
  1010. SizedBox(
  1011. height: 4.px,
  1012. ),
  1013. // Text('~',
  1014. // style: TextStyle(
  1015. // color: const Color(0x99C4CCDA),
  1016. // fontSize: 16.px,
  1017. // fontFamily: 'Exo2',
  1018. // height: 1.0,
  1019. // fontWeight: FontWeight.w600)),
  1020. Row(
  1021. children: [
  1022. SizedBox(
  1023. width: 8.px,
  1024. ),
  1025. GestureDetector(
  1026. onTap: () {
  1027. if (allowMinus1) {
  1028. setState(() {
  1029. tempDate = DateTime.fromMillisecondsSinceEpoch(
  1030. tempDate.millisecondsSinceEpoch - 15 * 60 * 1000);
  1031. calulate();
  1032. });
  1033. } else {
  1034. showDialog(
  1035. context: context,
  1036. barrierDismissible: false,
  1037. barrierColor: Colors.transparent,
  1038. builder: (BuildContext context) {
  1039. return Toast(
  1040. title: '记录断食时长\n不能短于1小时哦',
  1041. showError: true,
  1042. content: const SizedBox(
  1043. width: 0,
  1044. ),
  1045. );
  1046. });
  1047. }
  1048. },
  1049. child: Image.asset(
  1050. allowMinus1
  1051. ? 'assets/images/minus.png'
  1052. : 'assets/images/minus_disable.png',
  1053. width: 36.px,
  1054. height: 36.px,
  1055. ),
  1056. ),
  1057. Expanded(
  1058. child: Text(
  1059. endTime,
  1060. textAlign: TextAlign.center,
  1061. style: TextStyle(
  1062. color: kThemeColor,
  1063. fontSize: 28.px,
  1064. fontFamily: 'Exo2',
  1065. fontWeight: FontWeight.w600),
  1066. )),
  1067. GestureDetector(
  1068. onTap: () {
  1069. if (allowAdd1) {
  1070. setState(() {
  1071. tempDate = DateTime.fromMillisecondsSinceEpoch(
  1072. tempDate.millisecondsSinceEpoch +
  1073. 15 * 60 * 1000);
  1074. calulate();
  1075. });
  1076. } else {
  1077. int seconds =
  1078. tempDate.millisecondsSinceEpoch ~/ 1000 -
  1079. widget.detailState.widget.begin;
  1080. if (seconds >= 23 * 3600) {
  1081. showDialog(
  1082. context: context,
  1083. barrierDismissible: false,
  1084. barrierColor: Colors.transparent,
  1085. builder: (BuildContext context) {
  1086. return Toast(
  1087. title: '记录断食时长\n不能超过23小时哦',
  1088. showError: true,
  1089. content: const SizedBox(
  1090. width: 0,
  1091. ),
  1092. );
  1093. });
  1094. }
  1095. }
  1096. },
  1097. child: Image.asset(
  1098. allowAdd1
  1099. ? 'assets/images/plus.png'
  1100. : 'assets/images/plus_disable.png',
  1101. width: 36.px,
  1102. height: 36.px,
  1103. )),
  1104. SizedBox(
  1105. width: 8.px,
  1106. ),
  1107. ],
  1108. ),
  1109. Text(betweenTime,
  1110. style: TextStyle(
  1111. color: const Color(0x99C4CCDA), fontSize: 14.px))
  1112. ],
  1113. )
  1114. ],
  1115. ),
  1116. );
  1117. }
  1118. }