import 'dart:async'; import 'package:app_settings/app_settings.dart'; import 'package:fast/constants.dart'; import 'package:fast/extension/layout.dart'; import 'package:fast/utils/global.dart'; import 'package:fast/utils/util.dart'; import 'package:fast/view/component/fast_btn.dart'; import 'package:fast/view/component/navi_bar.dart'; import 'package:fast/view/component/share.dart'; import 'package:flutter/material.dart'; import 'package:fast/utils/size_fit.dart'; import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:get/get.dart'; import '../recharge.dart'; import 'alert_widget.dart'; // ignore: must_be_immutable class ChooseMode extends StatefulWidget { // ignore: prefer_typing_uninitialized_variables var callback; int seconds; ChooseMode({Key? key, required this.callback, required this.seconds}) : super(key: key); @override State createState() => _ChooseModeState(); } class _ChooseModeState extends State with SingleTickerProviderStateMixin { late AnimationController controller; late Animation scale; int selType = 0; bool enoughStone = true; @override void initState() { super.initState(); controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 200)); controller.addStatusListener((status) {}); scale = Tween(begin: 1.0, end: 1.2).animate(controller); } @override void dispose() { controller.dispose(); super.dispose(); } modeText(content, textAlign) { return Text( content, style: TextStyle( color: const Color(0x99C4CCDA), fontSize: 10.px, height: 1.6, fontWeight: FontWeight.normal), textAlign: textAlign, ); } @override Widget build(BuildContext context) { EdgeInsets safePadding = MediaQuery.of(context).padding; var size = MediaQuery.of(context).size; var stones = [1, 3, 4, 5]; var wins = [1, 3, 5, 7]; if (Global().balance < stones[selType]) { enoughStone = false; } else { enoughStone = true; } return Flex( direction: Axis.vertical, children: [ Expanded( child: Stack( children: [ SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( children: [ SizedBox( height: safePadding.top + 40.px, ), Image.asset( 'assets/images/single_mode.png', width: 298.px, height: 48.px, ), modeText( '记录一次断食,适合刚接触间歇性断食的小伙伴\n· 选择适合自己的断食时长,新手可选择12h断食开始\n· 适应后延至16h或更长。单次记录将消耗1颗逆龄石', TextAlign.left), SizedBox(height: 12.px), Stack( children: [ GestureDetector( onTap: () => setState(() { selType = 0; }), child: Container( width: 296.px, height: 64.px, child: Opacity( opacity: selType == 0 ? 1 : 0.8, child: Image.asset('assets/images/single_sel.png'), ), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(20.px)), border: Border.all( color: selType == 0 ? kThemeColor : Colors.transparent, width: 1.px)), ), ), if (selType == 0) Image.asset( 'assets/images/mode_checked1.png', width: 39.px, height: 39.px, ) ], ), // Container( // // width: 108.px,height: 152.px, // child: Image.asset('assets/images/3days_sel.png',width: 108.px,height: 152.px,), // decoration: BoxDecoration( // borderRadius: BorderRadius.all(Radius.circular(19.px)), // border: Border.all(color:Colors.red,width: 1.px) // ), // ), Image.asset( 'assets/images/challenge_mode.png', width: 298.px, height: 48.px, ), modeText( '间歇性断食挑战,激发身体内在活力,赢取傲人健康资本!\n· 期初投资3/4/5颗逆龄石,每天两次准时打卡赚取1颗\n· 挑战成功获得3/5/7颗逆龄石,分享好友还能领取更多', TextAlign.left), Callenage( selType: selType, callback: (type) { setState(() { selType = type; }); }, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '挑战规则', style: TextStyle( color: const Color(0xFFC4CCDA), fontSize: 12.px, fontWeight: FontWeight.bold), textAlign: TextAlign.left, ), SizedBox( height: 8.px, ), Container( margin: EdgeInsets.only(right: 24.px), child: modeText( '预先设定每天断食时长(≥12h)并选择挑战天数。挑战期间,每晚餐后开始断食打卡、次日早餐前结束断食打卡\n\n【16/8示例】今天晚餐后🕕18:00开始断食,计划明天早餐前🕙10:00结束断食\n\n以「结束断食 打卡」为例,明天\n', TextAlign.left)), Row( children: [ Image.asset( 'assets/images/green.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('10:00~10:30打卡,视为准时', TextAlign.left) ], ), Row( children: [ Image.asset( 'assets/images/yellow.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('10:30~18:00打卡,视为超时', TextAlign.left) ], ), Row( children: [ Image.asset( 'assets/images/red.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('18:00后为严重超时,打卡失效', TextAlign.left) ], ), modeText('同理适用于「开始断食 打卡」\n\n当天,如果「开始断食 打卡」和「结束断食 打卡」', TextAlign.left), Row( children: [ Image.asset( 'assets/images/green.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('两次打卡均准时,当天视为打卡成功:', TextAlign.left) ], ), Row( children: [ SizedBox( width: 18.px, ), modeText('获得+1颗逆龄石,挑战继续', TextAlign.left), Image.asset( 'assets/images/play.png', width: 14.px, height: 14.px, ), ], ), Row( children: [ Image.asset( 'assets/images/yellow.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('任何一次打卡超时,当天视为完成打卡:', TextAlign.left) ], ), Row( children: [ SizedBox( width: 18.px, ), modeText('无法获得逆龄石,挑战继续', TextAlign.left), Image.asset( 'assets/images/play.png', width: 14.px, height: 14.px, ), ], ), Row( children: [ Image.asset( 'assets/images/red.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('任何一次打卡失效,当天视为打卡失败:', TextAlign.left) ], ), Row( children: [ SizedBox( width: 18.px, ), modeText('无法获得逆龄石,挑战终止', TextAlign.left), Image.asset( 'assets/images/stop.png', width: 14.px, height: 14.px, ), ], ), modeText('\n如果挑战期间每天', TextAlign.left), Row( children: [ Image.asset( 'assets/images/play.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('连续打卡成功:视为挑战成功', TextAlign.left), ], ), Row( children: [ Image.asset( 'assets/images/stop.png', width: 14.px, height: 14.px, ), SizedBox( width: 4.px, ), modeText('未连续打卡成功,但也未曾打卡失败:视为完成挑战', TextAlign.left), ], ), /* Container( margin: EdgeInsets.only(right: 12.px), child: modeText( '\n\n\n如果挑战期间每天\n➡️ 连续打卡成功:视为挑战成功\n➡️ 未连续打卡成功,但也未曾打卡失败:\n视为完成挑战', TextAlign.left),)*/ ], ).width(375.px).paddingLeft(40.px).marginTop(20.px), SizedBox( height: 200.px, width: 0.px, ) ], ), ), NaviBar( title: '选择模式', closeCallback: () => Navigator.of(context).pop(), showInfo: true, ), ChooseFooter( enoughStone: enoughStone, stone: stones[selType], isLock: wins[selType] > Global().userBean!.dayLevel, type: selType, callback: () { if (selType != 0 && widget.seconds < 12 * 3600) { showDialog( context: context, barrierDismissible: false, barrierColor: const Color(0xF2000D1F), builder: (BuildContext context) { return AlertWidget( title: "为获得更加显著的健康效益\n挑战者模式下的断食时长\n应大于或等于12小时", confirm: '返回重新调整', hideCancel: true, confirmCallback: () { Navigator.of(context).pop(); Navigator.of(context).pop(); }); }); return; } if (Global().allowNotification == false && Global().pushEnable) { Util().showNotificationStatus(context); return; } Global().mainPage.getTokenId(); widget.callback(selType); Navigator.of(context).pop(); }, ) ], ), ), SizedBox( height: safePadding.bottom, ) ], ); } } class GetController extends GetxController { var selType = -1.obs; setType(type) { selType = type; } } // ignore: must_be_immutable class Callenage extends StatefulWidget { int selType; // ignore: prefer_typing_uninitialized_variables var callback; Callenage({Key? key, required this.selType, required this.callback}) : super(key: key); @override State createState() => _CallenageState(); } class _CallenageState extends State { // int selIndex = 0; void itemSel(index) { // setState(() { // selIndex = index; // }); widget.selType = index; widget.callback(index); } @override Widget build(BuildContext context) { /* var widget1 = Positioned( left: 18.px, top: 0.px, width: 108.px, height: 152.px, child: CallenageItem( selIndex: selIndex, currentIndex: 1, callback: (index) => itemSel(index), )); var widget2 = Positioned( left: 135.px, top: 0.px, width: 108.px, height: 152.px, child: CallenageItem( selIndex: selIndex, currentIndex: 2, callback: (index) => itemSel(index), )); var widget3 = Positioned( left: 251.px, top: 0.px, width: 108.px, height: 152.px, child: CallenageItem( selIndex: selIndex, currentIndex: 3, callback: (index) => itemSel(index), ), ); // ignore: deprecated_member_use List list; if (selIndex==1){ list = [widget2,widget3,widget1]; } else if (selIndex==2){ list = [widget1,widget3,widget2]; } else { list = [widget1,widget2,widget3]; } return SizedBox( height: 152.px, width: 375.px, child: Stack( children: list, ), ).marginTop(20.px);*/ return Row( children: [ SizedBox( width: 18.px, ), CallenageItem( selIndex: widget.selType, currentIndex: 1, callback: (index) => itemSel(index), ), SizedBox( width: 8.px, ), CallenageItem( selIndex: widget.selType, currentIndex: 2, callback: (index) => itemSel(index), ), SizedBox( width: 8.px, ), CallenageItem( selIndex: widget.selType, currentIndex: 3, callback: (index) => itemSel(index), ), ], ).marginTop(20.px); } } // ignore: must_be_immutable class CallenageItem extends StatefulWidget { int selIndex, currentIndex; // ignore: prefer_typing_uninitialized_variables var callback; CallenageItem( {Key? key, required this.selIndex, required this.currentIndex, required this.callback}) : super(key: key); @override State createState() => _CallenageItemState(); } class _CallenageItemState extends State with SingleTickerProviderStateMixin { late AnimationController controller; late Animation scale; bool expand = false; @override void initState() { super.initState(); controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 200)); controller.addStatusListener((status) {}); scale = Tween(begin: 1.0, end: 1.15).animate(controller); if (widget.selIndex == widget.currentIndex) { expand = true; controller.forward(); } } @override void dispose() { controller.dispose(); super.dispose(); } void tapItem() { setState(() { expand = true; }); widget.callback(widget.currentIndex); Timer(const Duration(milliseconds: 0), () { controller.forward(); }); } @override Widget build(BuildContext context) { if (expand == true && widget.selIndex != widget.currentIndex) { setState(() { expand = false; }); Timer(const Duration(milliseconds: 0), () { controller.reverse(); }); } var stones = [1, 3, 4, 5]; var wins = [1, 3, 5, 7]; var path = [ '', 'assets/images/3days_sel.png', 'assets/images/5days_sel.png', 'assets/images/7days_sel.png' ]; return GestureDetector( onTap: () { tapItem(); }, child: ScaleTransition( //SizeTransition scale: scale, child: Opacity( opacity: (expand || widget.selIndex == 0) ? 1.0 : 0.6, child: Container( // width: 108.px, // height: 152.px, child: ItemContent( imgBgPath: path[widget.currentIndex], isSelected: expand, day: wins[widget.currentIndex], stone: stones[widget.currentIndex], win: wins[widget.currentIndex]), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(20.px)), border: Border.all( color: expand ? kThemeColor : Colors.transparent, width: 1.px), ), )), ), ); } } // ignore: must_be_immutable class ItemContent extends StatelessWidget { String imgBgPath; int stone, win, day; bool isSelected; ItemContent( {Key? key, required this.imgBgPath, required this.isSelected, required this.stone, required this.day, required this.win}) : super(key: key); Widget desc() { return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SizedBox( width: 13.px, ), Text( '赚取', style: TextStyle( color: kBgColor, fontSize: 9.px, fontWeight: FontWeight.bold), ), Text( win.toString(), style: TextStyle( color: kBgColor, fontSize: 9.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), Image.asset( 'assets/images/stone.png', width: 9.px, height: 9.px, ), if (win == 3) Text( ',相当于免费', style: TextStyle( color: kBgColor, fontSize: 9.px, fontWeight: FontWeight.bold), ), if (win == 5 || win == 7) Text( ',净赚', style: TextStyle( color: kBgColor, fontSize: 9.px, fontWeight: FontWeight.bold), ), if (win == 5 || win == 7) Text( win == 5 ? '1' : '2', style: TextStyle( color: kBgColor, fontSize: 9.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), if (win == 5 || win == 7) Image.asset( 'assets/images/stone.png', width: 9.px, height: 9.px, ), ], ); } @override Widget build(BuildContext context) { int level = Global().userBean!.dayLevel; print(level.toString()); return Stack( children: [ Container( width: 108.px, height: 152.px, decoration: BoxDecoration( color: kThemeColor, borderRadius: BorderRadius.all(Radius.circular(20.px)), image: DecorationImage( image: AssetImage(imgBgPath), fit: BoxFit.cover)), ), // Image.asset( // imgBgPath, // width: 108.px, // height: 152.px, // ), if (!isSelected) Positioned( left: 0, right: 0, bottom: 0, child: Column( children: [ Container( width: 40.px, height: 1.px, color: const Color(0x66FFFFFF), ), Container( height: 47.px, alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '+' + win.toString(), style: TextStyle( color: Colors.white, fontWeight: FontWeight.w600, fontFamily: 'Exo2', fontSize: 16.px), ).marginBottom(4.px), Image.asset( 'assets/images/stone.png', width: 14.px, height: 14.px, ).marginLeft(3.px), Image.asset( 'assets/images/max.png', width: 30.px, height: 8.px, ).marginLeft(3.px) ]), ) ], )), if (isSelected) Positioned( left: 0, right: 0, bottom: 0, child: Container( height: 42.px, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ SizedBox( width: 13.px, ), Text( '投入', style: TextStyle( color: kBgColor, fontSize: 9.px, fontWeight: FontWeight.bold), ), Text( stone.toString(), style: TextStyle( color: kBgColor, fontSize: 9.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), Image.asset( 'assets/images/stone.png', width: 9.px, height: 9.px, ), Text( ';挑战成功', style: TextStyle( color: kBgColor, fontSize: 9.px, fontWeight: FontWeight.bold), ), ], ), desc() ], ), decoration: BoxDecoration( borderRadius: BorderRadius.only( bottomLeft: Radius.circular(18.px), bottomRight: Radius.circular(18.px)), gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [kThemeColor, Color(0x99AAFF00)])), ), ), if (isSelected) Container( width: 39.px, height: 39.px, decoration: BoxDecoration( borderRadius: BorderRadius.only(topLeft: Radius.circular(18.px)), image: DecorationImage( image: day > Global().userBean!.dayLevel ? const AssetImage('assets/images/mode_lock.png') : const AssetImage('assets/images/mode_checked.png'))), ), ], ); } } // ignore: must_be_immutable class ChooseFooter extends StatelessWidget { bool enoughStone = true; bool isLock; int stone; int type; // ignore: prefer_typing_uninitialized_variables var callback; ChooseFooter( {Key? key, required this.enoughStone, required this.isLock, required this.stone, required this.type, required this.callback}) : super(key: key); @override Widget build(BuildContext context) { List locks = [0, 1, 3, 5]; return Positioned( child: Container( height: 160.px, padding: EdgeInsets.only(top: 38.px), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Color(0x33000D1F), kBgColor, kBgColor, kBgColor, kBgColor ])), child: Column( children: [ if (isLock) GestureDetector( onTap: () => {Vibrate.feedback(FeedbackType.success)}, child: Opacity( opacity: 0.4, child: Container( width: 248.px, height: 48.px, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(24.px)), color: kThemeColor), child: Image.network( '${baseUrl}must_done_${locks[type]}.png', width: 209.px, height: 24.px, ), ), ), ).paddingBottom(17.px), if (!isLock && enoughStone) FastBtn( title: '立即开始', disable: !enoughStone, width: 248.px, height: 48.px, callback: callback, ).paddingBottom(17.px), if (!enoughStone && !isLock) GestureDetector( onTap: () { Get.to(() => const Recharge()); }, child: Container( width: 248.px, height: 48.px, alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(24.px)), border: Border.all(color: kThemeColor, width: 1.px), color: const Color(0x33C4CCDA), ), child: Text( '立即充值', style: TextStyle( color: kThemeColor, fontSize: 16.px, fontWeight: FontWeight.bold), ), ).paddingBottom(17.px), ), if (enoughStone) Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( type == 0 ? '开始将消耗 $stone' : '开始将投入 $stone', style: TextStyle( color: const Color(0x99FFFFFF), fontSize: 12.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), Image.asset( 'assets/images/stone.png', width: 14.px, height: 14.px, ) ], ) else Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '余额 ${Global().balance}', style: TextStyle( color: const Color(0x99FFFFFF), fontSize: 12.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), Image.asset( 'assets/images/stone.png', width: 14.px, height: 14.px, ), Text( ',开始需要 $stone', style: TextStyle( color: const Color(0x99FFFFFF), fontSize: 12.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), Image.asset( 'assets/images/stone.png', width: 14.px, height: 14.px, ) ], ), if (!enoughStone) Row( mainAxisAlignment: MainAxisAlignment.center, children: [ GestureDetector( onTap: () { showShare(context); }, child: Text('邀请好友', style: TextStyle( color: kThemeColor, fontSize: 12.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600)), ), Text( ',好友加入时我和ta各得 +3', style: TextStyle( color: const Color(0x99FFFFFF), fontSize: 12.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), Image.asset( 'assets/images/stone.png', width: 14.px, height: 14.px, ) ], ).marginTop(5.px) ], ), ), left: 0, right: 0, bottom: 0, ); } showShare(BuildContext context) { showDialog( context: context, barrierDismissible: false, barrierColor: const Color(0xF2000D1F), builder: (BuildContext context) { return Share( type: 'me', ); }); } }