import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:provider/provider.dart'; import 'package:sport/pages/run/run_detail_page.dart'; import 'package:sport/pages/run/run_list.dart'; import 'package:sport/pages/run/run_page.dart'; import 'package:sport/pages/run/run_start.dart'; import 'package:sport/pages/run/run_target_page.dart'; import 'package:sport/pages/run/setting_page.dart'; import 'package:sport/provider/lib/provider_widget.dart'; import 'package:sport/provider/lib/view_state_lifecycle.dart'; import 'package:sport/provider/sport_index_model.dart'; import 'package:sport/router/navigator_util.dart'; import 'package:sport/utils/DateFormat.dart'; import 'package:sport/utils/click.dart'; import 'package:sport/utils/sport_utils.dart'; import 'package:sport/widgets/decoration.dart'; import 'package:sport/widgets/game_run.dart'; import 'package:sport/widgets/image.dart'; import 'package:sport/widgets/misc.dart'; import 'package:umeng_common_sdk/umeng_common_sdk.dart'; class RunIndexPage extends StatefulWidget { @override State createState() { return _RunIndexState(); } } bool runDelete = false; class _RunIndexState extends ViewStateLifecycle with AutomaticKeepAliveClientMixin, RunSetting, TickerProviderStateMixin, WidgetsBindingObserver { double _runTargetDuration = 0; double _runTargetKm = 0; double _left = 0, _top = -25, _right = 0, _bottom = 0; double _progress = 1.0; late AnimationController _animationController; late Animation _animation; late AnimationController _animationController1; late Animation _animation1; ValueNotifier _imageIndex = ValueNotifier(0); @override void initState() { super.initState(); WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { _initImages(); }); } @override void dispose() { _animationController.dispose(); super.dispose(); } _initImages() async { final int imageCount = 60; final int maxTime = 28 * imageCount; _animationController1 = new AnimationController(duration: Duration(milliseconds: maxTime), vsync: this); _animation1 = new Tween(begin: 0, end: imageCount.toDouble()).animate(_animationController1) ..addListener(() { _imageIndex.value = _animation1.value.floor() % imageCount; }); _animationController1.repeat(); } @override SportIndexModel createModel() => Provider.of(context, listen: false); @override Widget build(BuildContext context) { final Color priColor = const Color(0xffFFDD00); return Scaffold( body: Stack( children: [ Positioned( left: 0, top: MediaQuery.of(context).padding.top + 90.0, child: Stack( clipBehavior: Clip.none, children: [ Positioned( child: Opacity(opacity: _progress, child: Image.asset("lib/assets/img/run_bg_2.png")), top: _top, left: _left, ), Image.asset("lib/assets/img/run_bg_1.png"), ], ), ), SafeArea( child: ProviderWidget( model: this.model, autoDispose: false, builder: (BuildContext context, SportIndexModel model, Widget? child) { return EasyRefresh.custom( onRefresh: () async { model.refresh(); }, header: buildClassicalHeader(triggerDistance: 120.0, extent: 120.0), slivers: [ SliverFillRemaining( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Center( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { NavigatorUtil.goPage(context, (context) => RunListPage()).then((value) { if (runDelete == true) { model.refresh(); } }); UmengCommonSdk.onEvent("run_history", {}); }, child: Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( height: 120, ), Text( "${formatNum((model.data?.sum?.distanceJog ?? 0) / 1000, 2)}", style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 60.0, fontFamily: "DIN"), strutStyle: fixedLine, ), const SizedBox( height: 8, ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( "运动总公里", style: Theme.of(context).textTheme.subtitle1!, ), const SizedBox( width: 5, ), arrowRight(), ], ), ], ), ), ), ), Column( children: [ Row( children: [ Expanded( child: GestureDetector( onTap: () async { await NavigatorUtil.goPage( context, (context) => SettingPage( run: true, )); loadSetting().then((value) => setState(() {})); UmengCommonSdk.onEvent("run_setting", {}); }, child: Column( children: [ Container( width: 44, height: 44, padding: const EdgeInsets.all(11.0), margin: const EdgeInsets.only(bottom: 5), decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.white), child: Image.asset("lib/assets/img/setgoals_icon_set.png"), ), Text( "设置", style: Theme.of(context).textTheme.subtitle2!, ) ], ), ), ), Expanded( child: GestureDetector( onTap: throttle(() async { bool check = await runCheck(context); if (check == true) { var result = await NavigatorUtil.goPage( context, (context) => RunStartPage( runTargetDuration: _runTargetDuration, runTargetKm: _runTargetKm, )); if (result == true) { model.refresh(); } } }), child: Hero( tag: "run", child: Container( width: 110.0, height: 110.0, margin: const EdgeInsets.all(4), decoration: BoxDecoration(shape: BoxShape.circle, color: priColor, boxShadow: [BoxShadow(offset: Offset(0.0, 20.0), blurRadius: 15.0, spreadRadius: 0, color: priColor.withOpacity(0.21))]), child: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ ValueListenableBuilder( valueListenable: _imageIndex, builder: (context, snapshot, _) { return IndexedStack( index: snapshot, children: List.generate( 60, (e) => Image.asset( "assets/run/000${e.toString().padLeft(2, '0')}.png", width: 45.0, height: 45.0, )).toList(), ); }), const SizedBox( height: 5, ), Text( "跑步", style: Theme.of(context).textTheme.subtitle1!, ), ], ), )), ), ), ), Expanded( child: GestureDetector( onTap: () async { var result = await NavigatorUtil.goPage(context, (context) => RunTargetPage(_runTargetKm > 0 ? _runTargetKm : runTargetKm, _runTargetDuration > 0 ? _runTargetDuration : runTargetDuration)); if (result != null) { setState(() { _runTargetKm = result[0]; _runTargetDuration = result[1]; }); UmengCommonSdk.onEvent("run_target", {}); } }, child: Column( children: [ Container( width: 44, height: 44, margin: const EdgeInsets.only(bottom: 5), decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.white), child: Image.asset("lib/assets/img/rung_icon_target.png"), ), Text( _runTargetKm > 0 ? "目标 ${(_runTargetKm / 1000).toStringAsFixed(2)} 公里" : _runTargetDuration > 0 ? "目标 ${(_runTargetDuration / 60).toStringAsFixed(2)} 分钟" : "设定目标", style: Theme.of(context).textTheme.subtitle2!, ), ], ), ), ), ], ), const SizedBox( height: 10, ), model.data?.jogLast != null ? Container( margin: const EdgeInsets.all(12.0), padding: const EdgeInsets.symmetric(horizontal: 17.0), decoration: circular(), child: GestureDetector( onTap: () { int id = model.data?.jogLastId ?? 0; if (id == 0) { NavigatorUtil.goPage(context, (context) => RunListPage()); } else { NavigatorUtil.goPage( context, (context) => RunDetailPage( id: id, )); } }, behavior: HitTestBehavior.opaque, child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 12.0), child: Row( children: [ Image.asset("lib/assets/img/run_icon_today.png"), const SizedBox( width: 5, ), Text( "最近记录", style: Theme.of(context).textTheme.subtitle1!, ), Expanded(child: Container()), Text( "${SportUtils.toDateTimeDay(DateTime.parse(model.data?.jogLast?.jogRecord?.begin ?? ""))} ${SportUtils.toEndTime(DateTime.parse(model.data?.jogLast?.jogRecord?.begin ?? ""))}-${SportUtils.toEndTime(DateTime.parse(model.data?.jogLast?.jogRecord?.end ?? ""))}", style: Theme.of(context).textTheme.bodyText1!, ), ], ), ), Divider( height: 1, ), Padding( padding: const EdgeInsets.symmetric(vertical: 14.0), child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ ClipRRect( child: model.data?.jogLast?.jogRecord?.trackThumb?.startsWith("/") == true ? Image.file( File(model.data?.jogLast?.jogRecord?.trackThumb ?? ""), fit: BoxFit.cover, width: 70.0, height: 70.0, ) : CachedNetworkImage( imageUrl: "${model.data?.jogLast?.jogRecord?.trackThumb}", fit: BoxFit.cover, width: 70.0, height: 70.0, ), borderRadius: BorderRadius.circular(6), ), const SizedBox( width: 14.0, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( "${formatNum((model.data?.jogLast?.distance ?? 0) / 1000.0, 2)}", style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 30.0, fontFamily: "DIN"), strutStyle: fixedLine, ), Text( " 公里", style: Theme.of(context).textTheme.bodyText1!, ) ], crossAxisAlignment: CrossAxisAlignment.end, ), const SizedBox( height: 16, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Image.asset( "lib/assets/img/trajectory_icon_1.png", color: Color(0xff999999), ), const SizedBox( width: 7, ), Text( "${DateFormat.toTime(model.data?.jogLast?.duration ?? 0)}", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontFamily: "DIN", fontSize: 16.0), ) ], ), Row( children: [ Image.asset( "lib/assets/img/trajectory_icon_2.png", color: Color(0xff999999), ), const SizedBox( width: 7, ), Row( children: [ Text( "${SportUtils.pace11(SportUtils.calPace(model.data?.jogLast?.duration ?? 0, (model.data?.jogLast?.distance ?? 0) / 1000))}", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 16.0, fontFamily: "DIN"), ), Text( "′", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 16.0), ), Text( "${SportUtils.pace12(SportUtils.calPace(model.data?.jogLast?.duration ?? 0, (model.data?.jogLast?.distance ?? 0) / 1000))}", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 16.0, fontFamily: "DIN"), ), Text( "″", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 16.0), ), ], ), ], ), Row( children: [ Image.asset( "lib/assets/img/trajectory_icon_3.png", color: Color(0xff999999), ), const SizedBox( width: 7, ), Text( "${model.data?.jogLast?.consume ?? 0}", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontFamily: "DIN", fontSize: 16.0), ), Text( " 大卡", style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 10.0), ), ], ), ], ), ], ), ), ], ), ), // Padding( // padding: const EdgeInsets.symmetric(vertical: 12.0), // child: ((model.data?.today?.distanceJog ?? 0) == 0) // ? Padding( // padding: const EdgeInsets.all(20.0), // child: Text( // "今日还未开始运动", // style: Theme.of(context).textTheme.bodyText1!, // ), // ) // : Row( // children: [ // Expanded( // child: Column( // children: [ // Text( // "${formatNum((model.data?.today?.distanceJog ?? 0) / 1000, 2)}", // style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), // ), // const SizedBox( // height: 5, // ), // Text( // "公里", // style: Theme.of(context).textTheme.bodyText1!, // ) // ], // ), // ), // Expanded( // child: Column( // children: [ // Text( // "${DateFormat.toTime(model.data?.today?.durationJog ?? 0)}", // style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), // ), // const SizedBox( // height: 5, // ), // Text( // "时长", // style: Theme.of(context).textTheme.bodyText1!, // ) // ], // ), // ), // Expanded( // child: Column( // children: [ // Text( // "${SportUtils.pace(SportUtils.calPace(model.data?.today?.durationJog ?? 0, (model.data?.today?.distanceJog ?? 0) / 1000))}", // style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), // ), // const SizedBox( // height: 5, // ), // Text( // "配速(每公里用时)", // style: Theme.of(context).textTheme.bodyText1!, // ) // ], // ), // ), // ], // ), // ), ], ), ), ) : Container( margin: const EdgeInsets.all(12.0), padding: const EdgeInsets.symmetric(horizontal: 17.0), decoration: circular(), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 12.0), child: Row( children: [ Image.asset("lib/assets/img/run_icon_today.png"), const SizedBox( width: 5, ), Text( "最近记录", style: Theme.of(context).textTheme.subtitle1!, ), Expanded(child: Container()), ], ), ), Divider( height: 1, ), Padding( padding: const EdgeInsets.symmetric(vertical: 40.0, horizontal: 20.0), child: Text( "还未开始运动", style: Theme.of(context).textTheme.bodyText1!, ), ), ], ), ), ], ) ], ), ) ]); }), ), ], ), ); } @override bool get wantKeepAlive => true; }