123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- import 'dart:math';
- import 'dart:ui';
- import 'dart:ui' as ui;
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_swiper/flutter_swiper.dart';
- import 'package:sport/bean/sport_detail.dart';
- import 'package:sport/bean/sport_index.dart';
- import 'package:sport/services/api/inject_api.dart';
- import 'package:sport/services/api/resp.dart';
- import 'package:sport/widgets/appbar.dart';
- import 'package:sport/widgets/decoration.dart';
- import 'package:sport/widgets/misc.dart';
- import 'package:sport/widgets/progress_bar.dart';
- import 'package:sport/widgets/space.dart';
- class StrengthPage extends StatefulWidget {
- @override
- State<StatefulWidget> createState() => _PageState();
- }
- class _PageState extends State<StrengthPage> with InjectApi {
- ValueNotifier<int> _valueNotifierIndex = ValueNotifier(26);
- SwiperController _swiperController;
- ScrollController _scrollController;
- @override
- void initState() {
- _swiperController = SwiperController();
- _scrollController = ScrollController();
- super.initState();
- }
- @override
- void dispose() {
- _swiperController?.dispose();
- _valueNotifierIndex?.dispose();
- _scrollController?.dispose();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- body: CustomScrollView(
- slivers: <Widget>[
- buildSliverAppBar(context, "运动强度", backgroundColor: Theme.of(context).scaffoldBackgroundColor),
- SliverToBoxAdapter(
- child: Container(
- margin: const EdgeInsets.symmetric(horizontal: 12.0),
- padding: const EdgeInsets.only(top:20.0,bottom: 10.0,left: 16.0,right: 16.0),
- decoration: circular(),
- child: FutureBuilder(
- future: createFutureType(0),
- builder: (BuildContext context, AsyncSnapshot<RespData<SportDetailSimple>> snapshot) => Column(
- children: <Widget>[
- Center(
- child: CustomPaint(
- painter: _Bg(),
- child: Container(
- width: 200,
- height: 200,
- child: Center(
- child: CircularProgressBar(
- percent: strengthToValue(snapshot?.data?.data?.sum?.consume ?? 0) / 12.0,
- radius: 69.0,
- center: Column(
- children: <Widget>[
- SizedBox(
- height: 16.0,
- ),
- Text(
- "${strengthToValue(snapshot?.data?.data?.sum?.consume ?? 0).toStringAsFixed(1)}",
- style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 40.0, fontFamily: "DIN"),
- strutStyle: fixedLine,
- ),SizedBox(
- height: 5.0,
- ),
- Text(
- "卡/分",
- style: Theme.of(context).textTheme.subtitle2,
- strutStyle: fixedLine,
- )
- ],
- mainAxisSize: MainAxisSize.min,
- ),
- ),
- ),
- ),
- ),
- ),
- SizedBox(
- width: 15.0,
- ),
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 6.0),
- child: Row(
- children: <Widget>[
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: <Widget>[
- Container(
- width: 8,
- height: 8,
- decoration: BoxDecoration(shape: BoxShape.circle, color: const Color(0xffFFE600)),
- ),
- const SizedBox(
- width: 5.0,
- ),
- Text(
- "低强度",
- style: Theme.of(context).textTheme.bodyText1,
- strutStyle: fixedLine,
- ),
- const SizedBox(
- width: 12.0,
- ),
- Container(
- width: 8,
- height: 8,
- decoration: BoxDecoration(shape: BoxShape.circle, color: const Color(0xffFFAA00)),
- ),
- const SizedBox(
- width: 5.0,
- ),
- Text(
- "强度适中",
- style: Theme.of(context).textTheme.bodyText1,
- strutStyle: fixedLine,
- ),
- const SizedBox(
- width: 12.0,
- ),
- Container(
- width: 8,
- height: 8,
- decoration: BoxDecoration(shape: BoxShape.circle, color: const Color(0xffFF7323)),
- ),
- const SizedBox(
- width: 5.0,
- ),
- Text(
- "高强度",
- style: Theme.of(context).textTheme.bodyText1,
- strutStyle: fixedLine,
- )
- ],
- ),
- Text(
- "单位:卡/分钟",
- style: Theme.of(context).textTheme.bodyText1,
- strutStyle: fixedLine,
- )
- ],
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- ),
- ),
- Divider(),
- Padding(
- padding: const EdgeInsets.symmetric(vertical: 16.0),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: <Widget>[
- Column(
- children: <Widget>[
- Text(
- "${snapshot?.data?.data?.sum?.consume ?? 0}",
- style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0, fontFamily: "DIN"),
- ),
- Space(height: 4.0,),
- Text(
- "运动消耗 (卡)",
- style: Theme.of(context).textTheme.bodyText1,
- ),
- ],
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.center,
- ),
- Column(
- children: <Widget>[
- Text(
- "${snapshot?.data?.data?.sum?.durationMinute ?? 0}",
- style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0, fontFamily: "DIN"),
- ),
- Space(height: 4.0,),
- Text(
- "运动时长 (分钟)",
- style: Theme.of(context).textTheme.bodyText1,
- ),
- ],
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.center,
- )
- ],
- ),
- )
- ],
- ),
- ),
- ),
- ),
- SliverToBoxAdapter(
- child: Container(
- height: 140.0,
- margin: const EdgeInsets.only(top: 12.0),
- child: CustomPaint(
- painter: _ListBg(),
- child: FutureBuilder(
- future: createFutureType(1).asStream().map((event) => convert(event)).last,
- builder: (BuildContext context, AsyncSnapshot<List<_DataItem>> snapshot) {
- var list = snapshot?.data;
- if (list == null || list?.isEmpty == true) return Container();
- return ValueListenableBuilder(
- valueListenable: _valueNotifierIndex,
- builder: (BuildContext context, int value, Widget child) {
- return SingleChildScrollView(
- reverse: true,
- child: Padding(
- padding: EdgeInsets.only(top: 10.0),
- child: Row(
- children: list?.map((e) {
- var index = list.indexOf(e);
- return GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () {
- // _valueNotifierIndex.value = index;
- // var page = max(0, strengthArr.indexOf(strengthToLabel(list[index].data)));
- // _swiperController?.move(page);
- },
- child: Column(
- mainAxisAlignment: MainAxisAlignment.end,
- children: <Widget>[
- CustomPaint(
- child: SizedBox(
- width: MediaQuery.of(context).size.width / 7.0,
- height: 100.0,
- ),
- painter: _Dot(list, index),
- ),
- Container(
- height: 30.0,
- decoration: index == value
- ? BoxDecoration(
- border: Border(bottom: BorderSide(color: Theme.of(context).accentColor, width: 3.0)),
- )
- : null,
- child: Center(
- child: Text(
- "${e.date}",
- style: e.date == "今日"
- ? Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor)
- : Theme.of(context).textTheme.subtitle1,
- ))),
- ],
- ));
- })?.toList(),
- )),
- scrollDirection: Axis.horizontal,
- );
- },
- );
- },
- ),
- )),
- ),
- SliverToBoxAdapter(
- child: Padding(
- padding: const EdgeInsets.only(top: 12.0),
- child: Container(
- width: double.infinity,
- height: 145.0,
- child: Swiper(
- controller: _swiperController,
- loop: false,
- itemBuilder: (BuildContext context, int index) {
- return Container(
- margin: const EdgeInsets.only(left:12.0,right: 12.0),
- // decoration: circular(),
- // padding: const EdgeInsets.all(12.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text(
- index == 0 ? "低强度" : index == 1 ? "强度适中" : "高强度",
- style: Theme.of(context).textTheme.headline1,
- ),
- SizedBox(
- height: 5.0,
- ),
- Text(
- index == 0
- ? "感觉舒适,呼吸有节奏,能自由进行交谈。该级别适用于恢复和基础心血功能训练,可以提高心脏的泵血能力和肌肉使用氧气的能力"
- : index == 1 ? "该级别训练强度适中,较难以进行交谈,呼吸加重。可以适当增强心肺功能,获得更强耐力" : "感觉难受,且呼吸急促,难以长时间维持该强度训练。可以增强力量和肌肉耐力,提高厌氧能力和乳酸阈值",
- style: Theme.of(context).textTheme.bodyText2,
- strutStyle: StrutStyle(height: 1.5,forceStrutHeight: true),
- ),
- ],
- ),
- );
- },
- itemCount: 3),
- ),
- ),
- )
- ],
- ),
- );
- }
- Future<RespData<SportDetailSimple>> createFutureType(int type) async {
- Future<RespData<SportDetailSimple>> data;
- var time = DateTime.now();
- switch (type) {
- case 0:
- data = api.getSportRecordListOneDay('${time.year}-${'${time.month}'.padLeft(2, '0')}-${'${time.day}'.padLeft(2, '0')}');
- break;
- case 1:
- DateTime end = DateTime(time.year, time.month, time.day + 3);
- DateTime start = DateTime(time.year, time.month, end.day - 30);
- 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;
- }
- return data;
- }
- List<_DataItem> convert(RespData<SportDetailSimple> data) {
- List<_DataItem> items = [];
- List<RecordsToday> records = data?.data?.records ?? [];
- var time = DateTime.now();
- DateTime end = DateTime(time.year, time.month, time.day + 3);
- for (var i = 0; i < 30; i++) {
- var day = DateTime(end.year, end.month, end.day - i);
- String tag = '${day.year}-${'${day.month}'.padLeft(2, '0')}-${'${day.day}'.padLeft(2, '0')}';
- String date = (time.month == day.month && time.day == day.day) ? '今日' : (day.day == 1) ? '${day.month}.${day.day}' : '${day.day}';
- int total = 0;
- for (var record in records) {
- if (tag == record.createdAt.split(" ")[0]) {
- total += record.consume;
- }
- }
- items.add(_DataItem(tag, date, total));
- if (i == 3) {
- _swiperController?.move(max(0, strengthArr.indexOf(strengthToLabel(total))));
- }
- }
- return items.reversed.toList();
- }
- }
- class _DataItem {
- final String tag;
- final String date;
- final int data;
- _DataItem(this.tag, this.date, this.data);
- }
- class _Bg extends CustomPainter {
- final Paint _paint = Paint()
- ..color = const Color(0xffDCDCDC)
- ..strokeWidth = 0.5
- ..style = PaintingStyle.stroke
- ..isAntiAlias = true;
- final ParagraphStyle _valueStyle = ParagraphStyle(
- textAlign: TextAlign.center,
- fontSize: 12,
- );
- @override
- void paint(Canvas canvas, Size size) {
- final double cx = size.width / 2;
- final double cy = size.height / 2;
- final double radius = (size.width - 40) / 2;
- canvas.drawCircle(Offset(cx, cy), radius, _paint);
- canvas.save();
- for (int i = 0; i < 4; i++) {
- canvas.translate(cx, cy);
- canvas.rotate(pi * 2 / 3);
- canvas.translate(-cx, -cy);
- canvas.drawLine(Offset(cx, cy - radius), Offset(cx, cy - radius + 4), _paint);
- }
- canvas.restore();
- ParagraphBuilder pb = ParagraphBuilder(_valueStyle)
- ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
- ..addText("0");
- ParagraphConstraints constraints = ParagraphConstraints(width: 20.0);
- Paragraph paragraph = pb.build()..layout(constraints);
- paragraph.computeLineMetrics().forEach((element) {
- canvas.drawParagraph(paragraph, Offset(cx - element.width - 4, 2));
- });
- pb = ParagraphBuilder(_valueStyle)
- ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
- ..addText("4.0");
- constraints = ParagraphConstraints(width: 20.0);
- paragraph = pb.build()..layout(constraints);
- paragraph.computeLineMetrics().forEach((element) {
- canvas.drawParagraph(paragraph, Offset(size.width - 25, 140));
- });
- pb = ParagraphBuilder(_valueStyle)
- ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
- ..addText("8.0");
- constraints = ParagraphConstraints(width: 20.0);
- paragraph = pb.build()..layout(constraints);
- paragraph.computeLineMetrics().forEach((element) {
- canvas.drawParagraph(paragraph, Offset(6, 140.0));
- });
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) => false;
- }
- class _ListBg extends CustomPainter {
- final Paint _paint = Paint()
- ..color = const Color(0xffDCDCDC)
- ..strokeWidth = 0.5
- ..isAntiAlias = true;
- final ParagraphStyle _valueStyle = ParagraphStyle(
- textAlign: TextAlign.right,
- fontSize: 12,
- );
- @override
- void paint(Canvas canvas, Size size) {
- final double height = size.height - 40;
- final int split = 3;
- final double splitHeight = height / split;
- double _startY = 10;
- for (var i = 0; i < split; i++) {
- canvas.drawLine(Offset(0, _startY), Offset(size.width, _startY), _paint);
- _startY += splitHeight;
- ParagraphBuilder pb = ParagraphBuilder(_valueStyle)
- ..pushStyle(ui.TextStyle(color: Color(0xff999999)))
- ..addText("${(8.0 - (4.0 * i)).toStringAsFixed(1)}");
- ParagraphConstraints constraints = ParagraphConstraints(width: size.width - 10);
- Paragraph paragraph = pb.build()..layout(constraints);
- paragraph.computeLineMetrics().forEach((element) {
- canvas.drawParagraph(paragraph, Offset(0, splitHeight * i + element.baseline / 2 + 14));
- });
- }
- canvas.drawLine(Offset(0, size.height - 30), Offset(size.width, size.height - 30), _paint);
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) => false;
- }
- class _Dot extends CustomPainter {
- final List<_DataItem> items;
- final int index;
- static const Color _color = const Color(0xffFFC400);
- final Paint _paint = Paint()
- ..maskFilter = MaskFilter.blur(BlurStyle.solid, 3)
- ..isAntiAlias = true;
- final Paint _line = Paint()
- ..color = _color
- ..strokeWidth = 0.5
- ..isAntiAlias = true;
- final Paint _polygonal = Paint()
- ..color = _color
- ..strokeWidth = 1
- ..isAntiAlias = true;
- _Dot(this.items, this.index);
- @override
- void paint(Canvas canvas, Size size) {
- if (index > items.length - 4) return;
- double _height = size.height;
- final double cx = size.width / 2;
- final double progress = min(1.0, items[index].data / 60.0 / 12.0);
- final double cy = _height * (1.0 - progress);
- // print("$progress $cy ${items[index].data}");
- final double radius = size.width * 0.15 / 2;
- _paint.color = _color;
- canvas.drawCircle(Offset(cx, cy), radius, _paint);
- if (index == items.length - 4) {
- double _h = cy;
- while (_h < _height) {
- canvas.drawLine(Offset(cx, _h), Offset(cx, min(_height, _h + 3)), _line);
- _h += 5;
- }
- }
- for (var i = index - 1; i < index + 1; i += 2) {
- if (i < 0) continue;
- if (i >= items.length) continue;
- double _cx = (cx - size.width) * (index - i);
- double _cy = size.height * (1.0 - min(1.0, items[i].data / 60.0 / 12.0));
- canvas.drawLine(Offset(_cx, _cy), Offset(cx, cy), _polygonal);
- }
- }
- @override
- bool shouldRepaint(CustomPainter oldDelegate) => false;
- }
|