| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- import 'dart:async';
- import 'package:fast/model/model.dart';
- import 'package:fast/utils/api.dart';
- import 'package:fast/utils/global.dart';
- import 'package:fast/utils/http_utils.dart';
- import 'package:fast/utils/size_fit.dart';
- import 'package:fast/utils/util.dart';
- import 'package:fast/view/component/fast_btn.dart';
- import 'package:fast/view/component/toast.dart';
- import 'package:flutter/material.dart';
- import 'challenge_checkout.dart';
- import 'challenge_result.dart';
- // ignore: must_be_immutable
- GlobalKey<CheckinItemState> checkinKey2 = GlobalKey();
- // ignore: must_be_immutable
- class FastCheckin extends StatefulWidget {
- bool abandon;
- bool supertimeout;
- FastBean fast;
- int timestamp;
- Map<String, dynamic> checkInfo;
- FastCheckin(
- {Key? key,
- required this.abandon,
- required this.supertimeout,
- required this.fast,
- required this.timestamp,
- required this.checkInfo})
- : super(key: key);
- @override
- State<FastCheckin> createState() => _FastCheckinState();
- }
- class _FastCheckinState extends State<FastCheckin>
- with TickerProviderStateMixin {
- late AnimationController controller2, controller3;
- late Animation aniStep2;
- late AnimationController animationController;
- late Animation step0, step1, step2, step3, step4, step5;
- bool rightTime = false;
- FastResultBean? resultBean;
- String desc1 = '', desc2 = '', desc3 = '';
- // Key<CheckinItemState> key =
- // ObjectKey checkinKey = const ObjectKey('item');
- // LocalKey<CheckinItemState> key = LocalKey();
- List checkList = [];
- bool btnEnable = true;
- int earnings = 0;
- int index = 1;
- int step = 0; //动画执行步骤
- late DateTime date;
- @override
- void initState() {
- super.initState();
- date = serverTime();
- controller2 = AnimationController(
- vsync: this, duration: const Duration(milliseconds: 300));
- controller3 = AnimationController(
- vsync: this, duration: const Duration(milliseconds: 1200));
- animationController = AnimationController(
- vsync: this, duration: const Duration(milliseconds: 3000));
- step0 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
- parent: animationController,
- curve: const Interval(0.0, 300.0 / 3000.0)));
- step1 = Tween(begin: 1.0, end: 1.1).animate(CurvedAnimation(
- parent: animationController,
- curve: const Interval(300.0 / 3000.0, 1500.0 / 3000.0)));
- step2 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
- parent: animationController,
- curve: const Interval(1500 / 3000, 1800.0 / 3000.0)));
- step3 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
- parent: animationController,
- curve: const Interval(1800 / 3000, 2100.0 / 3000.0)));
- step4 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
- parent: animationController,
- curve: const Interval(2100.0 / 3000, 2400.0 / 3000.0)));
- step5 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
- parent: animationController,
- curve: const Interval(2400.0 / 3000, 2700.0 / 3000.0)));
- step0.addListener(() {
- if (step0.value == 1.0) {
- setState(() {
- step = 1;
- });
- } else {}
- });
- step1.addListener(() {
- if (step1.value == 1.1) {
- setState(() {
- step = 2;
- });
- }
- });
- animationController.addListener(() {
- setState(() {});
- });
- animationController.forward();
- /*
- 动画步骤
- step 0:
- 状态图片从上到下 alpha 0-1
- step 1:
- 进食时长从下到上 alpha 0-1
- step 2:
- 页面停留 1s
- step 3:
- 状态图片从下到上推一点 alpha 保持不变
- 进食时长从上到下 alpha 1-0
- step 4:
- 打卡从上到下 alpha 0-1
- step 5:
- 描述 从上到下 alpha 0-1
- step 4-5:
- 底部按钮 alpha 0-1
- */
- aniStep2 = Tween(begin: 1.0, end: 0.0).animate(controller2);
- controller2.addListener(() {
- setState(() {});
- });
- controller3.addListener(() {
- setState(() {});
- });
- Timer(const Duration(milliseconds: 2000), () {
- setState(() {
- step = 1;
- controller2.forward();
- });
- });
- getCheckinData();
- if (widget.abandon) {
- giveUp();
- }
- }
- DateTime serverTime() {
- int milliseconds = DateTime.now().millisecondsSinceEpoch;
- milliseconds = milliseconds + Global().timeSeconds * 1000;
- return DateTime.fromMillisecondsSinceEpoch(milliseconds);
- }
- Future giveUp() async {
- Map<String, dynamic> data = await HttpUtils.post(Api.end,
- data: {'real_end_time': serverTime().millisecondsSinceEpoch ~/ 1000});
- resultBean = FastResultBean.fromJson(data);
- }
- void getCheckinData() {
- Map<String, dynamic> data = widget.checkInfo;
- setState(() {
- rightTime = data['checkin_status'] == 'SUCCESS';
- if (data['checkin_status'] == 'SUCCESS') {
- desc1 = '第${data['current_day_index']}天开始断食';
- desc2 = Util().checkFormateDate(
- DateTime.fromMillisecondsSinceEpoch(
- (data['start_time'] as int) * 1000),
- serverTime()) +
- '~' +
- Util().checkFormateDate(
- DateTime.fromMillisecondsSinceEpoch(
- (data['end_time'] as int) * 1000),
- serverTime()) +
- ',计划断食' +
- Util().betweenTime(
- DateTime.fromMillisecondsSinceEpoch(
- (data['start_time'] as int) * 1000),
- DateTime.fromMillisecondsSinceEpoch(
- (data['end_time'] as int) * 1000));
- desc3 = Util().checkFormateDate(
- DateTime.fromMillisecondsSinceEpoch(
- (data['checkin_expire'] as int) * 1000),
- serverTime()) +
- '前完成打卡,视为准时打卡';
- } else {
- desc1 = '第${data['current_day_index']}天开始断食';
- desc2 = Util().checkFormateDate(
- DateTime.fromMillisecondsSinceEpoch(
- (data['start_time'] as int) * 1000),
- serverTime()) +
- '~' +
- Util().checkFormateDate(
- DateTime.fromMillisecondsSinceEpoch(
- (data['end_time'] as int) * 1000),
- serverTime()) +
- ',计划断食' +
- Util().betweenTime(
- DateTime.fromMillisecondsSinceEpoch(
- (data['start_time'] as int) * 1000),
- DateTime.fromMillisecondsSinceEpoch(
- (data['end_time'] as int) * 1000));
- desc3 = Util().checkFormateDate(
- DateTime.fromMillisecondsSinceEpoch(
- (data['checkin_invalid'] as int) * 1000),
- serverTime()) +
- '前完成打卡,视为超时打卡';
- }
- if (widget.supertimeout) {
- desc1 = '第${data['current_day_index']}天结束断食·打卡失效\n挑战终止,欢迎再战';
- desc3 = Util().checkFormateDate(
- DateTime.fromMillisecondsSinceEpoch(
- (data['checkout_invalid'] as int) * 1000),
- serverTime()) +
- '前未完成打卡,打卡已失效';
- }
- });
- List checkinDays = data['checkin_days'];
- List<ChallengeCheckinBean> list = [];
- for (int i = 0; i < data['days']; i++) {
- ChallengeCheckinBean bean = ChallengeCheckinBean();
- bean.day = i + 1;
- if (i < checkinDays.length &&
- checkinDays[i]['checkin_status'] != 'PENDING') {
- bean.stone = checkinDays[i]['rjv_earnings'];
- bean.passed = true;
- }
- if (i == data['current_day_index'] - 1) {
- bean.today = true;
- if (widget.abandon || widget.supertimeout) {
- bean.abandon = true;
- }
- }
- list.add(bean);
- }
- List array = data['days'] == 7
- ? [
- [list[0], list[1], list[2], list[3]],
- [list[4], list[5], list[6]]
- ]
- : [list];
- setState(() {
- checkList = array;
- index = data['current_day_index'];
- earnings = data['rjv_earnings'];
- });
- }
- @override
- void dispose() {
- Global().showCheckin = false;
- controller2.dispose();
- controller3.dispose();
- animationController.dispose();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- SizeFit.initialize(context);
- double screenHeight = MediaQuery.of(context).size.height;
- List<Widget> widgets = [];
- for (int i = 0; i < earnings; i++) {
- widgets.add(Container(
- margin: EdgeInsets.only(left: 2.px),
- child: Image.asset(
- 'assets/images/stone.png',
- width: 12.px,
- height: 12.px,
- ),
- ));
- }
- double value1 = step == 0 ? step0.value : (1.0 - step2.value);
- double valueTop = step == 0
- ? 0.14 * screenHeight + 0.1 * screenHeight * step0.value
- : 0.14 * screenHeight + 0.1 * screenHeight * (1 - step2.value);
- int eatTime = 24 * 3600 - (widget.fast.endTime! - widget.fast.startTime!);
- int eatHour = eatTime ~/ 3600;
- int eatMinute = eatTime % 3600 ~/ 60;
- String str = eatMinute > 0 ? eatMinute.toString() + '分钟' : '';
- String strEatTime = eatHour.toString() + '小时' + str;
- return Container(
- decoration: BoxDecoration(
- image: DecorationImage(
- image: AssetImage('assets/images/${widget.fast.days}days_bg.png'),
- fit: BoxFit.cover),
- ),
- alignment: Alignment.center,
- child: Column(
- children: [
- Opacity(
- opacity: step0.value,
- child: Container(
- margin: EdgeInsets.only(top: valueTop),
- child: Column(
- children: [
- Opacity(
- opacity: 0.4,
- child: Image.asset(
- 'assets/images/challenge_mode.png',
- width: 298.px,
- height: 28.px,
- ),
- ),
- SizedBox(
- height: 12.px,
- ),
- Image.asset(
- 'assets/images/challenge_${widget.fast.days}.png',
- width: 207.px,
- height: 96.px,
- )
- ],
- ),
- ),
- ),
- Stack(
- children: [
- // Opacity(
- // opacity: value1,
- // child: Container(
- // alignment: Alignment.center,
- // child: Column(
- // mainAxisAlignment: MainAxisAlignment.center,
- // children: [
- // SizedBox(
- // height: 8.px + (1 - value1) * 200.px,
- // ),
- // Text(
- // '第${widget.fast.currentDayIndex}天进食结束',
- // textAlign: TextAlign.center,
- // style: TextStyle(
- // color: const Color(0xFFFF9B00),
- // fontSize: 36.px,
- // fontWeight: FontWeight.bold),
- // ),
- // SizedBox(height: 12.px),
- // Text('进食时长$strEatTime',
- // textAlign: TextAlign.center,
- // style:
- // TextStyle(color: Colors.white, fontSize: 16.px)),
- // ],
- // ),
- // ),
- // ),
- Opacity(
- opacity: step4.value,
- child: Container(
- alignment: Alignment.center,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- SizedBox(
- height: checkList.length == 1
- ? 95.px
- : 120.px + 20.px * step4.value,
- ),
- Text(
- desc1,
- // '第 ${widget.fast.currentDayIndex} 天开始断食打卡签到准时',
- textAlign: TextAlign.center,
- style: TextStyle(
- color: (widget.abandon || widget.supertimeout)
- ? const Color(0xFFFF0000)
- : const Color(0xFFC4CCDA),
- fontSize: (widget.abandon || widget.supertimeout)
- ? 12.px
- : 13.px),
- ),
- SizedBox(
- height: 5.px,
- ),
- Text(
- (widget.abandon || widget.supertimeout) ? '' : desc2,
- // '每晚餐后开始断食打卡,次日早餐前\n结束断食打卡,你已连续坚持 2 天',
- textAlign: TextAlign.center,
- style: TextStyle(
- color: const Color(0x99C4CCDA), fontSize: 12.px),
- )
- ],
- ),
- ),
- ),
- Opacity(
- opacity: step3.value,
- child: Container(
- alignment: Alignment.center,
- margin: EdgeInsets.only(top: 12.px * step3.value),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: List<Widget>.generate(
- checkList.length,
- (index) {
- return Container(
- height: 50.px,
- width: checkList[index].length * 50.px,
- margin: EdgeInsets.only(bottom: 8.px),
- decoration: BoxDecoration(
- color:
- const Color.fromARGB(38, 196, 204, 218),
- borderRadius:
- BorderRadius.all(Radius.circular(25.px))),
- child: Row(
- children: List<Widget>.generate(
- checkList[index].length, (j) {
- return CheckinItem(
- key: checkList[index][j].today
- ? checkinKey2
- : null,
- bean: checkList[index][j],
- isCheckin: true,
- );
- }),
- ),
- );
- },
- ),
- ))),
- ],
- ),
- Expanded(
- child: SizedBox(
- height: 0.px,
- )),
- Opacity(
- opacity: step4.value,
- child: Column(
- children: [
- if (widget.supertimeout || widget.abandon)
- FastBtn(
- title: '确定',
- disable: btnEnable ? false : true,
- width: 228.px,
- height: 48.px,
- callback: () {
- if (Global().allowNotification == false &&
- Global().pushEnable) {
- Util().showNotificationStatus(context);
- return;
- }
- if (widget.abandon) {
- endConfirm();
- } else if (widget.supertimeout) {
- superTimeoutEnd();
- } else {
- checkin();
- // checkinKey.currentState!.checkin();
- setState(() {
- btnEnable = false;
- });
- }
- }),
- if (!widget.supertimeout && !widget.abandon)
- FastBtn(
- title: "",
- img: Image.asset(
- 'assets/images/checkin.png',
- width: 132.px,
- height: 26.px,
- ),
- disable: !btnEnable,
- width: 228.px,
- height: 48.px,
- callback: () {
- if (Global().allowNotification == false &&
- Global().pushEnable) {
- Util().showNotificationStatus(context);
- return;
- }
- if (widget.abandon) {
- endConfirm();
- } else if (widget.supertimeout) {
- superTimeoutEnd();
- } else {
- checkin();
- // checkinKey.currentState!.checkin();
- setState(() {
- btnEnable = false;
- });
- }
- }),
- SizedBox(
- height: 17.px,
- ),
- Text(
- desc3, //'「结束断食」打卡后,即可获得逆龄石',
- style: TextStyle(
- color: const Color(0x99C4CCDA), fontSize: 12.px),
- ),
- SizedBox(
- height: 0.07 * screenHeight * step4.value,
- )
- ],
- ),
- )
- ],
- ),
- );
- }
- Future superTimeoutEnd() async {
- Map<String, dynamic> data = await HttpUtils.post(Api.checkin,
- data: {'day_index': index, 'checkin_time': widget.timestamp});
- resultBean = FastResultBean.fromJson(data);
- showResultPage();
- }
- Future endConfirm() async {
- await HttpUtils.post(Api.endConfirm, data: {});
- showResultPage();
- }
- showResultPage() {
- Navigator.of(context).pop();
- Global().homePage.closeCheckPop();
- showDialog(
- context: context,
- barrierDismissible: false,
- barrierColor: Colors.transparent,
- useSafeArea: false,
- builder: (BuildContext context) {
- return ChallengeResult(resultBean: resultBean!);
- });
- }
- Future checkin() async {
- Map<String, dynamic> data = await HttpUtils.post(Api.checkin,
- data: {'day_index': index, 'checkin_time': widget.timestamp});
- resultBean = FastResultBean.fromJson(data);
- checkinKey2.currentState!.checkin(false);
- if (resultBean!.over!) {
- Timer(const Duration(milliseconds: 2500), () {
- showResultPage();
- });
- } else {
- Timer(const Duration(milliseconds: 2500), () {
- Navigator.of(context).pop();
- showDialog(
- context: context,
- barrierDismissible: false,
- barrierColor: Colors.transparent,
- builder: (BuildContext context) {
- return Toast(
- title: rightTime ? '准时打卡' : '超时打卡',
- content: const SizedBox(
- width: 0,
- ),
- );
- });
-
- Global().homePage.closeCheckPop();
- if (widget.supertimeout) {
- showResultPage();
- return;
- } else {
- //正常打卡
- }
- });
-
- Global().homePage.checkUpdate();
- }
- }
- }
|