challenge_checkout.dart 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  1. import 'dart:async';
  2. import 'package:fast/model/model.dart';
  3. import 'package:fast/utils/api.dart';
  4. import 'package:fast/utils/global.dart';
  5. import 'package:fast/utils/http_utils.dart';
  6. import 'package:fast/utils/size_fit.dart';
  7. import 'package:fast/utils/util.dart';
  8. import 'package:fast/view/component/challenge_result.dart';
  9. import 'package:fast/view/component/fast.dart';
  10. import 'package:fast/view/component/fast_btn.dart';
  11. import 'package:fast/view/component/toast.dart';
  12. import 'package:flutter/material.dart';
  13. // ignore: must_be_immutable
  14. GlobalKey<CheckinItemState> checkinKey = GlobalKey();
  15. // ignore: must_be_immutable
  16. class FastCheckout extends StatefulWidget {
  17. bool abandon;
  18. bool supertimeout;
  19. int timestamp;
  20. Map<String, dynamic> checkInfo;
  21. FastBean fast;
  22. FastCheckout(
  23. {Key? key,
  24. required this.abandon,
  25. required this.supertimeout,
  26. required this.fast,
  27. required this.timestamp,
  28. required this.checkInfo})
  29. : super(key: key);
  30. @override
  31. State<FastCheckout> createState() => _FastCheckoutState();
  32. }
  33. class _FastCheckoutState extends State<FastCheckout>
  34. with TickerProviderStateMixin {
  35. late AnimationController controller2, controller3, controller4;
  36. late Animation aniStep2;
  37. late AnimationController animationController;
  38. late Animation step0, step1, step2, step3, step4, step5, step6;
  39. int step = 0; //动画执行步骤
  40. bool rightTime = false;
  41. FastResultBean? resultBean;
  42. List checkList = [];
  43. Map<String, dynamic>? users;
  44. bool btnEnable = true;
  45. int earnings = 0;
  46. int index = 1;
  47. int win = 0; //判断是否准时签到(开始断食签到和结束断食签到都准时才算准时)
  48. late DateTime date;
  49. String desc1 = '', desc2 = '', desc3 = '';
  50. @override
  51. void initState() {
  52. super.initState();
  53. date = serverTime();
  54. controller2 = AnimationController(
  55. vsync: this, duration: const Duration(milliseconds: 300));
  56. controller3 = AnimationController(
  57. vsync: this, duration: const Duration(milliseconds: 1200));
  58. controller4 = AnimationController(
  59. vsync: this, duration: const Duration(milliseconds: 300));
  60. animationController = AnimationController(
  61. vsync: this, duration: const Duration(milliseconds: 3000));
  62. step0 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
  63. parent: animationController,
  64. curve: const Interval(0.0, 300.0 / 3000.0)));
  65. step1 = Tween(begin: 1.0, end: 1.1).animate(CurvedAnimation(
  66. parent: animationController,
  67. curve: const Interval(300.0 / 3000.0, 1500.0 / 3000.0)));
  68. step2 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
  69. parent: animationController,
  70. curve: const Interval(1500 / 3000, 1800.0 / 3000.0)));
  71. step3 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
  72. parent: animationController,
  73. curve: const Interval(1800 / 3000, 2100.0 / 3000.0)));
  74. step4 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
  75. parent: animationController,
  76. curve: const Interval(2100.0 / 3000, 2400.0 / 3000.0)));
  77. step5 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
  78. parent: animationController,
  79. curve: const Interval(2400.0 / 3000, 2700.0 / 3000.0)));
  80. step6 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
  81. parent: controller4,
  82. curve: const Interval(0.0, 1.0)));
  83. step0.addListener(() {
  84. if (step0.value == 1.0) {
  85. setState(() {
  86. step = 1;
  87. });
  88. } else {}
  89. });
  90. step1.addListener(() {
  91. if (step1.value == 1.1) {
  92. setState(() {
  93. step = 2;
  94. });
  95. }
  96. });
  97. animationController.addListener(() {
  98. setState(() {});
  99. });
  100. animationController.forward();
  101. aniStep2 = Tween(begin: 1.0, end: 0.0).animate(controller2);
  102. controller2.addListener(() {
  103. setState(() {});
  104. });
  105. controller3.addListener(() {
  106. setState(() {});
  107. });
  108. controller4.addListener(() {
  109. setState(() {});
  110. });
  111. Timer(const Duration(milliseconds: 2000), () {
  112. setState(() {
  113. step = 1;
  114. controller2.forward();
  115. });
  116. });
  117. getCheckinData();
  118. getUsers();
  119. if (widget.abandon) {
  120. giveUp();
  121. }
  122. }
  123. void getCheckinData() {
  124. Map<String, dynamic> data = widget.checkInfo;
  125. setState(() {
  126. rightTime = data['checkout_status'] == 'SUCCESS';
  127. win = data['checkout_rjv'];
  128. if (data['checkout_status'] == 'SUCCESS') {
  129. desc1 = '第${data['current_day_index']}天结束断食';
  130. desc2 = Util().checkFormateDate(
  131. DateTime.fromMillisecondsSinceEpoch(
  132. (data['start_time'] as int) * 1000),
  133. serverTime()) +
  134. '~' +
  135. Util().checkFormateDate(
  136. DateTime.fromMillisecondsSinceEpoch(
  137. (data['end_time'] as int) * 1000),
  138. serverTime()) +
  139. ',已断食' +
  140. Util().betweenTime(
  141. DateTime.fromMillisecondsSinceEpoch(
  142. (data['start_time'] as int) * 1000),
  143. DateTime.fromMillisecondsSinceEpoch(
  144. (data['end_time'] as int) * 1000));
  145. desc3 = Util().checkFormateDate(
  146. DateTime.fromMillisecondsSinceEpoch(
  147. (data['checkout_expire'] as int) * 1000),
  148. serverTime()) +
  149. '前完成打卡,视为准时打卡';
  150. } else {
  151. desc1 = '第${data['current_day_index']}天结束断食';
  152. desc2 = Util().checkFormateDate(
  153. DateTime.fromMillisecondsSinceEpoch(
  154. (data['start_time'] as int) * 1000),
  155. serverTime()) +
  156. '~' +
  157. Util().checkFormateDate(
  158. DateTime.fromMillisecondsSinceEpoch(
  159. (data['end_time'] as int) * 1000),
  160. serverTime()) +
  161. ',已断食' +
  162. Util().betweenTime(
  163. DateTime.fromMillisecondsSinceEpoch(
  164. (data['start_time'] as int) * 1000),
  165. DateTime.fromMillisecondsSinceEpoch(
  166. (data['end_time'] as int) * 1000));
  167. desc3 = Util().checkFormateDate(
  168. DateTime.fromMillisecondsSinceEpoch(
  169. (data['checkout_invalid'] as int) * 1000),
  170. serverTime()) +
  171. '前完成打卡,视为超时打卡';
  172. }
  173. if (widget.supertimeout) {
  174. desc1 = '第${data['current_day_index']}天结束断食·打卡失效\n挑战终止,欢迎再战';
  175. desc3 = Util().checkFormateDate(
  176. DateTime.fromMillisecondsSinceEpoch(
  177. (data['checkout_invalid'] as int) * 1000),
  178. serverTime()) +
  179. '前未完成打卡,打卡已失效';
  180. }
  181. });
  182. List checkinDays = data['checkin_days'];
  183. List<ChallengeCheckinBean> list = [];
  184. for (int i = 0; i < data['days']; i++) {
  185. ChallengeCheckinBean bean = ChallengeCheckinBean();
  186. bean.day = i + 1;
  187. if (i < checkinDays.length &&
  188. checkinDays[i]['checkout_status'] != 'PENDING') {
  189. bean.stone = checkinDays[i]['rjv_earnings'];
  190. bean.passed = true;
  191. }
  192. if (i == data['current_day_index'] - 1) {
  193. bean.today = true;
  194. if (widget.abandon || widget.supertimeout) {
  195. bean.abandon = true;
  196. }
  197. }
  198. list.add(bean);
  199. }
  200. List array = data['days'] == 7
  201. ? [
  202. [list[0], list[1], list[2], list[3]],
  203. [list[4], list[5], list[6]]
  204. ]
  205. : [list];
  206. setState(() {
  207. checkList = array;
  208. index = data['current_day_index'];
  209. earnings = data['rjv_earnings'];
  210. });
  211. }
  212. Future getUsers() async {
  213. Map<String, dynamic> data = await HttpUtils.get(Api.overalls);
  214. setState(() {
  215. users = data;
  216. });
  217. }
  218. Future giveUp() async {
  219. Map<String, dynamic> data = await HttpUtils.post(Api.end,
  220. data: {'real_end_time': serverTime().millisecondsSinceEpoch ~/ 1000});
  221. resultBean = FastResultBean.fromJson(data);
  222. }
  223. DateTime serverTime() {
  224. int milliseconds = DateTime.now().millisecondsSinceEpoch;
  225. milliseconds = milliseconds + Global().timeSeconds * 1000;
  226. return DateTime.fromMillisecondsSinceEpoch(milliseconds);
  227. }
  228. @override
  229. void dispose() {
  230. controller2.dispose();
  231. controller3.dispose();
  232. controller4.dispose();
  233. animationController.dispose();
  234. super.dispose();
  235. }
  236. @override
  237. Widget build(BuildContext context) {
  238. double screenHeight = MediaQuery.of(context).size.height;
  239. List<Widget> widgets = [];
  240. for (int i = 0; i < earnings; i++) {
  241. widgets.add(Container(
  242. margin: EdgeInsets.only(left: 2.px),
  243. child: Image.asset(
  244. 'assets/images/stone.png',
  245. width: 12.px,
  246. height: 12.px,
  247. ),
  248. ));
  249. }
  250. double value1 = step == 0 ? step0.value : (1.0 - step2.value);
  251. double valueTop = step == 0
  252. ? 0.14 * screenHeight + 0.1 * screenHeight * step0.value
  253. : 0.14 * screenHeight + 0.1 * screenHeight * (1 - step2.value);
  254. return Container(
  255. decoration: BoxDecoration(
  256. image: DecorationImage(
  257. image: AssetImage('assets/images/${widget.fast.days}days_bg.png'),
  258. fit: BoxFit.cover),
  259. ),
  260. alignment: Alignment.center,
  261. child: Column(
  262. children: [
  263. Opacity(
  264. opacity: step0.value,
  265. child: Container(
  266. margin: EdgeInsets.only(top: valueTop),
  267. child: Column(
  268. children: [
  269. Opacity(
  270. opacity: 0.4,
  271. child: Image.asset(
  272. 'assets/images/challenge_mode.png',
  273. width: 298.px,
  274. height: 28.px,
  275. ),
  276. ),
  277. SizedBox(
  278. height: 12.px,
  279. ),
  280. Image.asset(
  281. 'assets/images/challenge_${widget.fast.days}.png',
  282. width: 207.px,
  283. height: 96.px,
  284. )
  285. ],
  286. ),
  287. ),
  288. ),
  289. Opacity(
  290. opacity: step3.value,
  291. child: Container(
  292. margin: EdgeInsets.only(top: 20.px * step3.value),
  293. child: Column(
  294. mainAxisAlignment: MainAxisAlignment.center,
  295. children: List<Widget>.generate(
  296. checkList.length,
  297. (index) {
  298. return Container(
  299. height: 50.px,
  300. width: checkList[index].length * 50.px,
  301. margin: EdgeInsets.only(bottom: 8.px),
  302. decoration: BoxDecoration(
  303. color: const Color.fromARGB(38, 196, 204, 218),
  304. borderRadius:
  305. BorderRadius.all(Radius.circular(25.px))),
  306. child: Row(
  307. children: List<Widget>.generate(
  308. checkList[index].length, (j) {
  309. return CheckinItem(
  310. key: checkList[index][j].today
  311. ? checkinKey
  312. : null,
  313. bean: checkList[index][j],
  314. isCheckin: false,
  315. );
  316. }),
  317. ),
  318. );
  319. },
  320. ),
  321. ))),
  322. Opacity(
  323. opacity: step4.value,
  324. child: Container(
  325. alignment: Alignment.center,
  326. // margin: EdgeInsets.only(top: 24.px * aniDesc.value),
  327. child: Column(
  328. mainAxisAlignment: MainAxisAlignment.center,
  329. children: [
  330. SizedBox(
  331. height: 12.px * step4.value,
  332. ),
  333. if (widget.abandon)
  334. Text(
  335. '挑战终止,欢迎再战',
  336. style: TextStyle(
  337. decoration: TextDecoration.none,
  338. color: const Color(0xFFFF0000),
  339. fontSize: 14.px),
  340. ),
  341. if (widget.supertimeout)
  342. Text(
  343. desc1,
  344. textAlign: TextAlign.center,
  345. style: TextStyle(
  346. decoration: TextDecoration.none,
  347. color: const Color(0xFFFF0000),
  348. fontSize: 14.px),
  349. ),
  350. if (!widget.abandon && !widget.supertimeout)
  351. Column(
  352. children: [
  353. Text(
  354. desc1,
  355. style: TextStyle(
  356. color: const Color(0xFFC4CCDA), fontSize: 16.px),
  357. ),
  358. SizedBox(
  359. height: 2.px,
  360. ),
  361. Text(
  362. desc2,
  363. style: TextStyle(
  364. color: const Color(0x99C4CCDA), fontSize: 12.px),
  365. )
  366. ],
  367. )
  368. ],
  369. ),
  370. ),
  371. ),
  372. const Expanded(child: SizedBox()),
  373. if (users != null)
  374. Opacity(
  375. opacity: step6.value,
  376. child: Stack(
  377. alignment: AlignmentDirectional.topStart,
  378. children: [
  379. bottomAvatar(4, const Color(0xCC080C1A)),
  380. bottomAvatar(3, const Color(0x99080C1A)),
  381. bottomAvatar(2, const Color(0x66080C1A)),
  382. bottomAvatar(1, const Color(0x33080C1A)),
  383. bottomAvatar(0, const Color(0x00080C1A)),
  384. Container(
  385. margin: EdgeInsets.fromLTRB(
  386. 12 * 4.px + 24.px + 8.px, 1.px, 0, 0),
  387. height: 16.px,
  388. child: Text(
  389. users!['fasting_count'].toString() + '人次已完成挑战',
  390. style: TextStyle(
  391. color: const Color(0x66FFFFFF),
  392. fontSize: 14.px,
  393. ),
  394. ),
  395. )
  396. ],
  397. ),
  398. ),
  399. SizedBox(
  400. height: 24.px,
  401. ),
  402. Opacity(
  403. opacity: step4.value,
  404. child: Column(
  405. children: [
  406. if (widget.supertimeout || widget.abandon)
  407. FastBtn(
  408. title: '确定',
  409. disable: btnEnable ? false : true,
  410. width: 228.px,
  411. height: 48.px,
  412. callback: () {
  413. if (Global().allowNotification == false &&
  414. Global().pushEnable) {
  415. Util().showNotificationStatus(context);
  416. return;
  417. }
  418. touchBtn();
  419. }),
  420. if (!widget.supertimeout && !widget.abandon)
  421. FastBtn(
  422. title: '确定',
  423. img: Image.asset(
  424. 'assets/images/checkout.png',
  425. width: 132.px,
  426. height: 26.px,
  427. ),
  428. disable: btnEnable ? false : true,
  429. width: 228.px,
  430. height: 48.px,
  431. callback: () {
  432. if (Global().allowNotification == false &&
  433. Global().pushEnable) {
  434. Util().showNotificationStatus(context);
  435. return;
  436. }
  437. touchBtn();
  438. }),
  439. SizedBox(
  440. height: 17.px,
  441. ),
  442. if (!widget.abandon)
  443. Text(
  444. desc3,
  445. style: TextStyle(
  446. color: const Color(0x99C4CCDA), fontSize: 12.px),
  447. ),
  448. SizedBox(
  449. height: 0.07 * screenHeight * step4.value,
  450. )
  451. ],
  452. ),
  453. ),
  454. ],
  455. ),
  456. );
  457. }
  458. bottomAvatar(int index, Color color) {
  459. if (users!['recent_users'].length > index) {
  460. return Container(
  461. width: 24.px,
  462. height: 24.px,
  463. margin: EdgeInsets.fromLTRB(index * 12.px, 0, 0, 0),
  464. child: Stack(
  465. children: [
  466. ClipOval(
  467. child: Image.network(
  468. users!['recent_users'][index]['avatar'],
  469. width: 24.px,
  470. height: 24.px,
  471. fit: BoxFit.cover,
  472. ),
  473. ),
  474. Container(
  475. width: 24.px,
  476. height: 24.px,
  477. decoration: BoxDecoration(
  478. color: color,
  479. shape: BoxShape.circle,
  480. border: Border.all(color: const Color(0xFF000D26))),
  481. )
  482. ],
  483. ),
  484. );
  485. }
  486. return SizedBox(
  487. width: 0.px,
  488. height: 0.px,
  489. );
  490. }
  491. touchBtn() {
  492. if (widget.abandon) {
  493. endConfirm();
  494. } else {
  495. // checkinKey.currentState!.checkin(rightTime);
  496. if (widget.supertimeout) {
  497. superTimeoutEnd();
  498. } else {
  499. checkout();
  500. }
  501. setState(() {
  502. btnEnable = false;
  503. });
  504. }
  505. }
  506. Future endConfirm() async {
  507. await HttpUtils.post(Api.endConfirm, data: {});
  508. showResultPage();
  509. }
  510. Future superTimeoutEnd() async {
  511. Map<String, dynamic> data = await HttpUtils.post(Api.checkout,
  512. data: {'day_index': index, 'checkout_time': widget.timestamp});
  513. resultBean = FastResultBean.fromJson(data);
  514. showResultPage();
  515. }
  516. Future checkout() async {
  517. Map<String, dynamic> data = await HttpUtils.post(Api.checkout,
  518. data: {'day_index': index, 'checkout_time': widget.timestamp});
  519. controller4.forward();
  520. resultBean = FastResultBean.fromJson(data);
  521. if (widget.supertimeout) {
  522. showResultPage();
  523. return;
  524. }
  525. checkinKey.currentState!.checkout(rightTime, win);
  526. if (resultBean!.over!) {
  527. Timer(const Duration(milliseconds: 2500), () {
  528. showResultPage();
  529. });
  530. } else {
  531. Timer(const Duration(milliseconds: 2500), () {
  532. Navigator.of(context).pop();
  533. Global().homePage.closeCheckPop();
  534. if (rightTime) {
  535. //正常打卡
  536. showDialog(
  537. context: context,
  538. barrierDismissible: false,
  539. barrierColor: Colors.transparent,
  540. builder: (BuildContext context) {
  541. return Toast(
  542. title: '准时打卡',
  543. content: win > 0
  544. ? Row(
  545. mainAxisAlignment: MainAxisAlignment.center,
  546. children: [
  547. Text(
  548. '+$win',
  549. style: TextStyle(
  550. color: Colors.white,
  551. fontSize: 16.px,
  552. fontWeight: FontWeight.w800,
  553. fontFamily: 'Exo2',
  554. decoration: TextDecoration.none),
  555. ),
  556. SizedBox(
  557. width: 3.px,
  558. ),
  559. Image.asset(
  560. 'assets/images/stone.png',
  561. width: 24.px,
  562. height: 24.px,
  563. )
  564. ],
  565. )
  566. : const SizedBox(
  567. width: 0,
  568. ),
  569. );
  570. });
  571. } else {
  572. showDialog(
  573. context: context,
  574. barrierDismissible: false,
  575. barrierColor: Colors.transparent,
  576. builder: (BuildContext context) {
  577. return Toast(
  578. title: '超时打卡',
  579. content: const SizedBox(
  580. width: 0,
  581. ),
  582. );
  583. });
  584. //超时打卡
  585. }
  586. });
  587. Global().homePage.getDatas();
  588. if (Global().fastKey!.currentContext != null) {
  589. FastState seekBar = Global().fastKey!.currentState as FastState;
  590. seekBar.reset();
  591. }
  592. }
  593. }
  594. showResultPage() {
  595. Navigator.of(context).pop();
  596. Global().homePage.closeCheckPop();
  597. showDialog(
  598. context: context,
  599. barrierDismissible: false,
  600. barrierColor: Colors.transparent,
  601. useSafeArea: false,
  602. builder: (BuildContext context) {
  603. return ChallengeResult(resultBean: resultBean!);
  604. });
  605. }
  606. }
  607. // ignore: must_be_immutable
  608. class CheckinItem extends StatefulWidget {
  609. ChallengeCheckinBean bean;
  610. bool isCheckin;
  611. CheckinItem({Key? key, required this.bean, required this.isCheckin})
  612. : super(key: key);
  613. @override
  614. State<CheckinItem> createState() => CheckinItemState();
  615. }
  616. class CheckinItemState extends State<CheckinItem>
  617. with SingleTickerProviderStateMixin {
  618. late AnimationController controller;
  619. late Animation scaleAni, scaleAni2;
  620. bool isRightTime = false;
  621. bool ing = false;
  622. @override
  623. void initState() {
  624. controller = AnimationController(
  625. vsync: this, duration: const Duration(milliseconds: 1600));
  626. TweenSequenceItem item0 =
  627. TweenSequenceItem(tween: Tween(begin: 0.0, end: 1.5), weight: 2);
  628. TweenSequenceItem item1 =
  629. TweenSequenceItem(tween: Tween(begin: 1.5, end: 1.4), weight: 1);
  630. TweenSequenceItem item2 =
  631. TweenSequenceItem(tween: Tween(begin: 1.45, end: 1.45), weight: 20);
  632. TweenSequenceItem item3 =
  633. TweenSequenceItem(tween: Tween(begin: 1.45, end: 1.0), weight: 1);
  634. TweenSequence tweenSequence = TweenSequence([item0, item1, item2, item3]);
  635. scaleAni = tweenSequence.animate(
  636. CurvedAnimation(parent: controller, curve: const Interval(0.0, 0.95)));
  637. TweenSequenceItem a0 =
  638. TweenSequenceItem(tween: Tween(begin: 1.0, end: 0.9), weight: 1);
  639. TweenSequenceItem a1 =
  640. TweenSequenceItem(tween: Tween(begin: 0.9, end: 1.0), weight: 1);
  641. TweenSequence tween2 = TweenSequence([a0, a1]);
  642. scaleAni2 = tween2.animate(
  643. CurvedAnimation(parent: controller, curve: const Interval(0.9, 1.0)));
  644. controller.addListener(() {
  645. setState(() {});
  646. });
  647. // Timer(const Duration(seconds: 3), () {
  648. // if (widget.isToday) {
  649. // controller.forward();
  650. // }
  651. // });
  652. super.initState();
  653. }
  654. checkin(bool rightTime) {
  655. setState(() {
  656. if (rightTime) {
  657. isRightTime = true;
  658. } else {
  659. isRightTime = false;
  660. }
  661. // isRightTime = rightTime;
  662. if (widget.isCheckin) {
  663. ing = true;
  664. }
  665. });
  666. if (widget.bean.today) {
  667. controller.forward();
  668. }
  669. }
  670. checkout(bool rightTime, int win) {
  671. setState(() {
  672. if (rightTime && win > 0) {
  673. isRightTime = true;
  674. } else {
  675. isRightTime = false;
  676. }
  677. // isRightTime = rightTime;
  678. if (widget.isCheckin) {
  679. ing = true;
  680. }
  681. });
  682. if (widget.bean.today) {
  683. controller.forward();
  684. }
  685. }
  686. @override
  687. void dispose() {
  688. controller.dispose();
  689. super.dispose();
  690. }
  691. @override
  692. Widget build(BuildContext context) {
  693. String iconPath = 'assets/images/checkin_undone.png';
  694. String day = widget.bean.day.toString();
  695. if (widget.bean.abandon) {
  696. iconPath = 'assets/images/checkin_fail.png';
  697. } else {
  698. if (widget.bean.passed) {
  699. iconPath = 'assets/images/checkin_done.png';
  700. } else if (widget.bean.today) {
  701. iconPath = widget.isCheckin
  702. ? 'assets/images/checkin_undone.png'
  703. : 'assets/images/checkin_today.png';
  704. } else {
  705. iconPath = 'assets/images/checkin_undone.png';
  706. }
  707. }
  708. String today = '';
  709. if (ing) {
  710. today = day;
  711. day = '';
  712. }
  713. if (widget.bean.passed || (widget.bean.abandon && widget.bean.today)) {
  714. day = '';
  715. }
  716. double opacity = (widget.bean.today && !widget.isCheckin) ? 1.0 : 0.5;
  717. if (ing) {
  718. opacity = 1.0;
  719. }
  720. return Opacity(
  721. opacity: opacity,
  722. child: Stack(
  723. children: [
  724. Opacity(
  725. opacity: ing ? 0.0 : 1.0,
  726. child: Container(
  727. width: 50.px,
  728. height: 50.px,
  729. alignment: Alignment.center,
  730. decoration: BoxDecoration(
  731. image: DecorationImage(
  732. image: AssetImage(iconPath), fit: BoxFit.cover)),
  733. child: Text(
  734. day,
  735. style: TextStyle(
  736. color: (widget.bean.today && !widget.isCheckin)
  737. ? Colors.white
  738. : const Color(0x66C4CCDA),
  739. fontFamily: 'Exo2',
  740. fontWeight: FontWeight.w600,
  741. fontSize: 16.px,
  742. decoration: TextDecoration.none),
  743. ),
  744. ),
  745. ),
  746. if (widget.bean.passed && widget.bean.stone > 0)
  747. Positioned(
  748. bottom: 5.px,
  749. left: 11.px,
  750. child: Container(
  751. width: 28.px,
  752. height: 14.px,
  753. alignment: Alignment.center,
  754. decoration: BoxDecoration(
  755. borderRadius: BorderRadius.all(Radius.circular(7.px)),
  756. color: const Color(0xFF050F1A)),
  757. child: Row(
  758. mainAxisAlignment: MainAxisAlignment.center,
  759. children: [
  760. Text(
  761. '+' + widget.bean.stone.toString(),
  762. style: TextStyle(
  763. color: Colors.white,
  764. fontSize: 8.px,
  765. fontFamily: 'Exo2',
  766. decoration: TextDecoration.none),
  767. ),
  768. Image.asset(
  769. 'assets/images/stone.png',
  770. width: 10.px,
  771. height: 10.px,
  772. )
  773. ],
  774. ),
  775. )),
  776. Transform.scale(
  777. scale: scaleAni.value,
  778. child: Container(
  779. width: 50.px,
  780. height: 50.px,
  781. alignment: widget.isCheckin
  782. ? Alignment.center
  783. : Alignment.bottomCenter,
  784. decoration: BoxDecoration(
  785. image: DecorationImage(
  786. image: widget.isCheckin
  787. ? const AssetImage(
  788. 'assets/images/checkin_today.png')
  789. : const AssetImage(
  790. 'assets/images/checked_today.png'),
  791. fit: BoxFit.cover)),
  792. child: widget.isCheckin
  793. ? Text(
  794. today,
  795. style: TextStyle(
  796. color: Colors.white,
  797. fontFamily: 'Exo2',
  798. fontWeight: FontWeight.w600,
  799. fontSize: 16.px),
  800. )
  801. : !isRightTime
  802. ? const SizedBox(
  803. width: 0,
  804. )
  805. : Container(
  806. width: 28.px,
  807. height: 14.px,
  808. alignment: Alignment.center,
  809. margin: EdgeInsets.only(bottom: 4.px),
  810. decoration: BoxDecoration(
  811. borderRadius:
  812. BorderRadius.all(Radius.circular(7.px)),
  813. color: const Color(0xFF050F1A)),
  814. child: Transform.scale(
  815. scale: scaleAni2.value,
  816. child: Row(
  817. mainAxisAlignment: MainAxisAlignment.center,
  818. children: [
  819. Text(
  820. '+1',
  821. style: TextStyle(
  822. color: Colors.white,
  823. fontSize: 8.px,
  824. fontFamily: 'Exo2',
  825. decoration: TextDecoration.none),
  826. ),
  827. Image.asset(
  828. 'assets/images/stone.png',
  829. width: 10.px,
  830. height: 10.px,
  831. )
  832. ],
  833. )),
  834. ),
  835. ),
  836. )
  837. ],
  838. ));
  839. }
  840. }