1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003 |
- import 'dart:convert';
- import 'dart:math' as math;
- import 'dart:ui' as ui;
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:provider/provider.dart';
- import 'package:sport/bean/jog/detail.dart';
- import 'package:sport/pages/run/location.dart';
- import 'package:sport/pages/run/map.dart';
- import 'package:sport/pages/run/map_replay_page.dart';
- import 'package:sport/pages/run/run_data.dart';
- import 'package:sport/pages/run/run_page.dart';
- import 'package:sport/pages/run/run_share.dart';
- import 'package:sport/pages/run/setting_page.dart';
- import 'package:sport/pages/run/statistics.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/app_lifecycle_state.dart';
- import 'package:sport/utils/DateFormat.dart';
- import 'package:sport/utils/sport_utils.dart';
- import 'package:sport/widgets/decoration.dart';
- import 'package:sport/widgets/image.dart';
- import 'package:sport/widgets/linear_progress_indicator.dart' as progress;
- import 'package:sport/widgets/loading.dart';
- import 'package:sport/widgets/misc.dart';
- import 'package:sport/widgets/popmenu_bg.dart';
- import 'chart.dart';
- class RunDetailPage extends StatefulWidget {
- final int id;
- final bool post;
- final bool share;
- final JogDetail? jogDetail;
- const RunDetailPage({Key? key, this.id = 0, this.post = false, this.share = false, this.jogDetail}) : super(key: key);
- @override
- State<StatefulWidget> createState() => _PageState();
- }
- class _PageState extends LifecycleState<RunDetailPage> with RunSetting, InjectApi, TickerProviderStateMixin {
- final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
- final GlobalKey<MapState> mapKey = GlobalKey();
- bool _loading = true;
- JogDetail? _jogDetail;
- double _opacity = 0;
- double _expandedHeight = 0;
- int _brightness = 0;
- ValueNotifier<MapNotification> _notifierMapNotification = ValueNotifier(MapNotification(0, 0, 0));
- @override
- bool autoLoadSetting() {
- return false;
- }
- @override
- void initState() {
- super.initState();
- refreshSetting();
- _showData();
- }
- _showData() async {
- List<Location> points = [];
- _jogDetail = widget.jogDetail;
- if (_jogDetail == null) {
- var result = await api.jogShowRecord(widget.id);
- if (result.data != null) {
- _jogDetail = result.data!;
- }
- }
- if (_jogDetail != null) {
- JogDetail data = _jogDetail!;
- totalDistance = data.distance?.toDouble() ?? 0;
- totalTime = data.duration ?? 0;
- totalConsume = data.consume ?? 0;
- totalStep = data.step ?? 0;
- met = data.met ?? 0;
- timeStart = DateTime.parse(data.begin ?? "");
- timeEnd = DateTime.parse(data.end ?? "");
- points = data.points ?? [];
- if (data.stepInfo != null) stepRateList = json.decode(data.stepInfo ?? "[]").cast<int>();
- if (data.altitudeInfo != null) altitudeList = json.decode(data.altitudeInfo ?? "[]").cast<double>();
- var runData = RunData(0);
- if (points.length > 1) {
- runData.calPoints(points);
- }
- _pointsKm.addAll(runData.pointsKm);
- _pointsKmTime.addAll(runData.pointsKmTime);
- totalAltitude = runData.totalAltitude;
- marathonHalf = runData.marathonHalf;
- marathonAll = runData.marathonAll;
- }
- if (stepRateList.isNotEmpty == true) stepMax = stepRateList.reduce((value, element) => value > element ? value : element);
- stepAvg = totalStep ~/ (totalTime / 60);
- if (_pointsKmTime.isNotEmpty == true) {
- kmTime = _pointsKmTime.values.fold(0, (value, element) => value + element.inSeconds);
- avg = kmTime ~/ _pointsKmTime.length;
- min = _pointsKmTime.values.reduce((value, element) => value.inSeconds < element.inSeconds ? value : element).inSeconds;
- max = _pointsKmTime.values.reduce((value, element) => value.inSeconds > element.inSeconds ? value : element).inSeconds;
- // print("1111111111111111 $_pointsKmTime 22222222222222222 avg $avg $min");
- }
- setState(() {
- _loading = false;
- this.points = points;
- });
- }
- final Map<int, Location> _pointsKm = {};
- final Map<int, Duration> _pointsKmTime = {};
- List<Location> points = <Location>[];
- List<int> stepRateList = [];
- List<double> altitudeList = [];
- double totalDistance = 0;
- double totalAltitude = 0;
- int totalTime = 0;
- int totalStep = 0;
- int stepAvg = 0;
- int stepMax = 0;
- int totalConsume = 0;
- double met = 0.0;
- int kmTime = 0;
- int avg = 0;
- double avgSpeed = 0.0;
- int min = 0;
- int max = 1;
- DateTime? timeStart;
- DateTime? timeEnd;
- int marathonHalf = 0;
- int marathonAll = 0;
- @override
- void dispose() {
- super.dispose();
- }
- _share() async {
- var path = await mapKey.currentState?.takeSnapshot();
- if (path == null) return;
- if (_jogDetail == null) return;
- NavigatorUtil.goPage(
- context,
- (context) => RunShare(
- detail: _jogDetail!,
- map: path,
- ));
- }
- @override
- Widget build(BuildContext context) {
- var maxKm = _pointsKmTime.isNotEmpty ? _pointsKmTime.keys.reduce((value, element) => value > element ? value : element) : 0;
- var map = points.isNotEmpty == true
- ? NotificationListener<MapNotification>(
- onNotification: (map) {
- _notifierMapNotification.value = map;
- return false;
- },
- child: MapWidget(
- key: mapKey,
- distance: totalDistance,
- runMapType: runMapType,
- runMapKm: runMapKm,
- points: points,
- pointsKm: _pointsKm,
- ),
- )
- : Container();
- const _padding = 16.0;
- var header = Consumer<UserModel>(
- builder: (_, model, __) => Container(
- margin: const EdgeInsets.fromLTRB(12.0, 30, 12.0, 0),
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)),
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: _padding),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "趣动户外跑",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- const SizedBox(
- height: 45.0,
- ),
- Row(
- textBaseline: TextBaseline.alphabetic,
- mainAxisSize: MainAxisSize.min,
- children: [
- ValueListenableBuilder<MapNotification>(
- valueListenable: _notifierMapNotification,
- builder: (_, data, __) {
- return Text(
- "${formatNum(data.distance / 1000, 2)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 45.0, fontFamily: "DIN"),
- strutStyle: fixedLine,
- );
- },
- ),
- Text(" 公里", style: Theme.of(context).textTheme.subtitle1!),
- marathonAll > 0
- ? Container(
- decoration: BoxDecoration(borderRadius: BorderRadius.circular(3), color: COLOR_RUN_FAST),
- margin: const EdgeInsets.only(left: 10),
- padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
- child: Center(
- child: Text(
- "全马",
- style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Colors.white),
- )),
- )
- : marathonHalf > 0
- ? Container(
- decoration: BoxDecoration(borderRadius: BorderRadius.circular(3), color: COLOR_RUN_SLOW),
- margin: const EdgeInsets.only(left: 10),
- padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
- child: Center(
- child: Text(
- "半马",
- style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Colors.white),
- )),
- )
- : Container()
- ],
- ),
- ],
- ),
- Container(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.end,
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- Container(
- decoration: BoxDecoration(color: Colors.grey, shape: BoxShape.circle),
- child: CircleAvatar(
- backgroundColor: Colors.black26,
- backgroundImage: userAvatarProvider(model.user.avatar),
- radius: 30.0,
- ),
- transform: Matrix4.translationValues(0, -24, 0),
- ),
- Text(model.user.name, style: Theme.of(context).textTheme.headline1!),
- const SizedBox(
- height: 12.0,
- ),
- Text(
- "${SportUtils.toDateTime(timeStart)} ${SportUtils.toEndTime(timeStart)} ~ ${SportUtils.toEndTime(timeEnd)}",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ),
- ],
- ),
- ),
- ],
- ),
- ),
- );
- double infoHeight = 230 * MediaQuery.of(context).textScaleFactor;
- final double _iconSize = 40.0;
- List<Widget> _kmList = [];
- if (_pointsKmTime.isNotEmpty) {
- List<int> keys = _pointsKmTime.keys.toList();
- int second = 0;
- int totalSecond = 0;
- for (var i = 0; i < keys.length; i++) {
- int e = keys[i];
- second += _pointsKmTime[e]?.inSeconds ?? 0;
- // print("11111111111111 --- $e $second");
- totalSecond += _pointsKmTime[e]?.inSeconds ?? 0;
- _kmList.add(Padding(
- padding: const EdgeInsets.symmetric(vertical: 1.0),
- child: Row(
- mainAxisSize: MainAxisSize.max,
- children: [
- ConstrainedBox(
- constraints: BoxConstraints(minWidth: 16),
- child: Padding(
- padding: const EdgeInsets.only(top: 1.0),
- child: Text(
- "${e.toString().padLeft(2, "0")}",
- style: Theme.of(context).textTheme.subtitle1!.copyWith(fontFamily: "DIN"),
- ),
- ),
- ),
- Expanded(
- child: Container(
- height: 12.0,
- color: Color(0xfff1f1f1),
- margin: const EdgeInsets.symmetric(horizontal: 10.0),
- child: CustomPaint(
- painter: progress.LinearProgressIndicator((_pointsKmTime[e]?.inSeconds ?? 0) == min ? [Color(0xffFFC57A), Color(0xffFF5B1D)] : [Color(0xffFFF4CE), Color(0xffFFC400)], (_pointsKmTime[e]?.inSeconds ?? 0) / max * 0.9),
- ),
- ),
- ),
- ConstrainedBox(
- constraints: BoxConstraints(minWidth: 50),
- child: Text(
- "${SportUtils.pace(SportUtils.calPace((_pointsKmTime[e]?.inSeconds ?? 0), 1))}",
- style: Theme.of(context).textTheme.subtitle1!,
- ),
- ),
- ],
- ),
- ));
- if ((i + 1) % 5 == 0) {
- String tips = "近5公里用时 ${DateFormat.toTime(second)}";
- if (i > 5) {
- tips += " ${i + 1}公里用时 ${DateFormat.toTime(totalSecond)}";
- }
- _kmList.add(Padding(
- padding: const EdgeInsets.fromLTRB(26, 5, 0, 15),
- child: Container(
- height: 18.0,
- child: Text(
- tips,
- maxLines: 1,
- style: Theme.of(context).textTheme.bodyText1?.copyWith(fontSize: 11),
- )),
- ));
- second = 0;
- }
- }
- }
- double mapHeight = MediaQuery.of(context).size.height - MediaQuery.of(context).padding.top - 24.0 - infoHeight;
- return AnnotatedRegion<SystemUiOverlayStyle>(
- value: SystemUiOverlayStyle.light,
- child: Scaffold(
- key: _scaffoldKey,
- body: LoadingWidget(
- loading: _loading,
- willPop: false,
- child: CustomScrollView(
- shrinkWrap: widget.share,
- physics: ClampingScrollPhysics(),
- slivers: [
- SliverAppBar(
- expandedHeight: widget.share? 300:mapHeight,
- pinned: true,
- forceElevated: true,
- automaticallyImplyLeading: false,
- title: widget.share
- ? null
- : Row(
- children: [
- GestureDetector(
- onTap: () {
- Navigator.maybePop(context);
- },
- child: Container(width: _iconSize, height: _iconSize, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(50.0)), child: arrowBack()),
- ),
- Expanded(
- child: Container(
- height: 1,
- )),
- PopupMenuTheme(
- data: PopupMenuThemeData(shape: PopmenuShape(borderRadius: BorderRadius.all(Radius.circular(10.0)))),
- child: PopupMenuButton(
- offset: Offset(-10, kToolbarHeight / 2 + 25),
- onSelected: (val) {
- NavigatorUtil.goPage(
- context,
- (context) => MapReplayPage(
- distance: totalDistance,
- duration: totalTime,
- kcal: totalConsume,
- runMapType: runMapType,
- runMapKm: runMapKm,
- points: points,
- pointsKm: _pointsKm,
- begin: timeStart?.millisecondsSinceEpoch ?? 0,
- showType: val == "1" ? 1 : 0,
- ));
- },
- itemBuilder: (context) {
- return divideMenus([
- PopupMenuItem(
- value: "1",
- child: Center(
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 12.0),
- child: Text(
- "生成视频(20秒)",
- style: Theme.of(context).textTheme.subtitle1!,
- ),
- )),
- ),
- menuDivider(),
- PopupMenuItem(
- value: "0",
- child: Center(
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 12.0),
- child: Text(
- "生成视频(${totalDistance ~/ 1000 * 4 + 4}秒)",
- style: Theme.of(context).textTheme.subtitle1!,
- ),
- )),
- ),
- ]);
- },
- child: Container(width: _iconSize, height: _iconSize, margin: EdgeInsets.only(right: 12.0), decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(50.0)), child: Image.asset("lib/assets/img/topbar_icon_trajectory.png"))),
- ),
- GestureDetector(
- onTap: () {
- _share();
- },
- child: Container(width: _iconSize, height: _iconSize, margin: EdgeInsets.only(right: 12.0), decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(50.0)), child: Image.asset("lib/assets/img/bbs_icon_share.png")),
- ),
- GestureDetector(
- onTap: () async {
- await NavigatorUtil.goPage(context, (context) => SettingPage());
- refreshSetting();
- },
- child: Container(width: _iconSize, height: _iconSize, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(50.0)), child: Image.asset("lib/assets/img/setgoals_icon_set.png")),
- ),
- ],
- ),
- flexibleSpace: FlexibleSpaceBar(
- collapseMode: CollapseMode.pin,
- background: Container(
- color: Theme.of(context).scaffoldBackgroundColor,
- child: Stack(
- children: [Positioned(top: 0, left: 0, right: 0, bottom: 50.0, child: map), Align(alignment: Alignment.bottomCenter, child: header)],
- ),
- )),
- ),
- SliverToBoxAdapter(
- child: Container(
- height: infoHeight,
- margin: const EdgeInsets.fromLTRB(12.0, 0, 12.0, 12.0),
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.only(bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)),
- ),
- child: Column(
- children: [
- Container(
- margin: const EdgeInsets.symmetric(vertical: 20.0),
- height: 20,
- child: Stack(
- fit: StackFit.expand,
- children: [
- Center(
- child: Container(
- height: 3,
- width: double.infinity,
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.centerLeft,
- end: Alignment.centerRight,
- colors: [
- COLOR_RUN_FAST,
- COLOR_RUN_MIDDLE,
- COLOR_RUN_SLOW,
- ],
- ),
- ),
- ),
- ),
- if (min != 0)
- Positioned(
- top: 0,
- bottom: 0,
- left: _padding,
- child: Container(
- color: Colors.white,
- height: double.infinity,
- padding: const EdgeInsets.symmetric(horizontal: 4.0),
- child: Center(
- child: Text(
- // "${SportUtils.toEndTime(timeStart)}开始",
- "快 ${SportUtils.pace(SportUtils.calPace((min), 1))}",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- ),
- ),
- ),
- if (max != 0)
- Positioned(
- top: 0,
- bottom: 0,
- right: _padding,
- child: Container(
- color: Colors.white,
- height: double.infinity,
- padding: const EdgeInsets.symmetric(horizontal: 4.0),
- child: Center(
- child: Text(
- // "${SportUtils.toEndTime(timeEnd)}结束",
- "慢 ${SportUtils.pace(SportUtils.calPace((max), 1))}",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- Padding(
- padding: const EdgeInsets.only(left: 20.0),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: [
- Expanded(
- flex: 4,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "${DateFormat.toTime(totalTime)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- "时长",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ),
- Expanded(
- flex: 3,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "${met.toStringAsFixed(1)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- "MET值",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ),
- Expanded(
- flex: 4,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- children: [
- Text(
- "${SportUtils.pace11(SportUtils.calPace(totalTime, totalDistance / 1000))}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- "′",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0),
- ),
- Text(
- "${SportUtils.pace12(SportUtils.calPace(totalTime, totalDistance / 1000))}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- "″",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0),
- ),
- ],
- ),
- Text(
- "配速(每公里用时)",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ),
- ],
- ),
- ),
- const SizedBox(
- height: 20.0,
- ),
- Padding(
- padding: const EdgeInsets.only(left: 20.0),
- child: Row(
- children: [
- Expanded(
- flex: 4,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "$totalConsume",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- "消耗(大卡)",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ),
- Expanded(
- flex: 3,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- totalStep == 0 ? "0" : "${totalStep ~/ (totalTime / 60)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- "步频(步/分钟)",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- ],
- ),
- ),
- Expanded(
- flex: 4,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "${totalTime == 0 ? 0 : (totalDistance / 1000.0 / totalTime * 3600).toStringAsFixed(1)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- "时速(公里/小时)",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ),
- ],
- ),
- ),
- const SizedBox(
- height: 20.0,
- ),
- Center(
- child: Container(
- decoration: BoxDecoration(color: Theme.of(context).backgroundColor, borderRadius: BorderRadius.circular(radius)),
- margin: EdgeInsets.symmetric(vertical: 10.0),
- padding: EdgeInsets.fromLTRB(30.0, 2, 30.0, 0),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Image.asset(
- "lib/assets/img/run_icon_today.png",
- color: Color(0xffC5C5C5),
- ),
- SizedBox(
- width: 12.0,
- ),
- Row(
- children: [
- Text(
- "步幅 ",
- style: Theme.of(context).textTheme.subtitle2!,
- ),
- Text(
- totalStep == 0 ? "0" : "${(totalDistance / totalStep * 100).toInt()}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- " 厘米",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- ],
- ),
- Container(
- width: 1,
- height: 20.0,
- color: Color(0xffdcdcdc),
- margin: EdgeInsets.symmetric(horizontal: 20.0),
- ),
- Row(
- children: [
- Text(
- "步数 ",
- style: Theme.of(context).textTheme.subtitle2!,
- ),
- Text(
- "${totalStep}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 22.0, fontFamily: "DIN"),
- ),
- Text(
- " 步",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- ],
- ),
- ],
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- if (_pointsKmTime.isNotEmpty == true)
- SliverToBoxAdapter(
- child: BoxWidget(
- title: "配速",
- icon: "run_icon_tile1.png",
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (marathonHalf > 0)
- Align(
- alignment: Alignment.centerLeft,
- child: Padding(
- padding: const EdgeInsets.only(bottom: 16.0, top: 8.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "${DateFormat.toTime(marathonHalf)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 25.0, fontFamily: "DIN"),
- ),
- Text(
- "半马总用时",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- mainAxisSize: MainAxisSize.min,
- ),
- ),
- ),
- if (marathonAll > 0)
- Align(
- alignment: Alignment.centerLeft,
- child: Padding(
- padding: const EdgeInsets.only(bottom: 16.0, top: 8.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "${DateFormat.toTime(marathonHalf)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 25.0, fontFamily: "DIN"),
- ),
- Text(
- "全马总用时",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- mainAxisSize: MainAxisSize.min,
- ),
- ),
- ),
- CustomPaint(
- foregroundPainter: AvgPainter(context, avg * 1.0 / max * 0.9, avg, _pointsKmTime.length, MediaQuery.of(context).textScaleFactor),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisSize: MainAxisSize.max,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "公里",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- Expanded(
- child: Container(height: 18,),
- ),
- Text(
- "配速",
- style: Theme.of(context).textTheme.bodyText1!,
- ),
- const SizedBox(
- width: 20.0,
- ),
- ],
- ),
- const SizedBox(
- height: 12.0,
- ),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: _kmList,
- ),
- // if (_pointsKmTime.length > 0 && totalTime > 0)
- // Row(
- // mainAxisAlignment: MainAxisAlignment.spaceAround,
- // children: [
- // RichText(
- // text: TextSpan(children: [TextSpan(text: "$maxKm公里累计用时 ", style: Theme.of(context).textTheme.bodyText1!), TextSpan(text: "${DateFormat.toTime(kmTime)}", style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Theme.of(context).accentColor))]),
- // ),
- // if (totalTime - kmTime > 0)
- // RichText(
- // text: TextSpan(children: [TextSpan(text: "最后不足1公里用时 ", style: Theme.of(context).textTheme.bodyText1!), TextSpan(text: "${DateFormat.toTime((totalTime) - kmTime)}", style: Theme.of(context).textTheme.bodyText1!.copyWith(color: Theme.of(context).accentColor))]),
- // ),
- // ],
- // ),
- if (_pointsKmTime.length > 0 && totalTime > 0 && (totalTime - kmTime > 20))
- Padding(
- padding: const EdgeInsets.fromLTRB(26, 5, 0, 15),
- child: Container(
- height: 18.0,
- child: Text(
- "最后不足1公里用时 ${DateFormat.toTime((totalTime) - kmTime)}",
- style: Theme.of(context).textTheme.bodyText1?.copyWith(fontSize: 11),
- )),
- ),
- const SizedBox(
- height: 16.0,
- ),
- ],
- ),
- ),
- ],
- )),
- ),
- SliverToBoxAdapter(
- child: BoxWidget(
- title: "步频",
- icon: "run_icon_tile2.png",
- child: Column(
- children: [
- Row(
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "$stepAvg",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 25.0, fontFamily: "DIN"),
- ),
- Text(
- "平均步频(步/分钟)",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- const SizedBox(
- width: 37.0,
- ),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "$stepMax",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 25.0, fontFamily: "DIN"),
- ),
- Text(
- "最高步频(步/分钟)",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ],
- ),
- RunChart(
- values: stepRateList.map((e) => e.toDouble()).toList(),
- gradient: false,
- ),
- ],
- )),
- ),
- SliverToBoxAdapter(
- child: BoxWidget(
- title: "运动海拔",
- icon: "run_icon_tile3.png",
- child: Column(
- children: [
- Row(
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- "${(totalAltitude).toStringAsFixed(1)}",
- style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 25.0, fontFamily: "DIN"),
- ),
- Text(
- "累计爬升(米)",
- style: Theme.of(context).textTheme.bodyText1!,
- )
- ],
- ),
- ],
- ),
- RunChart(
- values: altitudeList.map((e) => e.toDouble()).toList(),
- // values: [-10,20,0,0,1,2,3,4,5,6,8,9,8,10,-6,-5,-20, 20, 30],
- gradient: true,
- ),
- ],
- )),
- ),
- SliverToBoxAdapter(
- child: Container(
- height: 50,
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
- }
- class AvgPainter extends CustomPainter {
- final BuildContext context;
- final int pace;
- final double progress;
- final int length;
- final double dpi;
- AvgPainter(this.context, this.progress, this.pace, this.length, this.dpi);
- final Paint _paint = Paint()
- ..color = Color(0xff999999)
- ..strokeWidth = 0.5
- ..isAntiAlias = true;
- final ui.ParagraphStyle _valueStyle = ui.ParagraphStyle(
- textAlign: TextAlign.left,
- fontSize: 10,
- );
- @override
- void paint(Canvas canvas, Size size) {
- if (length == 0) return;
- double dpr = ui.window.devicePixelRatio;
- double p = math.min(.9, progress);
- double startX = 26 + (size.width - 86) * p;
- double startY = 34;
- double endY = size.height - 8 * dpr;
- var dashWidth = 3;
- var dashSpace = 3;
- final space = (dashSpace + dashWidth);
- double height = (endY - startY);
- int split = length ~/ 5;
- double lineHeight = (height - split * 38 / 2.0 * dpr) / length;
- double lineSplit = lineHeight * 5;
- // print("111111111111111 height: $height, sss: $split lineHeight: $lineHeight");
- // canvas.drawRect(Rect.fromLTWH(26, startY + lineHeight * 5, 100, 38), _paint);
- // canvas.clipRect(Rect.fromLTWH(26, startY + lineHeight * 5, 1000, 38));
- double ss = 0;
- while (startY < endY) {
- if (ss > lineSplit - 5) {
- startY += 42;
- ss = 0;
- }
- canvas.drawLine(Offset(startX, startY), Offset(startX, math.min(startY + dashWidth, endY)), _paint);
- startY += space;
- ss += space;
- }
- var text = TextPainter(
- textAlign: TextAlign.center,
- text: TextSpan(children: <InlineSpan>[
- TextSpan(text: "平均配速", style: Theme.of(context).textTheme.bodyText1!),
- TextSpan(text: "\n${SportUtils.pace(SportUtils.calPace((this.pace), 1))}", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 10)),
- ]),
- textDirection: TextDirection.ltr)
- ..layout(maxWidth: size.width);
- text.paint(canvas, Offset(startX - text.minIntrinsicWidth / 2, 0));
- }
- @override
- bool shouldRepaint(covariant CustomPainter oldDelegate) {
- return false;
- }
- }
|