123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095 |
- import 'dart:math' as math;
- import 'package:flutter/material.dart';
- import 'package:flutter/scheduler.dart';
- import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
- import 'package:get_it/get_it.dart';
- import 'package:provider/provider.dart';
- import 'package:sport/bean/sport_detail.dart';
- import 'package:sport/bean/sport_index.dart';
- import 'package:sport/pages/data/sport_data_page.dart';
- import 'package:sport/pages/data/sport_reference_page.dart';
- import 'package:sport/pages/home/duration_setting_page.dart';
- import 'package:sport/pages/home/strength_page.dart';
- import 'package:sport/pages/run/statistics_page.dart';
- import 'package:sport/provider/bluetooth.dart';
- import 'package:sport/provider/user_model.dart';
- import 'package:sport/router/navigator_util.dart';
- import 'package:sport/services/api/inject_api.dart';
- import 'package:sport/services/api/resp.dart';
- import 'package:sport/utils/sport_utils.dart';
- import 'package:sport/widgets/chart.dart';
- import 'package:sport/widgets/circular_percent_indicator.dart';
- import 'package:sport/widgets/data_chart.dart';
- import 'package:sport/widgets/decoration.dart';
- import 'package:sport/widgets/image.dart';
- import 'package:sport/widgets/loading.dart';
- import 'package:umeng_common_sdk/umeng_common_sdk.dart';
- class SportDataDetailPage extends StatefulWidget {
- final int type;
- final int sportType;
- final DateTime time;
- final DateTime? selectDate;
- final int index;
- const SportDataDetailPage({Key? key, required this.type, required this.sportType, required this.time, this.selectDate, required this.index}) : super(key: key);
- @override
- State<StatefulWidget> createState() => SportDataDetailPageState();
- }
- class SportDataDetailPageState extends State<SportDataDetailPage> with InjectApi, AutomaticKeepAliveClientMixin {
- int _tabIndex = 0;
- int _index = -1;
- int _initialPage = 0;
- SportDetail? _detail;
- final List<Map<String, dynamic>> tabs = [
- {"icon": "icon_table_consume_default", "icon_select": "icon_table_consume_select", "name": "消耗", "color": 0xffFFC400},
- {"icon": "icon_table_steps_default", "icon_select": "icon_table_steps_select", "name": "步数", "color": 0xff27D171},
- {"icon": "icon_table_duration_default", "icon_select": "icon_table_duration_select", "name": "时长", "color": 0xff5498FF},
- {"icon": "icon_table_strength_default", "icon_select": "icon_table_strength_select", "name": "强度", "color": 0xffFF5B1D},
- ];
- @override
- void initState() {
- super.initState();
- _tabIndex = widget.sportType;
- _loadData();
- }
- _loadData() async {
- setState(() {
- _detail = null;
- });
- var bluetooth = GetIt.I<Bluetooth>();
- if (bluetooth.isConnected == true) {
- await bluetooth.queryDeviceStep();
- }
- createFuture(widget.time).then((value) {
- if (mounted)
- setState(() {
- _detail = value;
- });
- });
- }
- Text title() {
- int type = widget.type;
- DateTime now = DateTime.now();
- DateTime time = widget.time;
- if (type == 0) {
- return Text(
- "${time.year} ${'${time.month}'.padLeft(2, '0')}-${'${time.day}'.padLeft(2, '0')} ${WEEK[time.weekday == 0 ? 6 : time.weekday - 1]}${now.day == time.day ? "(今天)" : ""}",
- style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
- );
- } else if (type == 1) {
- DateTime start = DateTime(time.year, time.month, time.day - time.weekday + 1);
- DateTime end = DateTime(time.year, time.month, time.day + 6 - time.weekday + 1);
- return Text(
- "${start.year} ${'${start.month}'.padLeft(2, '0')}-${'${start.day}'.padLeft(2, '0')} 至 ${start.year == end.year ? "" : "${end.year} "}${'${end.month}'.padLeft(2, '0')}-${'${end.day}'.padLeft(2, '0')}${now.day >= time.day && now.day < end.day ? "(本周)" : ""}",
- style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
- );
- } else if (type == 2) {
- return Text(
- "${time.year} ${'${time.month}'.padLeft(2, '0')}月${now.month == time.month ? "(本月)" : ""}",
- style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
- );
- } else if (type == 3) {
- return Text(
- "${time.year}年",
- style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.white),
- );
- }
- return Text("");
- }
- Future<SportDetail?> createFuture(DateTime time) {
- int type = widget.type;
- return createFutureType(type, time);
- }
- Future<SportDetail?> createFutureType(int type, DateTime time) async {
- Future<RespData<SportDetailSimple>>? data;
- DateTime now = widget.selectDate == null ? DateTime.now() : widget.selectDate!;
- switch (type) {
- case 0:
- data = api.getSportRecordListOneDay('${time.year}-${'${time.month}'.padLeft(2, '0')}-${'${time.day}'.padLeft(2, '0')}');
- break;
- case 1:
- _initialPage = now.weekday - 1;
- DateTime start = DateTime(time.year, time.month, time.day - time.weekday + 1);
- DateTime end = DateTime(time.year, time.month, time.day + 6 - time.weekday + 1);
- data = api.getSportRecordListByDay('${start.year}-${'${start.month}'.padLeft(2, '0')}-${'${start.day}'.padLeft(2, '0')}', '${end.year}-${'${end.month}'.padLeft(2, '0')}-${'${end.day}'.padLeft(2, '0')}');
- break;
- case 2:
- _initialPage = now.day - 1;
- DateTime start = DateTime(time.year, time.month, 1);
- DateTime end = DateTime(time.year, time.month + 1, 0);
- data = api.getSportRecordListByDay('${start.year}-${'${start.month}'.padLeft(2, '0')}-${'${start.day}'.padLeft(2, '0')}', '${end.year}-${'${end.month}'.padLeft(2, '0')}-${'${end.day}'.padLeft(2, '0')}');
- break;
- case 3:
- _initialPage = now.month - 1;
- data = api.getSportRecordListByMonth(time.year);
- break;
- }
- _index = _initialPage = -1;
- if (data != null) {
- var simple = await data;
- if (simple.code == 0) {
- return SportDetail(
- target: simple.data?.target,
- exerDayTotal: simple.data?.exerDayTotal ?? 0,
- recordsTodaySum: simple.data?.sum ?? RecordsTodaySum(consume: 0, duration: 0, crouch: 0, jump: 0),
- recordsTodayAvg: simple.data?.avg ?? RecordsTodaySum(consume: 0, duration: 0, crouch: 0, jump: 0, times: 0, step: 0),
- recordsToday: simple.data?.records,
- targetFinish: simple.data?.targetFinish ?? [],
- exerDay: simple.data?.exerDay ?? []);
- }
- }
- return null;
- }
- String numToStr(num? v, {int asFixed = -1}) {
- return SportUtils.numToStr(v, asFixed: asFixed);
- }
- @override
- Widget build(BuildContext context) {
- super.build(context);
- final List<String> xAxisList = xAxis();
- final List<double> dataList = data();
- final List<double> yAxisList = yAxis(dataList);
- int initialPage = _initialPage;
- String avgConsume = "0";
- String avgStep = "0";
- String avgDuration = "0";
- List<String> tabTitles = ["", "", "", ""];
- if (_detail != null) {
- tabTitles[0] = numToStr(_detail?.recordsTodaySum?.consume);
- tabTitles[1] = numToStr(_detail?.recordsTodaySum?.step);
- tabTitles[2] = numToStr((_detail?.recordsTodaySum?.durationMin ?? 0), asFixed: 0);
- tabTitles[3] = "${_detail?.recordsTodaySum?.met.toStringAsFixed(1)}";
- }
- if (widget.type != 0) {
- avgConsume = numToStr(_detail?.recordsTodayAvg?.consume);
- avgStep = numToStr(_detail?.recordsTodayAvg?.step);
- avgDuration = numToStr(_detail?.recordsTodayAvg?.durationMin ?? 0, asFixed: 0);
- }
- var _typeColor = Color(tabs[_tabIndex]["color"] as int);
- RecordsTodaySum? item = selectRecord(_index);
- var _bg = const Color(0xff241D19);
- return Scaffold(
- backgroundColor: _bg,
- body: RefreshIndicator(
- color: Theme.of(context).colorScheme.secondary,
- onRefresh: () async {
- _loadData();
- },
- child: CustomScrollView(
- slivers: [
- SliverFillRemaining(
- child: Column(
- children: [
- Container(
- color: _bg,
- child: Column(
- children: [
- Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () => PageNotification(page: 1).dispatch(context),
- child: Padding(
- padding: const EdgeInsets.all(25.0),
- child: arrowLeft(color: Colors.white),
- ),
- ),
- InkWell(
- onTap: () {
- SportDataPage.of(context)?.showCalendar(widget.selectDate ?? widget.time);
- UmengCommonSdk.onEvent("sport_data_calendar", {});
- },
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- const SizedBox(
- width: 18,
- ),
- title(),
- const SizedBox(
- width: 4,
- ),
- Image.asset(
- "lib/assets/img/btn_date_bottom.png",
- width: 14,
- ),
- ],
- ),
- )),
- widget.index == 0
- ? Padding(
- padding: const EdgeInsets.all(25.0),
- child: arrowRight(color: Color(0x90cccccc)),
- )
- : GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () => PageNotification(page: -1).dispatch(context),
- child: Padding(
- padding: const EdgeInsets.all(25.0),
- child: arrowRight(color: Colors.white),
- ),
- ),
- ],
- ),
- ),
- // if (widget.type != 0)
- // Padding(
- // padding: const EdgeInsets.fromLTRB(12.0, 0, 12.0, 20.0),
- // child: Row(
- // children: [
- // Expanded(
- // child: Column(
- // children: [
- // Text(
- // "日均大卡",
- // style: Theme
- // .of(context)
- // .textTheme
- // .bodyText1,
- // ),
- // const SizedBox(
- // height: 10,
- // ),
- // Text(
- // avgConsume,
- // style: Theme
- // .of(context)
- // .textTheme
- // .subtitle1
- // ?.copyWith(fontSize: 25, fontFamily: "DIN", color: Colors.white),
- // )
- // ],
- // ),
- // ),
- // Container(
- // width: 0.5,
- // height: 46,
- // color: Color(0xffDCDCDC).withOpacity(.3),
- // ),
- // Expanded(
- // child: Column(
- // children: [
- // Text(
- // "日均步数",
- // style: Theme
- // .of(context)
- // .textTheme
- // .bodyText1,
- // ),
- // const SizedBox(
- // height: 10,
- // ),
- // Text(
- // avgStep,
- // style: Theme
- // .of(context)
- // .textTheme
- // .subtitle1
- // ?.copyWith(fontSize: 25, fontFamily: "DIN", color: Colors.white),
- // )
- // ],
- // ),
- // ),
- // Container(
- // width: 0.5,
- // height: 46,
- // color: Color(0xffDCDCDC).withOpacity(.3),
- // ),
- // Expanded(
- // child: Column(
- // children: [
- // Text(
- // "周均分钟",
- // style: Theme
- // .of(context)
- // .textTheme
- // .bodyText1,
- // ),
- // const SizedBox(
- // height: 10,
- // ),
- // Text(
- // avgDuration,
- // style: Theme
- // .of(context)
- // .textTheme
- // .subtitle1
- // ?.copyWith(fontSize: 25, fontFamily: "DIN", color: Colors.white),
- // )
- // ],
- // ),
- // )
- // ],
- // ),
- // ),
- AlignedGridView.count(
- shrinkWrap: true,
- crossAxisCount: 4,
- crossAxisSpacing: 8,
- padding: const EdgeInsets.fromLTRB(16.0, 10.0, 16.0, 0),
- physics: NeverScrollableScrollPhysics(),
- itemCount: 4,
- itemBuilder: (context, index) {
- var tab = tabs[index];
- return InkWell(
- onTap: () {
- SportTypeNotification(index: index).dispatch(context);
- setState(() {
- _tabIndex = index;
- });
- UmengCommonSdk.onEvent("sport_data_type_$index", {});
- },
- child: Column(
- children: [
- Container(
- width: double.infinity,
- padding: const EdgeInsets.symmetric(vertical: 10.0),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(6),
- color: _tabIndex == index ? Color(tab["color"] as int) : Color(0xfff1f1f1).withOpacity(0.15),
- ),
- child: Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Image.asset(
- "lib/assets/img/${_tabIndex == index ? tab["icon_select"] : tab["icon"]}.png",
- width: 16,
- ),
- const SizedBox(
- width: 5,
- ),
- Text(
- "${(tab["name"] is String) ? tab["name"] : (tab["name"] as List)[widget.type]}",
- style: Theme.of(context).textTheme.bodyText2?.copyWith(color: _tabIndex == index ? Colors.white : Color(0xff999999)),
- ),
- ],
- ),
- ),
- ),
- _tabIndex == index
- ? CustomPaint(
- painter: TrianglePath(),
- child: Container(
- height: 20,
- width: 20,
- ),
- )
- : Container(
- height: 20,
- ),
- ],
- ),
- );
- },
- ),
- ],
- ),
- ),
- Expanded(
- child: Container(
- color: Colors.white,
- child: _detail == null
- ? RequestLoadingWidget()
- : Column(
- children: [
- IndexedStack(
- index: _tabIndex,
- children: [
- Header(
- index: 0,
- type: widget.type,
- params: {
- "date": "${_index < 0 ? "${TABS[widget.type]}总消耗" : item?.getDate(widget.type)}",
- "unit": "大卡",
- "total": "${item?.consume ?? tabTitles[_tabIndex]}",
- "current": "${widget.type > 0 && _index > -1 ? "1" : null}",
- "game": "游戏消耗",
- "jog": "跑步消耗",
- "daily": "日常消耗",
- "game_value": "${item?.consume_game ?? 0}",
- "jog_value": "${item?.consume_jog ?? 0}",
- "daily_value": "${item?.consume_daily ?? 0}",
- "times": "${item?.times}",
- "avg": "$avgConsume"
- },
- recordsTodaySum: item,
- color: _typeColor,
- ),
- Header(
- index: 1,
- type: widget.type,
- params: {
- "date": "${_index < 0 ? "${TABS[widget.type]}总步数" : item?.getDate(widget.type)}",
- "unit": "步",
- "total": "${item?.step ?? tabTitles[_tabIndex]}",
- "current": "${widget.type > 0 && _index > -1 ? "1" : null}",
- "game": "游戏步数",
- "jog": "跑步步数",
- "daily": "日常步数",
- "game_value": "${item?.step_game ?? 0}",
- "jog_value": "${item?.step_jog ?? 0}",
- "daily_value": "${item?.step_daily ?? 0}",
- "times": "${item?.times}",
- "avg": "$avgStep"
- },
- color: _typeColor,
- ),
- Header(
- index: 2,
- type: widget.type,
- params: {
- "date": "${_index < 0 ? "${TABS[widget.type]}总时长" : item?.getDate(widget.type)}",
- "unit": "分钟",
- "total": "${item?.durationMin.toStringAsFixed(0) ?? tabTitles[_tabIndex]}",
- "current": "${widget.type > 0 && _index > -1 ? "1" : null}",
- "game": "游戏时长",
- "jog": "跑步时长",
- "daily": widget.type == 0 || _index > -1 ? "运动目标" : "达标天数",
- "game_value": "${item?.durationMinGame.toStringAsFixed(0) ?? 0}",
- "jog_value": "${item?.durationMinJog.toStringAsFixed(0) ?? 0}",
- "daily_value": widget.type == 0 || _index > -1 ? "${(_detail?.target?.duration ?? 0) ~/ 60}" : "${yAxisList.where((element) => element > (_detail?.target?.duration ?? 0)).isNotEmpty ? yAxisList.where((element) => element > (_detail?.target?.duration ?? 0)).reduce((value, element) => value + element) : 0}",
- "times": "${item?.times}",
- "avg": "$avgDuration"
- },
- settingDuration: widget.type == 0 || _index > -1,
- color: _typeColor,
- ),
- Header(
- index: 3,
- type: widget.type,
- params: {
- "date": "${_index < 0 ? "${TABS[widget.type]}MET" : item?.getDate(widget.type)}",
- "unit": "MET",
- "total": "${(item?.met.toStringAsFixed(1) ?? tabTitles[_tabIndex])}",
- "current": "${widget.type > 0 && _index > -1 ? "1" : null}",
- "game": "游戏消耗",
- "jog": "跑步消耗",
- "daily": "日常消耗",
- "game_value": "${item?.consume_game ?? 0}",
- "jog_value": "${item?.consume_jog ?? 0}",
- "daily_value": "${item?.consume_daily ?? 0}",
- "met": "强度评级",
- "met_value": "${metToLabel(item?.met ?? 0.0)}",
- "met_double": "${item?.met ?? 0.0}",
- "met_desc": "${metToDetail(item?.met ?? 0.0)}",
- },
- color: _typeColor,
- ),
- ],
- ),
- Expanded(child: LayoutBuilder(
- builder: (context, size) {
- return Container(
- height: size.maxHeight,
- child: widget.type == 0 && _tabIndex == 3
- ? Container(
- width: double.infinity,
- child: Center(
- child: Container(
- transform: Matrix4.translationValues(0, -100, 0),
- width: 300,
- height: 300,
- child: CustomPaint(
- painter: StrengthBg(),
- child: Container(
- child: Center(
- child: CircularPercentIndicator(
- radius: 200.0,
- lineWidth: 10.0,
- percent: (item?.met ?? 0) / 12.0,
- // percent: .7,
- center: Column(
- children: <Widget>[
- Text(
- "${item?.met.toStringAsFixed(1)}",
- style: Theme.of(context).textTheme.headline2!.copyWith(fontSize: 40.0, fontFamily: "DIN"),
- ),
- Text(
- "MET值",
- style: Theme.of(context).textTheme.subtitle2,
- )
- ],
- mainAxisSize: MainAxisSize.min,
- ),
- animation: true,
- animationDuration: 1000,
- animateFromLastPercent: true,
- startAngle: 200.0,
- arcType: ArcType.CUSTOM_3,
- backgroundColor: Color(0xfff1f1f1),
- rotateLinearGradient: true,
- circularStrokeCap: CircularStrokeCap.butt,
- linearGradient: LinearGradient(
- colors: <Color>[Color(0xffFFE600), Color(0xffFF7323)],
- ),
- ),
- ),
- ),
- ),
- ),
- ),
- )
- : ChartWidget(
- xAxis: xAxisList,
- yAxis: yAxisList,
- data: dataList,
- initialPage: initialPage,
- color: _typeColor,
- xAxisType: widget.type,
- chartHeight: size.maxHeight,
- targetLine: ((widget.type != 0 && widget.type != 3 && _tabIndex == 2) ? (_detail?.target?.duration.toDouble() ?? 0.0) : 0.0) / 60.0,
- unit: ["大卡", "步", "分钟", ""][_tabIndex],
- onTap: (index, drag) {
- final old = _index;
- setState(() {
- _index = drag
- ? index
- : _index == index
- ? -1
- : index;
- });
- return drag ? false : old == index;
- },
- ),
- );
- },
- ))
- ],
- ),
- ),
- ),
- ],
- ))
- ],
- ),
- ),
- );
- }
- List<double> data() {
- switch (widget.type) {
- case 0:
- {
- List<double> data = List.generate(24, (index) => 0);
- if (_detail != null) {
- for (var i = 0; i < (_detail!.recordsToday?.length ?? 0); i++) {
- var item = _detail!.recordsToday![i];
- for (var j = 0; j < data.length; j++) {
- if (item.isSameHour(j)) {
- data[j] += item.getValue(_tabIndex);
- }
- }
- }
- }
- return data;
- }
- case 1:
- DateTime t = widget.time;
- DateTime week = DateTime(t.year, t.month, t.day).subtract(Duration(days: t.weekday - 1));
- List<double> data = List.filled(7, 0);
- if (_detail != null) {
- for (var i = 0; i < (_detail!.recordsToday?.length ?? 0); i++) {
- var item = _detail!.recordsToday![i];
- for (var j = 0; j < data.length; j++) {
- if (item.isSameDay(week.add(Duration(days: j)).day)) {
- data[j] = item.getValue(_tabIndex);
- }
- }
- }
- }
- return data;
- case 2:
- DateTime t = widget.time;
- DateTime now = DateTime(t.year, t.month, 1);
- DateTime next = DateTime(t.year, t.month + 1, 1);
- int diff = now.difference(next).inDays.abs();
- List<double> data = List.filled(diff, 0);
- if (_detail != null) {
- for (var i = 0; i < (_detail!.recordsToday?.length ?? 0); i++) {
- var item = _detail!.recordsToday![i];
- for (var j = 0; j < data.length; j++) {
- if (item.isSameDay(now.add(Duration(days: j)).day)) {
- data[j] = item.getValue(_tabIndex);
- }
- }
- }
- }
- return data;
- case 3:
- List<double> data = List.filled(12, 0);
- if (_detail != null) {
- for (var i = 0; i < (_detail!.recordsToday?.length ?? 0); i++) {
- var item = _detail!.recordsToday![i];
- data[(item.month) - 1] = item.getValue(_tabIndex);
- }
- }
- return data;
- default:
- return [];
- }
- }
- List<String> xAxis() {
- switch (widget.type) {
- case 0:
- return ["00:00", "06:00", "12:00", "18:00", "00:00"];
- case 1:
- DateTime t = widget.time;
- DateTime now = DateTime(t.year, t.month, t.day - t.weekday);
- return List.generate(7, (index) {
- DateTime time = DateTime(now.year, now.month, now.day + index + 1);
- return "${time.month}/${time.day}\n${WEEK[index]}";
- });
- case 2:
- DateTime t = widget.time;
- DateTime now = DateTime(t.year, t.month, 1);
- DateTime next = DateTime(t.year, t.month + 1, 1);
- int diff = now.difference(next).inDays.abs();
- return ["1", "5", "10", "15", "20", "25", if (diff > 29) "30"];
- case 3:
- return List.generate(12, (index) => "${index + 1}");
- default:
- return [];
- }
- }
- List<double> yAxis(List<double> dataList) {
- if (dataList.isNotEmpty == true) {
- double max = dataList.reduce((value, element) => value > element ? value : element);
- max = math.max(max, ((widget.type != 0 && widget.type != 3 && _tabIndex == 2) ? (_detail?.target?.duration.toDouble() ?? 0.0) : 0.0) / 60.0);
- int count = 5;
- double split = max / count;
- int length = split.round().toString().length;
- int one = int.parse("1" + List.filled(math.max(0, length - 2), 0).join(""));
- int p = one;
- while (p < split) {
- p += one;
- }
- print("1111111111111111 max = $max $split $length $p");
- // print("max $max $p $split");
- return List.generate(count + 1, (index) => index * p.toDouble());
- }
- return [];
- }
- RecordsTodaySum? selectRecord(int index) {
- if (_detail == null) return null;
- if (widget.type == 0 || index < 0) {
- return _detail?.recordsTodaySum;
- }
- DateTime t = widget.time;
- DateTime week = DateTime(t.year, t.month, t.day).subtract(Duration(days: t.weekday - 1));
- var recordsToday = _detail?.recordsToday ?? [];
- if (recordsToday.isNotEmpty == true) {
- for (var element in recordsToday) {
- switch (widget.type) {
- case 1:
- if (element.isSameDay(week.add(Duration(days: index)).day)) return element;
- break;
- case 2:
- if (element.isSameDay(index + 1)) return element;
- break;
- case 3:
- if (element.month == index + 1) return element;
- break;
- }
- }
- }
- switch (widget.type) {
- case 0:
- return RecordsTodaySum(createdAt: "${t.year}-${t.month.toString().padLeft(2, '0')}-${t.day.toString().padLeft(2, '0')}");
- case 1:
- return RecordsTodaySum(createdAt: "${t.year}-${t.month.toString().padLeft(2, '0')}-${(index + week.day).toString().padLeft(2, '0')}");
- case 2:
- return RecordsTodaySum(createdAt: "${t.year}-${t.month.toString().padLeft(2, '0')}-${(index + 1).toString().padLeft(2, '0')}");
- case 3:
- return RecordsTodaySum(createdAt: "${t.year}-${(index + 1).toString().padLeft(2, '0')}-01", year: t.year, month: index + 1);
- default:
- return null;
- }
- }
- @override
- bool get wantKeepAlive => true;
- void refresh() {
- _loadData();
- }
- }
- class Header extends StatelessWidget {
- final int index;
- final int type;
- final Map<String, String> params;
- final RecordsTodaySum? recordsTodaySum;
- final Color color;
- final bool settingDuration;
- const Header({Key? key, required this.index, required this.type, required this.params, this.recordsTodaySum, required this.color, this.settingDuration = false}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- final _marginBox = EdgeInsets.zero;
- final current = params["current"] == "1";
- final color = current ? this.color : const Color(0xff999999);
- final _textStyle = Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 14.0, color: const Color(0xff999999));
- final _textStyleValue = Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 18.0, color: color, fontWeight: FontWeight.w500);
- return Container(
- decoration: circular(),
- padding: const EdgeInsets.all(16.0),
- margin: _marginBox,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- if (!(index == 3 && type == 0))
- Container(
- margin: const EdgeInsets.only(bottom: 16.0),
- height: 70,
- child: Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- params["date"] != "null"
- ? Text(
- "${params["date"]}",
- style: Theme.of(context).textTheme.bodyText1?.copyWith(color: color),
- )
- : Container(),
- Row(
- children: <Widget>[
- Text(
- "${params["total"]}",
- style: current ? Theme.of(context).textTheme.headline2!.copyWith(fontSize: 35.0, fontFamily: "DIN", color: this.color) : Theme.of(context).textTheme.headline2!.copyWith(fontSize: 35.0, fontFamily: "DIN"),
- ),
- Text(
- " ${params["unit"]}",
- style: current ? Theme.of(context).textTheme.subtitle2?.copyWith(color: this.color) : Theme.of(context).textTheme.subtitle2,
- )
- ],
- crossAxisAlignment: CrossAxisAlignment.baseline,
- textBaseline: TextBaseline.alphabetic,
- mainAxisSize: MainAxisSize.min,
- ),
- ],
- ),
- ),
- ),
- if (recordsTodaySum != null)
- FutureBuilder<String>(
- future: Provider.of<UserModel>(context, listen: false).sport(recordsTodaySum),
- builder: (context, snapshot) {
- return InkWell(
- onTap: () async {
- if (await showDialog(
- context: context,
- builder: (context) => SportReferencePage(
- sum: recordsTodaySum!,
- ),
- ) ==
- true) {
- SportDataDetailPageState? state = context.findAncestorStateOfType();
- state?.refresh();
- }
- },
- child: Stack(
- children: [
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
- decoration: BoxDecoration(color: Theme.of(context).scaffoldBackgroundColor, borderRadius: BorderRadius.circular(50)),
- child: Center(
- child: Text(
- "相当于 ${snapshot.data ?? ""}",
- style: Theme.of(context).textTheme.bodyText1,
- ),
- ),
- ),
- Positioned(top: 0, bottom: 0, right: 16.0, child: arrowRight4()),
- ],
- ),
- );
- }),
- if (!params.containsKey("met"))
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 18.0, horizontal: 6),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Text(
- "${params["game"]}",
- style: _textStyle,
- ),
- const SizedBox(
- height: 6,
- ),
- Text(
- "${params["game_value"]}",
- style: _textStyleValue,
- ),
- ],
- ),
- Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Text(
- "${params["jog"]}",
- style: _textStyle,
- ),
- const SizedBox(
- height: 6,
- ),
- Text(
- "${params["jog_value"]}",
- style: _textStyleValue,
- ),
- ],
- ),
- settingDuration
- ? InkWell(
- onTap: () async {
- await NavigatorUtil.goPage(context, (context) => DurationSettingPage());
- SportDataDetailPageState? state = context.findAncestorStateOfType();
- state?.refresh();
- },
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Row(
- children: [
- Text(
- "${params["daily"]}",
- style: _textStyle,
- ),
- const SizedBox(
- width: 5,
- ),
- arrowRight4()
- ],
- ),
- const SizedBox(
- height: 6,
- ),
- Text(
- "${params["daily_value"]}",
- style: _textStyleValue,
- ),
- ],
- ),
- )
- : Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Text(
- "${params["daily"]}",
- style: _textStyle,
- ),
- const SizedBox(
- height: 6,
- ),
- Text(
- "${params["daily_value"]}",
- style: _textStyleValue,
- ),
- ],
- ),
- ],
- ),
- ),
- if (params.containsKey("times"))
- const Divider(
- height: 1,
- ),
- if (params.containsKey("times"))
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 6),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "运动${params["times"]}次",
- style: Theme.of(context).textTheme.bodyText1,
- ),
- if (params["current"] != "1" && type != 0) Text("${index == 2 ? "周均" : "日均"}${params["avg"]}${params["unit"]}", style: Theme.of(context).textTheme.bodyText1),
- ],
- ),
- ),
- if (params.containsKey("met"))
- SizedBox(
- height: 20,
- ),
- if (params.containsKey("met"))
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 8.0),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "${params["met"]}",
- style: Theme.of(context).textTheme.headline3,
- ),
- Text(
- "${params["met_value"]}",
- style: _textStyle.copyWith(color: metToColor(double.parse(params["met_double"] ?? "0.0"))),
- ),
- ],
- ),
- ),
- if (params.containsKey("met"))
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 8.0),
- child: Text(
- "${params["met_desc"]}",
- style: _textStyle.copyWith(fontSize: 14.0, color: Color(0xff999999), height: 1.5),
- )),
- SizedBox(
- height: 20.0,
- ),
- ],
- ),
- );
- }
- }
- class ChartWidget extends StatefulWidget {
- final List<String> xAxis;
- final List<double> yAxis;
- final List<double> data;
- final int initialPage;
- final int? xAxisType;
- final Color color;
- final double chartHeight;
- final double targetLine;
- final String? unit;
- final bool Function(int index, bool drag) onTap;
- const ChartWidget({Key? key, required this.xAxis, required this.yAxis, required this.data, this.initialPage = 0, this.xAxisType, required this.color, required this.chartHeight, this.targetLine = 0, this.unit, required this.onTap}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _ChartWidget();
- }
- class _ChartWidget extends State<ChartWidget> {
- double _dx = 0;
- List<Rect> area = [];
- onTap(Offset offset, bool drag) {
- var dx = offset.dx;
- for (var i = 0; i < area.length; i++) {
- var rect = area[i];
- if (rect.contains(offset)) {
- if (widget.onTap(i, drag)) {
- dx = 0;
- }
- break;
- }
- }
- if (offset == Offset.zero) {
- widget.onTap(-1, drag);
- }
- setState(() {
- _dx = dx;
- });
- }
- @override
- Widget build(BuildContext context) {
- return GestureDetector(
- onHorizontalDragStart: (details) {
- setState(() {
- _dx = details.localPosition.dx;
- });
- },
- onHorizontalDragUpdate: (details) {
- // print("${details.localPosition.dx} -- ${details.localPosition.dy}");
- onTap(details.localPosition, true);
- },
- onHorizontalDragCancel: () {},
- onHorizontalDragEnd: (details) {
- if (details.velocity.pixelsPerSecond.dx > 0) {
- // 右滑
- PageNotification(page: 1).dispatch(context);
- } else if (details.velocity.pixelsPerSecond.dx < 0) {
- // 左滑
- PageNotification(page: -1).dispatch(context);
- } else {}
- onTap(Offset.zero, false);
- },
- onTapUp: (details) {
- onTap(details.localPosition, false);
- },
- child: Container(
- child: CustomPaint(
- size: Size.fromHeight(widget.chartHeight),
- painter: DataChart(
- callback: (area) {
- this.area = area;
- },
- xAxis: widget.xAxis,
- yAxis: widget.yAxis,
- data: widget.data,
- dx: _dx,
- initialPage: widget.initialPage,
- color: widget.color,
- xAxisType: widget.xAxisType,
- targetLine: widget.targetLine,
- unit: widget.unit),
- ),
- ),
- );
- }
- }
- class TrianglePath extends CustomPainter {
- @override
- void paint(Canvas canvas, Size size) {
- var path = Path();
- path.moveTo(size.width / 2, size.height / 2);
- path.lineTo(0, size.height + 2);
- path.lineTo(size.width, size.height + 2);
- canvas.drawPath(path, Paint()..color = Colors.white);
- }
- @override
- bool shouldRepaint(covariant CustomPainter oldDelegate) {
- return false;
- }
- }
|