import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:sport/application.dart'; import 'package:sport/bean/jog/sum.dart'; import 'package:sport/pages/run/run_detail_page.dart'; import 'package:sport/pages/run/run_index.dart'; import 'package:sport/pages/run/run_page.dart'; import 'package:sport/pages/run/statistics_page.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/services/app_lifecycle_state.dart'; import 'package:sport/utils/DateFormat.dart'; import 'package:sport/utils/sport_utils.dart'; import 'package:sport/widgets/appbar.dart'; import 'package:sport/widgets/error.dart'; import 'package:sport/widgets/image.dart'; import 'package:sport/widgets/loading.dart'; import 'package:umeng_common_sdk/umeng_common_sdk.dart'; class RunListPage extends StatefulWidget { @override State createState() => _PageState(); } class _PageState extends LifecycleState with InjectApi { ValueNotifier _notifierYear = ValueNotifier(DateTime.now().year); bool expand = true; bool loading = true; Object? error; List? records; Sum? sum; @override void initState() { super.initState(); runDelete = false; _notifierYear.addListener(() { _loadData(); }); records = []; _loadData(); } @override void dispose() { super.dispose(); _notifierYear.dispose(); } _loadData() async { setState(() { this.loading = true; this.records = []; }); try { RespData data = await api.jogListOneYear(_notifierYear.value); setState(() { this.loading = false; this.sum = data.data?.sum; this.records = data.data?.records ?? []; }); } catch (e) { print(e); if(isDebugShoe) { setState(() { this.error = e; }); } } } @override Widget build(BuildContext context) { final List slivers = []; slivers.add(SliverAppBar( backgroundColor: const Color(0xff241D19), expandedHeight: 300.0, elevation: 0.0, pinned: true, leading: IconButton( icon: Image.asset( "lib/assets/img/topbar_return.png", color: Colors.white, ), onPressed: () { Navigator.maybePop(context); }, ), titleSpacing: -16.0, centerTitle: false, title: Text( "累计记录", style: titleStyle.copyWith(color: Colors.white), ), brightness: Brightness.dark, actions: [ TextButton( child: Text( "跑步统计", style: titleStyle.copyWith( color: Colors.white, fontSize: 16.0, fontWeight: FontWeight.normal, ), ), onPressed: () { NavigatorUtil.goPage(context, (context) => RunStatisticsPage()); UmengCommonSdk.onEvent("run_statistics", {}); }) ], flexibleSpace: FlexibleSpaceBar( collapseMode: CollapseMode.none, background: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ Text("${formatNum((sum?.distance ?? 0) / 1000, 2)}", style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 45.0, fontFamily: "DIN", color: Theme.of(context).accentColor)), Text( "累计里程(公里)", style: Theme.of(context).textTheme.headline6!, ), const SizedBox( height: 10.0, ), Padding( padding: const EdgeInsets.symmetric(vertical: 25.0), child: Row( children: [ Expanded( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text("${DateFormat.toTime(sum?.duration ?? 0)}", style: Theme.of(context).textTheme.headline4!.copyWith( fontSize: 20.0, fontFamily: "DIN", )), Text( "总时长", style: Theme.of(context).textTheme.headline6!.copyWith(fontSize: 12.0), ), ], )), Container( height: 42.0, width: .5, color: Colors.white54, ), Expanded( child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ Text( "${SportUtils.pace11(SportUtils.calPace(sum?.duration ?? 0, (sum?.distance ?? 0.0).toDouble() / 1000))}", style: Theme.of(context).textTheme.headline4!.copyWith(fontSize: 20.0, fontFamily: "DIN"), ), Text( "′", style: Theme.of(context).textTheme.headline4!.copyWith(fontSize: 20.0), ), Text( "${SportUtils.pace12(SportUtils.calPace(sum?.duration ?? 0, (sum?.distance ?? 0.0).toDouble() / 1000))}", style: Theme.of(context).textTheme.headline4!.copyWith(fontSize: 20.0, fontFamily: "DIN"), ), Text( "″", style: Theme.of(context).textTheme.headline4!.copyWith(fontSize: 20.0), ), ], ), Text( "配速(每公里用时)", style: Theme.of(context).textTheme.headline6!.copyWith(fontSize: 12.0), ), ], )), Container( height: 42.0, width: .5, color: Colors.white54, ), Expanded( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text("${sum?.times ?? 0}", style: Theme.of(context).textTheme.headline4!.copyWith( fontSize: 20.0, fontFamily: "DIN", )), Text( "运动次数(次)", style: Theme.of(context).textTheme.headline6!.copyWith(fontSize: 12.0), ), ], )), ], ), ), const SizedBox( height: 40.0, ), ], ), ), bottom: PreferredSize( preferredSize: Size.fromHeight(50.0), child: Container( height: 50.0, transform: Matrix4.translationValues(0, 2, 0), decoration: BoxDecoration( borderRadius: BorderRadius.only(topRight: Radius.circular(10), topLeft: Radius.circular(10)), color: Colors.white, ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ GestureDetector( behavior: HitTestBehavior.opaque, onTap: () async { var year = await showDialog( context: context, builder: (context) => Dialog( child: ConstrainedBox( constraints: BoxConstraints(minHeight: 300, maxHeight: 400), child: ScrollablePositionedList.builder( itemCount: 10, itemBuilder: (context, index) { var now = DateTime.now(); int year = now.year - index; return GestureDetector( onTap: () => Navigator.pop(context, year), child: Container( width: double.infinity, margin: EdgeInsets.symmetric(vertical: 20, horizontal: 30), alignment: Alignment.center, child: Text('$year'), ), ); }, initialScrollIndex: DateTime.now().year - _notifierYear.value, )))); if (year != null) _notifierYear.value = year; }, child: ValueListenableBuilder( valueListenable: _notifierYear, builder: (_, year, __) => RichText( text: TextSpan(children: [ TextSpan(text: '$year', style: Theme.of(context).textTheme.subtitle1!), WidgetSpan( alignment: PlaceholderAlignment.middle, child: Padding(padding: const EdgeInsets.only(left: 7.0), child: arrowBottom()), ), ]), ), ), ), GestureDetector( onTap: () { setState(() { expand = !expand; records?.forEach((element) { element.expand = expand; }); }); }, child: RichText( text: TextSpan(children: [ TextSpan(text: expand == true ? '收起' : '展开', style: Theme.of(context).textTheme.bodyText1!), WidgetSpan( alignment: PlaceholderAlignment.middle, child: Padding( padding: const EdgeInsets.only(left: 7.0), child: Image.asset("lib/assets/img/run_data_${expand == true ? 'retract' : 'open'}.png"), ), ), ]), ), ), ], ), ), )), )); if (loading == true) { slivers.add(SliverToBoxAdapter( child: RequestLoadingWidget(), )); } else { if (records?.isNotEmpty != true) { slivers.add(SliverToBoxAdapter( child: RequestErrorWidget( null, assets: RequestErrorWidget.ASSETS_NO_MOTION, msg: "暂无记录", ), )); } } if(error != null){ slivers.add(SliverToBoxAdapter( child: RequestErrorWidget( null, assets: RequestErrorWidget.ASSETS_NO_MOTION, msg: "${error}", ), )); } if (records?.isNotEmpty == true) { for (var i = 0; i < records!.length; i++) { var e = records![i]; var child = e.records ?? []; final SizedBox space = const SizedBox( width: 4, ); slivers.add(SliverStickyHeader( header: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { setState(() { e.expand = !e.expand; if (e.expand == true) expand = true; }); }, child: Container( color: Colors.white, padding: const EdgeInsets.fromLTRB(12.0, 10, 12.0, 0), child: Container( width: double.infinity, color: const Color(0xfff1f1f1), padding: const EdgeInsets.fromLTRB(9, 7, 9, 5), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Container( width: 50.0, child: Align( alignment: Alignment.centerLeft, child: Column( children: [ Text( '${e.month}月', style: Theme.of(context).textTheme.headline1!, ), Text( '共${child.length}次', style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 10), ), ], ), ), ), Expanded( flex: 3, child: Row( mainAxisSize: MainAxisSize.min, children: [ Image.asset("lib/assets/img/home_title_steps.png"), space, Text( '${formatNum((e.distance ?? 0) / 1000, 2)}', style: Theme.of(context).textTheme.bodyText2!, ), ], ), ), Expanded( flex: 4, child: Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ Image.asset("lib/assets/img/rundata_icon_duration.png"), space, Text( '${DateFormat.toTime(e.duration ?? 0)}', style: Theme.of(context).textTheme.bodyText2!, ), ], ), ), ), Expanded( flex: 3, child: Center( child: Row( mainAxisSize: MainAxisSize.min, children: [ Image.asset("lib/assets/img/home_title_duration.png"), space, Text( '${SportUtils.pace4((e.duration ?? 0), (e.distance ?? 0))}', style: Theme.of(context).textTheme.bodyText2!, ), ], ), ), ), Container( width: 20, alignment: Alignment.centerRight, child: e.expand == false ? Image.asset("lib/assets/img/run_data_open.png") : Container(), ), ], ), ), ), ), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, i) { var e = child[i]; int day = 0; int hour = 0; int minute = 0; if (e.begin != null) { var time = DateTime.parse(e.begin!); day = time.day; hour = time.hour; minute = time.minute; } return Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(21.0, 8.0, 12.0, 12.0), child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { NavigatorUtil.goPage( context, (context) => RunDetailPage( id: e.id!, )); }, onLongPress: () async{ var result = await showCupertinoModalPopup( context: context, builder: (BuildContext context) { return CupertinoActionSheet( title: Text('提示'), message: Text('请选择要操作的选项'), cancelButton: TextButton(onPressed: () {Navigator.pop(context, false); }, child: Center(child: Text("取消", style: Theme.of(context).textTheme.bodyText1,)),), actions: [ CupertinoActionSheetAction( child: Text( '拷贝该条记录到今天', style: TextStyle(color: Color(0xff333333),fontSize: 16.0), ), onPressed: () async { api.jogCopyRecord(e.id!).then((value) => _loadData()); Navigator.pop(context, false); }, isDefaultAction: true, ), CupertinoActionSheetAction( child: Text('删除该条记录', style: TextStyle(fontSize: 16.0),), onPressed: () { api.jogDelRecord(e.id!).then((value) => _loadData()); runDelete = true; Navigator.pop(context, true); }, isDestructiveAction: true, ), ], ); }); if (result == true) {} }, child: Container( key: Key("item_${e.id}"), child: Column( children: [ Row( children: [ Container( width: 45.0, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( '$day', style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 20.0, color: Theme.of(context).accentColor, fontFamily: "DIN"), ), Text( '日', style: Theme.of(context).textTheme.subtitle1!.copyWith(fontSize: 12.0, color: Theme.of(context).accentColor), ), ], ), const SizedBox( height: 3.0, ), Text( '${hour.toString().padLeft(2, "0")}:${minute.toString().padLeft(2, "0")}', style: Theme.of(context).textTheme.bodyText1!, ), ], ), ), Expanded( child: Row( children: [ Expanded( child: Column( children: [ Text( '${formatNum((e.distance ?? 0) / 1000, 2)}', style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), ), Text( '公里', style: Theme.of(context).textTheme.bodyText1!, ), ], ), ), Expanded( child: Column( children: [ Text( '${DateFormat.toTime(e.duration ?? 0)}', style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), ), Text( '时长', style: Theme.of(context).textTheme.bodyText1!, ), ], ), ), Expanded( child: Column( children: [ Text( '${SportUtils.pace4(e.duration ?? 0, e.distance ?? 0)}', style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), ), Text( '配速', style: Theme.of(context).textTheme.bodyText1!, ), ], ), ) ], ), ) ], ), ], ), ), // Table( // defaultVerticalAlignment: TableCellVerticalAlignment.bottom, // children: [ // TableRow(children: [ // TableCell( // child: , // ), // TableCell( // child: Text( // '${formatNum((e.distance ?? 0) / 1000, 2)}', // style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), // )), // TableCell( // child: Text( // '${DateFormat.toTime(e.duration ?? 0)}', // style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), // )), // TableCell( // child: Text( // '${SportUtils.pace4(e.duration ?? 0, e.distance ?? 0)}', // style: Theme.of(context).textTheme.headline1!.copyWith(fontSize: 20.0, fontFamily: "DIN"), // )), // ]), // TableRow(children: [ // TableCell(child: Text('${hour.toString().padLeft(2, "0")}:${minute.toString().padLeft(2, "0")}')), // TableCell( // child: Text( // '公里', // style: Theme.of(context).textTheme.bodyText1!, // )), // TableCell(child: Text('时长', style: Theme.of(context).textTheme.bodyText1!)), // TableCell(child: Text('配速', style: Theme.of(context).textTheme.bodyText1!)), // ]), // ], // ), ), ), if (i < child.length - 1) Divider( height: 1, ), ], ); }, childCount: e.expand == true ? child.length : 0, ), ), )); } } return Scaffold( backgroundColor: Colors.white, body: CustomScrollView( slivers: slivers, ), ); } }