import 'dart:async'; import 'package:fast/extension/button.dart'; import 'package:fast/extension/relation.dart'; import 'package:fast/model/model.dart'; import 'package:fast/utils/global.dart'; import 'package:fast/utils/size_fit.dart'; import 'package:fast/utils/util.dart'; import 'package:flutter/material.dart'; import 'alert_widget.dart'; import 'bubble_widget.dart'; import 'challenge_checkout.dart'; class Eat extends StatefulWidget { const Eat({Key? key}) : super(key: key); @override _EatState createState() => _EatState(); } class _EatState extends State { final GlobalKey globalKey = GlobalKey(); FastBean currentFast = Global().currentFast!; late Timer timer; double x = 38.px; int s = 6; @override void initState() { super.initState(); WidgetsBinding.instance?.addPostFrameCallback((_) { setState(() { var obj = globalKey.currentContext; var width = obj?.size!.width; x = (width! / 2 + 17.px) * 2; }); }); timer = Timer.periodic(const Duration(seconds: 1), (e) { setState(() { s--; if (s == 0) { timer.cancel(); } }); }); } @override void didChangeDependencies() { super.didChangeDependencies(); } @override void dispose() { timer.cancel(); super.dispose(); } @override Widget build(BuildContext context) { int fastTime = (currentFast.endTime! - currentFast.startTime!) ~/ 60; int fastHour = fastTime ~/ 60; int fastMinute = fastTime % 60.toInt(); int eatMinute = fastMinute == 0 ? 0 : 60 - fastMinute; int eatHour = fastMinute == 0 ? 24 - fastHour : 23 - fastHour; String strFast = ''; String strEat = ''; if (fastMinute == 0) { strFast = fastHour.toString(); strEat = eatHour.toString(); } else { strFast = fastHour.toString() + ':' + fastMinute.toString().padLeft(2, '0'); strEat = eatHour.toString() + ':' + eatMinute.toString().padLeft(2, '0'); } Size sz = Util().boundingTextSize( strEat, TextStyle(fontFamily: 'Exo2', fontSize: 32.px)); return Stack(children: [ Positioned(top: 10.px, right: 0.px, child: const Progress()), Column(mainAxisAlignment: MainAxisAlignment.start, children: [ SizedBox( height: 30.px, ), const Bubble(), SizedBox( height: 30.px, ), if (s > 0) Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: Text( strFast, textAlign: TextAlign.right, style: TextStyle( color: const Color(0x99FFFFFF), fontFamily: 'Exo2', fontSize: 32.px), ), ), SizedBox( width: 12.px, ), Text('/', style: TextStyle( color: const Color(0x99FFFFFF), fontFamily: 'Exo2', fontSize: 16.px)), SizedBox( width: 12.px, ), Expanded( child: Row( children: [ if (s > 2) Text( strEat, key: globalKey, style: TextStyle( color: const Color(0xFFFF9B00), fontFamily: 'Exo2', fontWeight: FontWeight.w600, fontSize: 32.px), ).relationOne( -49.px + sz.width / 2.0, -40, BubbleWidget( 94.px, 42.px, const Color(0xFFFF9B00), BubbleArrowDirection.bottom, arrAngle: 80.px, arrHeight: 8.px, child: Text( '进食开始…', style: TextStyle( color: Colors.black, fontSize: 14.px, fontWeight: FontWeight.bold), ), )), if (s <= 2) Container( margin: EdgeInsets.only(bottom: 4.px), child: Text( strEat, key: globalKey, style: TextStyle( color: const Color(0xFFFF9B00), fontFamily: 'Exo2', fontWeight: FontWeight.w600, fontSize: 32.px), ),) ], )) ]), if (s <= 0) Container( alignment: Alignment.center, child: RRectButton( '结束挑战', width: 144.px, height: 50.px, backgroundColor: const Color(0x1AC4CCDA), textColor: const Color(0xFFC4CCDA), radius: 25.px, fontSize: 16.px, onPressed: () => end(), ), ) ]) ]); } end() { if (Global().allowNotification == false && Global().pushEnable) { Util().showNotificationStatus(context); return; } showConfirm('确定现在退出\n"${currentFast.days}天连续计划"吗?', () { Global().homePage.showCheckout(true); }); } void showConfirm(String title, Function() confirmCallback) { showDialog( context: context, barrierDismissible: false, barrierColor: const Color(0xF2000D1F), builder: (BuildContext context) { return AlertWidget( title: title, confirm: '确定', confirmCallback: () { Navigator.of(context).pop(); confirmCallback(); }); }); } } class Progress extends StatefulWidget { const Progress({Key? key}) : super(key: key); @override _ProgressState createState() => _ProgressState(); } class _ProgressState extends State { late FastBean fastBean; @override void initState() { fastBean = Global().currentFast!; super.initState(); } @override Widget build(BuildContext context) { return Container( width: 64.px, height: 54.px, alignment: Alignment.center, margin: EdgeInsets.fromLTRB(0, 0, 14.px, 0.px), decoration: BoxDecoration( color: const Color(0x1AFFFFFF), borderRadius: BorderRadius.all(Radius.circular(12.px))), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ SizedBox(height: 5.px,), Container( padding: EdgeInsets.fromLTRB(0, 0, 0, 6.px), decoration: BoxDecoration( border: Border( bottom: BorderSide( color: const Color(0x1AFFFFFF), width: 1.px))), child: Text( '${fastBean.days}天挑战', style: TextStyle(color: const Color(0x66FFFFFF), fontSize: 10.px), ), ), SizedBox( height: 2.px, ), Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( '${fastBean.finishDays}', style: TextStyle( color: const Color(0xFFC4CCDA), fontSize: 16.px, fontWeight: FontWeight.bold, fontFamily: 'Exo2'), ), const SizedBox( width: 3, ), Text( '/', style: TextStyle(color: const Color(0xFFC4CCDA), fontSize: 9.px), ), const SizedBox( width: 3, ), Text( '${fastBean.days}', style: TextStyle( color: const Color(0xFFC4CCDA), fontSize: 16.px, fontWeight: FontWeight.bold, fontFamily: 'Exo2'), ) ], ) ], )); } } class Bubble extends StatelessWidget { const Bubble({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Stack(children: [ BubbleRotation( key: UniqueKey(), duration: 3000, alignment: Alignment.topLeft, padding: EdgeInsets.zero, ), BubbleRotation( key: UniqueKey(), duration: 2500, alignment: Alignment.topRight, padding: EdgeInsets.all(6.px), ), const BubbleContent(), ]); } } class BubbleContent extends StatefulWidget { const BubbleContent({Key? key}) : super(key: key); @override State createState() => _BubbleContentState(); } class _BubbleContentState extends State { late FastBean fastBean; late Timer timer; late String hourMinute; late String second; int type = 0; @override void initState() { calulate(); timer = Timer.periodic(const Duration(seconds: 1), (t) { calulate(); }); super.initState(); } @override void dispose() { timer.cancel(); super.dispose(); } void calulate() { fastBean = Global().currentFast!; int seconds = fastBean.startTime! - serverTime().millisecondsSinceEpoch ~/ 1000; int hours = seconds ~/ 3600; int minutes = seconds % 3600 ~/ 60; int sec = (seconds % 60).floor(); // seconds = 8; setState(() { if (seconds >= 60) { type = 0; hourMinute = hours.toString().padLeft(2, '0') + ':' + minutes.toString().padLeft(2, '0'); second = sec.toString().padLeft(2, '0'); } else if (seconds >= 10) { type = 1; second = sec.toString(); } else { type = 2; second = sec.toString(); if (sec==0){ Global().homePage.eatEnd(); } } }); } DateTime serverTime() { int milliseconds = DateTime.now().millisecondsSinceEpoch; milliseconds = milliseconds + Global().timeSeconds * 1000; return DateTime.fromMillisecondsSinceEpoch(milliseconds); } @override Widget build(BuildContext context) { return Container( width: 300.px, height: 300.px, alignment: Alignment.center, child: Container( width: 260.px, height: 260.px, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(260.px)), color: const Color(0xffFF9B00)), child: Column( children: [ if (type != 2) Container( margin: EdgeInsets.only(top: type == 0 ? 57.px : 76.px), child: Text( '距下次断食开始还剩', style: TextStyle( color: Colors.white, fontSize: 12.px, height: 1.0), ), ), if (type == 0) Container( margin: EdgeInsets.only(top: 6.px), foregroundDecoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0x80FF9B00), Color(0x00FF9B00)])), child: Text( hourMinute, style: TextStyle( color: Colors.white, fontFamily: 'Fast', height: 1.0, fontSize: 40.px), ), ), Container( margin: EdgeInsets.only(top: type==0?0.0:type==2?80.px:10.px), child: Text( second, style: TextStyle( color: Colors.white, fontFamily: 'Fast', height: 1.0, fontSize: type == 2 ? 120.px : 80.px), ),) ], ), )); } } // ignore: must_be_immutable class BubbleRotation extends StatefulWidget { int duration; AlignmentGeometry alignment; EdgeInsets padding; BubbleRotation( {Key? key, required this.duration, required this.alignment, required this.padding}) : super(key: key); @override _BubbleRotationState createState() => _BubbleRotationState(); } class _BubbleRotationState extends State with SingleTickerProviderStateMixin { AnimationController? controller; @override void initState() { super.initState(); controller = AnimationController( vsync: this, duration: Duration(milliseconds: widget.duration)); controller!.addStatusListener((status) { if (status == AnimationStatus.completed) { controller!.reset(); controller!.forward(); } else if (status == AnimationStatus.dismissed) {} }); controller!.forward(); } @override void dispose() { controller!.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return RotationTransition( turns: controller!, alignment: Alignment.center, child: Container( width: 300.px, height: 300.px, alignment: widget.alignment, padding: widget.padding, child: Container( width: 260.px, height: 260.px, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(260.px)), color: const Color(0x75FF9B00))), ), ); } }