| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263 |
- 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/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/storage.dart';
- import 'package:fast/utils/util.dart';
- import 'package:fast/view/component/alert_widget.dart';
- import 'package:fast/view/component/challenge_checkout.dart';
- import 'package:fast/view/component/choose_mode.dart';
- import 'package:fast/view/component/frame_image.dart';
- import 'package:fast/view/component/guide.dart';
- import 'package:fast/view/component/record.dart';
- import 'package:fast/view/component/toast.dart';
- import 'package:flutter/material.dart';
- import 'dart:math';
- import 'dart:ui' as ui;
- import 'package:flutter/services.dart';
- import 'package:flutter_vibrate/flutter_vibrate.dart';
- import '../../constants.dart';
- import 'bubble_widget.dart';
- import 'progress_painter.dart';
- import 'target_painter.dart';
- class Fast extends StatefulWidget {
- const Fast({Key? key}) : super(key: key);
- @override
- FastState createState() => FastState();
- }
- class FastState extends State<Fast> with SingleTickerProviderStateMixin {
- double arcAngle = 0; //圆弧终点
- double beginAngle = 0.0; //圆弧起点
- double dy = 0; //起点y坐标
- double dx = 0; //起点x坐标
- late ui.Image startImage; //起点图标
- late ui.Image progressImage; //起点图标
- bool isLoad = false; //起点图片是否加载完毕,加载完毕后再用canvas画出
- double endLeft = 0; //终点x坐标
- double endTop = 0; //终点y坐标
- int status = 0; //0准备中 1单次模式 2挑战模式 3进食模式
- int targetHour = Global().fastHour; //目标小时
- int targetMinute = Global().fastMinute; //目标分钟
- int leftHour = 0; //进食小时
- int leftMinute = 0; //进食分钟
- String beginHour = ''; //开始时 点击开始按钮前,每分钟刷新
- String beginMinutes = ''; //开始分
- String endHour = ''; //结束时 开始时+目标时
- String endMinutes = ''; //结束分
- String currentHour = ''; //点击开始后,断食时长
- String currentMinute = '';
- DateTime beginTime = DateTime.now();
- String beginCount = '00:00';
- String beginSecond = '00';
- String beginFormateTime = "00:00:00";
- double rotate = 0; //当前位置箭头旋转的角度
- double currentAngel = 0; //当前位置的弧度
- bool showBeginCount = true; //是否显示正计时
- bool showMinuteCountdown = false; //1分钟倒计时
- bool showSecondCountdown = false; //10秒倒计时
- String strMinuteCountdown = '00:59'; //倒计时60秒
- String strSecondCountdown = '9'; //倒计时10秒
- bool hiddenTime = false; //开始断食后4秒钟,再隐藏时间
- bool showTips = false; //显示断食中的气泡
- int recordBegin = 0;
- int recordEnd = 0;
- int recordTarget = 0;
- int enterTimes = 0; //进入页面倒计时,如果进食中,显示tips 这个时间判断是否显示进行中气泡以及是否显示结束任务
- FastBean? currentFast;
- double circleWidth = Global().circleWidth;
- double paintWidth = Global().paintWidth;
- double progressWidth = 0.0;
- bool hasShowSingleConfirm = false;
- late AnimationController controller;
- late Animation animation0;
- late Animation animation1;
- late Animation animation2;
- late Animation animation3;
- late Animation animation4;
- Timer? _timer;
- @override
- void dispose() {
- controller.dispose();
- _timer!.cancel();
- super.dispose();
- }
- @override
- void initState() {
- super.initState();
- if (targetMinute > 0) {
- leftMinute = 60 - targetMinute;
- leftHour = 24 - targetHour - 1;
- } else {
- leftHour = 24 - targetHour;
- }
- setBeginTime(serverTime());
- _getLocalImage();
- const timeout = Duration(seconds: 1);
- if (_timer != null) {
- _timer!.cancel();
- }
- _timer = Timer.periodic(timeout, (timer) {
- if (status == 0) {
- setBeginTime(serverTime());
- } else {
- setBeginTime(beginTime);
- calculate();
- }
- });
- controller = AnimationController(
- vsync: this, duration: const Duration(milliseconds: 2200));
- controller.addListener(() {
- setState(() {});
- });
- animation0 = Tween(begin: 0.0, end: 1.0).animate(
- CurvedAnimation(parent: controller, curve: const Interval(0.3, 0.45)));
- animation1 = Tween(begin: 0.0, end: 1.0).animate(
- CurvedAnimation(parent: controller, curve: const Interval(0.45, 0.6)));
- animation2 = Tween(begin: 0.0, end: 1.0).animate(
- CurvedAnimation(parent: controller, curve: const Interval(0.6, 0.8)));
- animation3 = Tween(begin: 0.0, end: 1.0).animate(
- CurvedAnimation(parent: controller, curve: const Interval(0.8, 0.9)));
- animation4 = Tween(begin: 0.0, end: 1.0).animate(
- CurvedAnimation(parent: controller, curve: const Interval(0.9, 1.0)));
- Timer(const Duration(milliseconds: 200), () {
- controller.forward();
- setState(() {});
- });
- Timer(const Duration(milliseconds: 3000), () {
- showGuide();
- });
- }
- void updateProgress() {
- FastBean? fastBean = Global().currentFast;
- if (fastBean == null) {
- setState(() {
- status = 0;
- });
- return;
- }
- int status1 = 0;
- int targetDuration = (fastBean.endTime! - fastBean.startTime!) ~/ 60;
- int tH = targetDuration ~/ 60;
- int tM = (targetDuration % 60).toInt();
- List<int> result = leftHourAndMinute(tH, tM);
- int leftHour1 = result[0];
- int leftMinute1 = result[1];
- if (fastBean.ongoing == true) {
- if (fastBean.mode == 'SINGLE') {
- status1 = 1;
- } else if (fastBean.mode == 'CHALLENGER') {
- status1 = 2;
- getCheckinInfo();
- }
- }
- DateTime time = fastBean.ongoing == true
- ? DateTime.fromMillisecondsSinceEpoch(fastBean.startTime! * 1000)
- : serverTime();
- setState(() {
- currentFast = fastBean;
- targetHour = tH;
- targetMinute = tM;
- leftHour = leftHour1;
- leftMinute = leftMinute1;
- status = status1;
- beginTime = time;
- recordBegin = fastBean.startTime! * 1000;
- recordTarget = fastBean.endTime! * 1000;
- recordEnd = serverTime().millisecondsSinceEpoch;
- enterTimes = 6;
- });
- if (fastBean.ongoing == false ||
- fastBean.endTime! > serverTime().millisecondsSinceEpoch ~/ 1000) {
- return;
- }
- Timer(const Duration(seconds: 1), () {
- if (status == 2) {
- bool supertimeout = false;
- if (serverTime().millisecondsSinceEpoch ~/ 1000 - fastBean.startTime! >
- 24 * 3600) {
- supertimeout = true;
- }
- Global().homePage.showCheckout(false);
- return;
- }
- //show record page
- Timer(const Duration(seconds: 2), () {
- showRecordConfirm(currentFast!.startTime!,
- serverTime().millisecondsSinceEpoch ~/ 1000, currentFast!.endTime!);
- });
- });
- }
- List<int> leftHourAndMinute(int h, int m) {
- int leftHour1 = 24;
- int leftMinute1 = 0;
- if (m > 0) {
- leftHour1--;
- leftMinute1 = 60 - m;
- }
- leftHour1 -= h;
- return [leftHour1, leftMinute1];
- }
- void getCheckinInfo() {}
- DateTime serverTime() {
- int milliseconds = DateTime.now().millisecondsSinceEpoch;
- milliseconds = milliseconds + Global().timeSeconds * 1000;
- return DateTime.fromMillisecondsSinceEpoch(milliseconds);
- }
- void demo() {
- showDialog(
- context: context,
- barrierDismissible: false,
- barrierColor: Colors.transparent,
- useSafeArea: false,
- builder: (BuildContext context) {
- return Record(
- fast: this,
- begin: 0,
- end: 0,
- target: 0,
- );
- });
- }
- Future showGuide() async {
- bool? hasShowGuide = StorageUtil().prefs!.getBool('hasShowGuide');
- if (hasShowGuide != null && hasShowGuide) {
- return;
- }
- StorageUtil().prefs!.setBool('hasShowGuide', true);
- showDialog(
- context: context,
- barrierDismissible: false,
- barrierColor: const Color(0x99000D1F),
- useSafeArea: false,
- builder: (BuildContext context) {
- return Guide(
- endLeft: endLeft,
- endTop: endTop,
- left: circleWidth / 2.0 +
- Global().progressWidth * cos(beginAngle) -
- paintWidth / 2.0,
- top: circleWidth / 2.0 +
- Global().progressWidth * sin(beginAngle) -
- paintWidth / 2.0,
- );
- });
- }
- calculate() {
- enterTimes--;
- var count = (serverTime().millisecondsSinceEpoch -
- beginTime.millisecondsSinceEpoch) /
- 60000;
- var hour = count ~/ 60;
- var minute = (count % 60).toInt();
- var count1 = (serverTime().millisecondsSinceEpoch -
- beginTime.millisecondsSinceEpoch) /
- 1000;
- var second = (count1 % 3600 % 60).toInt();
- var secondCount = (serverTime().millisecondsSinceEpoch -
- beginTime.millisecondsSinceEpoch) /
- 1000;
- var millis = beginTime.millisecondsSinceEpoch;
- millis = millis + (targetHour * 60 + targetMinute) * 60 * 1000;
- var targetDate = DateTime.fromMillisecondsSinceEpoch(millis);
- var leftSeconds = ((targetDate.millisecondsSinceEpoch -
- serverTime().millisecondsSinceEpoch) /
- 1000)
- .floor();
- if (leftSeconds < 60) {
- leftSeconds++;
- }
- var show1 = false;
- var show2 = false;
- var show3 = false;
- if (leftSeconds < 0 || leftSeconds >= 60) {
- show1 = true;
- } else if (leftSeconds >= 10) {
- show2 = true;
- } else {
- show3 = true;
- }
- setState(() {
- beginCount = (hour.toString()).padLeft(2, '0') +
- ':' +
- (minute.toString()).padLeft(2, '0');
- beginSecond = second.toString().padLeft(2, '0');
- beginFormateTime = (hour.toString()).padLeft(2, '0') +
- ':' +
- (minute.toString()).padLeft(2, '0') +
- ':' +
- second.toString().padLeft(2, '0');
- currentAngel = count / (24 * 60) * 2 * pi;
- // rotate = 180
- showBeginCount = show1;
- showMinuteCountdown = show2;
- showSecondCountdown = show3;
- strSecondCountdown = leftSeconds.toString();
- strMinuteCountdown = '00:' + (leftSeconds.toString()).padLeft(2, '0');
- hiddenTime = secondCount >= 6 && (enterTimes < 0 || enterTimes > 6);
- showTips = (secondCount > 0 && secondCount < 4) ||
- (enterTimes > 1 && enterTimes < 4);
- });
- if (leftSeconds == 0) {
- achieve();
- }
- }
- void start() {
- showDialog(
- context: context,
- barrierDismissible: false,
- useSafeArea: false,
- barrierColor: const Color(0xFF000D1F),
- builder: (BuildContext context) {
- return ChooseMode(
- seconds: targetHour * 3600 + targetMinute * 60,
- callback: (type) {
- startFast(type);
- },
- );
- });
- }
- Future startFast(type) async {
- int days = [1, 3, 5, 7][type];
- int begin = DateTime.now().millisecondsSinceEpoch ~/ 1000;
- int end = begin + targetHour * 3600 + targetMinute * 60;
- Map<String, dynamic> data = await HttpUtils.post(Api.start, data: {
- "days": days,
- "mode": type == 0 ? 'SINGLE' : "CHALLENGER",
- "start_time": begin,
- "end_time": end
- });
- FastBean bean = FastBean.fromJson(data);
- setState(() {
- status = type == 0 ? 1 : 2;
- beginTime = serverTime();
- enterTimes = 6;
- currentFast = bean;
- });
- Global().homePage.getDatas();
- }
- void end() {
- if (Global().allowNotification == false && Global().pushEnable) {
- Util().showNotificationStatus(context);
- return;
- }
- if (status == 2) {
- showConfirm('确定现在退出\n"${currentFast!.days}天连续计划"吗?', () {
- Global().homePage.showCheckout(true);
- });
- return;
- }
- int secondsCount = (serverTime().millisecondsSinceEpoch -
- beginTime.millisecondsSinceEpoch) ~/
- 1000;
- int target = targetHour * 3600 + targetMinute * 60;
- if (secondsCount < 3600) {
- showConfirm('断食时长过短,将不被记录\n确定要结束吗?', () {
- giveupRecord();
- });
- } else if (target - secondsCount > 60) {
- showConfirm('还未到预定断食结束时间,\n确定要结束吗?', () {
- showRecordConfirm(currentFast!.startTime!,
- serverTime().millisecondsSinceEpoch ~/ 1000, currentFast!.endTime!);
- });
- } else {
- showRecordConfirm(currentFast!.startTime!,
- serverTime().millisecondsSinceEpoch ~/ 1000, currentFast!.endTime!);
- }
- }
- void showRecordConfirm(int begin, int end, int target) {
- if (hasShowSingleConfirm || Global().currentFast == null) {
- //防止重复弹窗打开
- // print("lalalal");
- return;
- }
- hasShowSingleConfirm = true;
- showDialog(
- context: context,
- barrierDismissible: false,
- barrierColor: Colors.transparent,
- useSafeArea: false,
- builder: (BuildContext context) {
- return Record(
- fast: this,
- begin: begin,
- end: end,
- target: target,
- );
- });
- }
- void closeRecordConfirm() {
- hasShowSingleConfirm = false;
- }
- 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();
- });
- });
- }
- //单次模式 确认记录
- Future confirmRecord(endTimeSeconds, isRightTime, enterTime) async {
- Map<String, dynamic> data = await HttpUtils.post(Api.end,
- data: {"real_end_time": endTimeSeconds, "real_entry_dt": enterTime});
- if (data != null) {}
- reset();
- Global().mainPage.showMe();
- showDialog(
- context: context,
- barrierDismissible: false,
- barrierColor: Colors.transparent,
- builder: (BuildContext context) {
- return Toast(
- title: isRightTime ? '成功记录' : '完成记录',
- content: const SizedBox(
- width: 0,
- height: 0,
- ),
- );
- });
- }
- Future expandTime(seconds) async {
- Map<String, dynamic> data =
- await HttpUtils.post(Api.delay, data: {"seconds": seconds});
- Global().homePage.getDatas();
- String strTime = Util().betweenTimeBySeconds(seconds);
- showDialog(
- context: context,
- barrierDismissible: false,
- barrierColor: Colors.transparent,
- builder: (BuildContext context) {
- return Toast(
- title: '记录延时\n$strTime',
- content: const SizedBox(
- width: 0,
- height: 0,
- ),
- );
- });
- }
- //单次模式 放弃记录
- Future giveupRecord() async {
- Map<String, dynamic> data = await HttpUtils.post(Api.giveUp, data: {});
- if (data != null) {}
- reset();
- }
- void reset() {
- setState(() {
- status = 0;
- currentAngel = 0;
- beginTime = DateTime.now();
- hiddenTime = false;
- beginSecond = '00';
- beginCount = '00:00';
- });
- Global().homePage.getDatas();
- }
- void expand() {}
- void achieve() {
- if (status == 1) {
- showRecordConfirm(currentFast!.startTime!, currentFast!.endTime!,
- currentFast!.endTime!);
- } else if (status == 2) {
- Global().homePage.showCheckout(false);
- }
- }
- void setBeginTime(now) {
- // var now = DateTime.now();
- var countMinutes = now.minute + (now.hour - 6) * 60;
- var millis = now.millisecondsSinceEpoch;
- millis = millis + (targetHour * 60 + targetMinute) * 60 * 1000;
- var end = DateTime.fromMillisecondsSinceEpoch(millis);
- var angel = countMinutes / (24 * 60) * 2 * pi;
- var temp = (end.minute + (end.hour - 6) * 60) / (24 * 60) * 2 * pi;
- var left = circleWidth / 2 + Global().progressWidth * cos(temp);
- var top = circleWidth / 2 + Global().progressWidth * sin(temp);
- setState(() {
- beginHour = now.hour.toString();
- beginMinutes = now.minute.toString();
- endHour = end.hour.toString();
- endMinutes = end.minute.toString();
- beginAngle = angel;
- arcAngle = temp;
- endLeft = left - Global().paintWidth / 2.0;
- endTop = top - Global().paintWidth / 2.0;
- });
- }
- Future<ui.Image> getAssetImage(String asset, {width, height}) async {
- ByteData data = await rootBundle.load(asset);
- ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),
- targetWidth: width, targetHeight: height);
- ui.FrameInfo fi = await codec.getNextFrame();
- return fi.image;
- }
- _getLocalImage() async {
- ui.Image imageFrame = await getAssetImage('assets/images/seek_start.png',
- width: 120, height: 120);
- ui.Image imageFrame2 =
- await getAssetImage('assets/images/ing.png', width: 120, height: 120);
- setState(() {
- startImage = imageFrame;
- progressImage = imageFrame2;
- isLoad = true;
- });
- }
- void updateArc(dx, dy, width) {
- if (status != 0) {
- return;
- }
- double x = dx - circleWidth / 2;
- double y = dy - circleWidth / 2;
- double angel = atan2(y, x);
- if (angel < 0) {
- angel = 2 * pi + angel;
- }
- var left = circleWidth / 2 + Global().progressWidth * cos(angel);
- var top = circleWidth / 2 + Global().progressWidth * sin(angel);
- double endAngel = angel + pi / 2;
- if (endAngel >= 2 * pi) {
- endAngel -= 2 * pi;
- }
- int endCount = endAngel * 24 * 60 ~/ (2 * pi);
- int endHourTemp = endCount ~/ 60;
- int endMinuteTemp = (endCount % 60).toInt();
- int nowHour = serverTime().hour;
- int nowMinute = serverTime().minute;
- if (endHourTemp < nowHour) {
- endHourTemp += 24;
- } else if (endHourTemp == nowHour && endMinuteTemp < nowMinute) {
- endHourTemp += 24;
- }
- int targetHourTemp, targetMinuteTemp;
- if (endMinuteTemp >= nowMinute) {
- targetMinuteTemp = endMinuteTemp - nowMinute;
- } else {
- targetMinuteTemp = 60 + endMinuteTemp - nowMinute;
- endHourTemp -= 1;
- }
- targetHourTemp = endHourTemp - nowHour;
- targetMinuteTemp = ((targetMinuteTemp % 60) / 15).floor() * 15;
- if (targetHourTemp * 60 + targetMinuteTemp < 60 ||
- targetHourTemp * 60 + targetMinuteTemp > 23 * 60) {
- return;
- }
- int leftHourTemp = 24;
- int leftMinuteTemp = 0;
- if (targetMinuteTemp > 0) {
- leftHourTemp--;
- leftMinuteTemp = 60 - targetMinuteTemp;
- }
- leftHourTemp -= targetHourTemp;
- if (targetMinute == 0) {
- Vibrate.feedback(FeedbackType.success);
- }
- // if (targetMinute != targetMinuteTemp) {
- // Vibrate.feedback(FeedbackType.success);
- // }
- setState(() {
- arcAngle = angel;
- endLeft = left - 20.px;
- endTop = top - 20.px;
- endHour = endHourTemp.toString();
- endMinutes = endMinuteTemp.toString();
- targetHour = targetHourTemp;
- targetMinute = targetMinuteTemp;
- leftHour = leftHourTemp;
- leftMinute = leftMinuteTemp;
- });
- }
- getTextWidget() {
- if (showBeginCount) {
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Container(
- foregroundDecoration: const BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [Color(0xAA000D1F), Color(0x00000D1F)])),
- child: Text(
- beginCount,
- style: TextStyle(
- height: 1.0,
- fontFamily: 'Fast',
- color: kThemeColor,
- fontSize: 32.px),
- ),
- ),
- Text(
- beginSecond,
- style: TextStyle(
- color: kThemeColor,
- fontFamily: 'Fast',
- fontSize: 64.px,
- height: 1.0),
- ),
- SizedBox(
- height: 10.px,
- )
- ],
- );
- } else if (showSecondCountdown) {
- return Container(
- margin: EdgeInsets.only(bottom: 20.px),
- child: Text(
- strSecondCountdown,
- style: TextStyle(
- color: Colors.white,
- fontFamily: 'Exo2',
- fontWeight: FontWeight.w900,
- fontSize: 96.px),
- ),
- );
- }
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- strMinuteCountdown,
- style: TextStyle(
- color: Colors.white,
- height: 1.0,
- fontFamily: 'Fast',
- fontSize: 40.px),
- ),
- Container(
- foregroundDecoration: const BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [Color(0x00000D1F), Color(0xFF000D1F)])),
- child: Text(
- beginFormateTime,
- style: TextStyle(
- color: kThemeColor,
- fontFamily: 'Fast',
- height: 1.0,
- fontSize: 28.px),
- ),
- )
- ],
- );
- }
- bubbleWidget(strHour, strMinute, width) {
- return Row(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Container(
- margin: EdgeInsets.only(top: 2.px, left: 0.px),
- child: Text(strHour,
- style: TextStyle(
- color: kThemeColor,
- fontFamily: "Exo2",
- fontWeight: FontWeight.w600,
- fontSize: 32.px)),
- ),
- Text(strMinute,
- style: TextStyle(
- color: kThemeColor,
- fontFamily: "Exo2",
- fontWeight: FontWeight.w600,
- fontSize: 32.px))
- ],
- ).relationOne(
- // -40.px + width / 2.0 + 8.px,
- -40.px + width / 2.0,
- -35.px,
- BubbleWidget(
- 80.px,
- 42.px,
- kThemeColor,
- BubbleArrowDirection.bottom,
- arrAngle: 80.px,
- arrHeight: 8.px,
- child: Text(
- '断食中…',
- style: TextStyle(
- color: Colors.black,
- fontSize: 14.px,
- fontWeight: FontWeight.bold),
- ),
- ));
- }
- getTimeStatusWidget() {
- if (hiddenTime) {
- return Container(
- alignment: Alignment.center,
- child: RRectButton(
- status == 1 ? '结束断食' : '结束挑战',
- width: 144.px,
- height: 50.px,
- backgroundColor: const Color(0x1AC4CCDA),
- textColor: const Color(0xFFC4CCDA),
- radius: 25.px,
- fontSize: 16.px,
- onPressed: () => end(),
- ));
- }
- String strHour =
- targetMinute == 0 ? targetHour.toString() : targetHour.toString() + ':';
- String strMinute =
- targetMinute == 0 ? '' : targetMinute.toString().padLeft(2, '0');
- double width = Util()
- .boundingTextSize(
- strHour,
- TextStyle(
- fontFamily: 'Exo2',
- fontWeight: FontWeight.w600,
- fontSize: 32.px))
- .width +
- Util()
- .boundingTextSize(
- strMinute,
- TextStyle(
- fontFamily: 'Exo2',
- fontWeight: FontWeight.w600,
- fontSize: 32.px))
- .width;
- return Container(
- height: 50.px,
- alignment: Alignment.center,
- child: Row(
- children: [
- Expanded(
- child:
- Row(crossAxisAlignment: CrossAxisAlignment.end, children: [
- Expanded(
- child: SizedBox(
- width: 0.px,
- )),
- showTips
- ? const SizedBox(
- width: 0,
- height: 0,
- ).relationOne(
- 80.px - width, 0, bubbleWidget(strHour, strMinute, width))
- : Row(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Text(strHour,
- style: TextStyle(
- color: kThemeColor,
- fontFamily: "Exo2",
- fontWeight: FontWeight.w600,
- fontSize: 32.px)),
- Text(strMinute,
- style: TextStyle(
- color: kThemeColor,
- fontFamily: "Exo2",
- fontWeight: FontWeight.w600,
- fontSize: 32.px))
- ],
- )
- ])),
- SizedBox(
- width: 10.px,
- ),
- Container(
- margin: EdgeInsets.only(
- top: 4.px,
- ),
- child: Image.asset(
- 'assets/images/seperate1.png',
- width: 8.px,
- height: 16.px,
- ),
- ),
- SizedBox(
- width: 10.px,
- ),
- Expanded(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Text(
- leftMinute == 0
- ? leftHour.toString()
- : leftHour.toString() + ':',
- style: TextStyle(
- color: Colors.white,
- fontFamily: "Exo2",
- fontSize: 32.px)),
- Text(
- leftMinute == 0
- ? ''
- : leftMinute.toString().padLeft(2, '0'),
- style: TextStyle(
- color: Colors.white,
- fontFamily: "Exo2",
- fontSize: 32.px),
- )
- ])),
- ],
- ));
- }
- @override
- Widget build(BuildContext context) {
- final size = MediaQuery.of(context).size;
- final width = size.width;
- var step = 0.171;
- var arcTemp = 0.0;
- // beginAngle = -pi/2.0;
- // arcAngle = pi+pi*2/3.0;
- if (beginAngle > pi) {
- beginAngle = beginAngle - 2 * pi;
- }
- if (arcAngle - beginAngle > 2 * pi) {
- arcAngle = arcAngle - 2 * pi;
- }
- if (beginAngle > 0 && arcAngle < 0) {
- arcAngle = arcAngle + 2 * pi;
- }
- if (beginAngle > arcAngle) {
- arcAngle = arcAngle + 2 * pi;
- }
- // if (beginAngle > pi) {
- // arcTemp = (beginAngle - 2 * pi) * (1 - animation3.value) +
- // arcAngle * animation3.value;
- // } else {
- // arcTemp =
- // beginAngle * (1 - animation3.value) + arcAngle * animation3.value;
- // }
- arcTemp = beginAngle * (1 - animation3.value) + arcAngle * animation3.value;
- return Stack(children: [
- // SizedBox(width: 375.px,),
- if (status == 2)
- Positioned(
- right: 14.px,
- top: 0.px,
- child: Opacity(
- opacity: animation4.value,
- child: Container(
- width: 64.px,
- height: 54.px,
- padding: EdgeInsets.all(8.px),
- decoration: BoxDecoration(
- color: const Color(0x1AC4CCDA),
- borderRadius: BorderRadius.all(Radius.circular(12.px)),
- ),
- child: Column(
- children: [
- Text(
- '${currentFast!.days}天挑战',
- style: TextStyle(
- color: const Color(0x66FFFFFF),
- fontSize: 10.px,
- height: 1.0),
- ),
- Container(
- height: 1.px,
- color: const Color(0x1AFFFFFF),
- margin: EdgeInsets.only(top: 5.px, bottom: 5.px),
- ),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Text(
- '${currentFast!.finishDays}',
- style: TextStyle(
- color: const Color(0xFFC4CCDA),
- fontFamily: 'Exo2',
- fontWeight: FontWeight.w600,
- fontSize: 16.px,
- height: 1.0),
- ),
- SizedBox(
- width: 2.px,
- ),
- Image.asset(
- 'assets/images/seperate1.png',
- width: 4.px,
- height: 8.px,
- ),
- SizedBox(
- width: 2.px,
- ),
- Text(
- '${currentFast!.days}',
- style: TextStyle(
- color: const Color(0xFFC4CCDA),
- fontFamily: 'Exo2',
- fontWeight: FontWeight.w600,
- fontSize: 16.px,
- height: 1.0),
- ),
- ],
- )
- ],
- ),
- ))),
- SingleChildScrollView(
- physics: const NeverScrollableScrollPhysics(),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- Stack(
- children: [
- Opacity(
- opacity: animation0.value,
- child: Transform.scale(
- scale: animation0.value,
- child: Container(
- width: circleWidth,
- height: circleWidth,
- margin: EdgeInsets.fromLTRB(0, 0, 0, 16.px),
- decoration: const BoxDecoration(
- image: DecorationImage(
- alignment: Alignment.topLeft,
- image:
- AssetImage('assets/images/seek_bg.png'))),
- child: Opacity(
- opacity: animation2.value,
- child: CustomPaint(
- foregroundPainter: TargetPainter(
- lineColor: Colors.yellow,
- completeColor: Colors
- .orange, //[Colors.orange,Colors.blue],
- // arcAngle: arcAngle*animation3.value,
- arcAngle: arcTemp,
- beginAngle: beginAngle,
- width: paintWidth,
- localImage: isLoad ? startImage : null),
- ),
- ),
- ),
- ),
- ),
- // Positioned(child: Container(width: 40,height: 40,color: Colors.red,)),
- Opacity(
- opacity: animation3.value,
- child: Container(
- width: paintWidth,
- height: paintWidth,
- decoration: BoxDecoration(
- borderRadius:
- BorderRadius.all(Radius.circular(20.px)),
- color: const Color(0xFFC4CCDA)),
- margin: EdgeInsets.only(left: endLeft, top: endTop),
- ),
- ),
- if (status != 0)
- Transform.rotate(
- angle: beginAngle - step,
- child: SizedBox(
- width: circleWidth,
- height: circleWidth,
- child: CustomPaint(
- foregroundPainter: ProgressPainter(
- lineColor: Colors.yellow,
- completeColor:
- Colors.orange, //[Colors.orange,Colors.blue],
- arcAngle: currentAngel * animation3.value,
- beginAngle: beginAngle,
- width: paintWidth,
- localImage: isLoad ? progressImage : null),
- ),
- ),
- ),
- if (status != 0) ingImg(beginAngle, currentAngel),
- // Opacity(
- // opacity: animation4.value,
- // child: ingImg(beginAngle, currentAngel),
- // ),
- Opacity(
- opacity: animation4.value,
- child: Container(
- alignment: Alignment.center,
- child: status == 0
- ? TextButton(
- onPressed: () => {start()},
- child: Image(
- image:
- const AssetImage('assets/images/begin.png'),
- width: Global().paintWidth * 2,
- height: Global().paintWidth * 2,
- ),
- )
- : getTextWidget(),
- width: circleWidth,
- height: circleWidth,
- ),
- ),
- startImg(beginAngle),
- Opacity(
- opacity: animation3.value,
- child: GestureDetector(
- onTapDown: (TapDownDetails details) {
- updateArc(details.localPosition.dx,
- details.localPosition.dy, width);
- },
- onLongPressMoveUpdate: (details) {
- updateArc(details.localPosition.dx,
- details.localPosition.dy, width);
- },
- onHorizontalDragUpdate: (details) {
- updateArc(details.localPosition.dx,
- details.localPosition.dy, width);
- },
- onVerticalDragUpdate: (details) {
- updateArc(details.localPosition.dx,
- details.localPosition.dy, width);
- },
- child: Container(
- margin: EdgeInsets.fromLTRB(endLeft, endTop, 0, 0),
- child: Image(
- image: const AssetImage('assets/images/seek_end.png'),
- width: paintWidth,
- height: paintWidth,
- ),
- ),
- ),
- ),
- ],
- ),
- Opacity(
- opacity: animation2.value,
- child: getTimeStatusWidget(),
- )
- ],
- ))
- ]);
- }
- ingImg(beginAngle, currentAngel) {
- if (currentAngel == 0) {
- return const SizedBox(
- width: 0.0,
- height: 0.0,
- );
- }
- var step = 0.171;
- var maxEnd = beginAngle + currentAngel;
- if (maxEnd > 2 * pi - step) {
- maxEnd = 2 * pi - 2 * step;
- }
- return Positioned(
- left: circleWidth / 2.0 +
- Global().progressWidth * cos(beginAngle + currentAngel) -
- paintWidth / 2.0,
- top: circleWidth / 2.0 +
- Global().progressWidth * sin(beginAngle + currentAngel) -
- paintWidth / 2.0,
- child: Transform.rotate(
- angle: beginAngle + currentAngel - pi / 2.0,
- child: Opacity(
- opacity: animation4.value,
- child: Container(
- color: Colors.transparent,
- width: paintWidth,
- height: paintWidth,
- child: FrameAnimationImage(assetList: const [
- 'assets/images/a_00000.png',
- 'assets/images/a_00001.png',
- 'assets/images/a_00002.png',
- 'assets/images/a_00003.png',
- 'assets/images/a_00004.png',
- 'assets/images/a_00005.png',
- 'assets/images/a_00006.png',
- 'assets/images/a_00007.png',
- 'assets/images/a_00008.png',
- 'assets/images/a_00009.png',
- 'assets/images/a_00010.png',
- 'assets/images/a_00011.png',
- 'assets/images/a_00012.png',
- 'assets/images/a_00013.png',
- 'assets/images/a_00014.png',
- 'assets/images/a_00015.png',
- ], width: paintWidth, height: paintWidth, interval: 40),
- ),
- ),
- ));
- }
- startImg(beginAngle) {
- var step = 0.171;
- var maxEnd = beginAngle;
- if (maxEnd > 2 * pi - step) {
- maxEnd = 2 * pi - 2 * step;
- }
- return Positioned(
- left: circleWidth / 2.0 +
- Global().progressWidth * cos(beginAngle) -
- paintWidth / 2.0,
- top: circleWidth / 2.0 +
- Global().progressWidth * sin(beginAngle) -
- paintWidth / 2.0,
- child: Opacity(
- opacity: animation4.value,
- child: Container(
- color: Colors.transparent,
- width: paintWidth,
- height: paintWidth,
- child: Image.asset(
- 'assets/images/seek_start.png',
- width: paintWidth,
- height: paintWidth,
- ),
- ),
- ),
- );
- }
- }
- // ignore: must_be_immutable
- class TextGradientColorWidget extends StatefulWidget {
- String data;
- TextStyle? style;
- List<Color> colors;
- TextGradientColorWidget({Key? key, required this.data, required this.colors})
- : super(key: key);
- @override
- _TextGradientColorState createState() => _TextGradientColorState();
- }
- class _TextGradientColorState extends State<TextGradientColorWidget> {
- WidgetsBinding? widgetsBinding = WidgetsBinding.instance;
- TextStyle? textStyle;
- @override
- void initState() {
- // TODO: implement initState
- super.initState();
- widgetsBinding!.addPostFrameCallback((timeStamp) {
- final RenderBox? box = context.findRenderObject() as RenderBox;
- var left = 0.0;
- var width = 0.0;
- if (box != null) {
- final topLeftPosition = box.localToGlobal(Offset.zero);
- final size = box.size;
- left = topLeftPosition.dx;
- width = size.width;
- setState(() {
- textStyle = TextStyle(
- fontSize: 30.px,
- fontFamily: 'Fast',
- foreground: Paint()
- ..shader = LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: widget.colors,
- ).createShader(Rect.fromLTWH(left - 20,
- topLeftPosition.dy - 260, width, size.height + 20)));
- });
- //注意 TextStyle中color和foreground 中colors不能同时设置
- }
- });
- }
- @override
- Widget build(BuildContext context) {
- // TODO: implement build
- return Text(widget.data, style: textStyle);
- }
- }
|