import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_blue/flutter_blue.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sport/bean/game.dart' as game; import 'package:sport/bean/game.dart'; import 'package:sport/bean/sport_detail.dart'; import 'package:sport/bean/sport_index.dart'; import 'package:sport/bean/user.dart'; import 'package:sport/constant/ui.dart' show ui_padding, ui_margin_list; import 'package:sport/pages/game/detail_bottom.dart'; import 'package:sport/pages/game/game_detail.dart'; import 'package:sport/pages/game/game_info.dart'; import 'package:sport/pages/home/consume_page.dart'; import 'package:sport/pages/home/duration_page.dart'; import 'package:sport/pages/home/guide_page.dart'; import 'package:sport/pages/home/sport_history_all_page.dart'; import 'package:sport/pages/home/sport_history_page.dart'; import 'package:sport/pages/home/sport_list_page.dart'; import 'package:sport/pages/home/step_page.dart'; import 'package:sport/pages/home/strength_page.dart'; import 'package:sport/provider/bluetooth.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/provider/user_model.dart'; import 'package:sport/router/navigator_util.dart'; import 'package:sport/router/routes.dart'; import 'package:sport/widgets/button_primary.dart'; import 'package:sport/widgets/circular_percent_indicator.dart'; import 'package:sport/widgets/decoration.dart'; import 'package:sport/widgets/dialog/search_device.dart'; import 'package:sport/widgets/dialog/share_popup.dart'; import 'package:sport/widgets/error.dart'; import 'package:sport/widgets/game_run.dart'; import 'package:sport/widgets/image.dart'; import 'package:sport/widgets/loading.dart'; import 'package:sport/widgets/misc.dart'; import 'package:sport/widgets/progress_bar.dart'; import 'package:sport/widgets/space.dart'; import 'package:sport/widgets/wave_painter.dart'; class HomeInfoPage extends StatefulWidget { @override State createState() => _PageState(); } class _PageState extends ViewStateLifecycle with AutomaticKeepAliveClientMixin { String _rank; ValueNotifier _valueNotifierGuide = ValueNotifier(false); static const String GUIDE = "guide"; EasyRefreshController _easyRefreshController; Bluetooth bluetooth; @override void initState() { super.initState(); _easyRefreshController = EasyRefreshController(); loadSetting(); initList(); bluetooth = Provider.of(context, listen: false); } void loadSetting() async { SharedPreferences preferences = await SharedPreferences.getInstance(); _valueNotifierGuide.value = preferences.getBool(GUIDE) ?? true; } @override SportIndexModel createModel() => SportIndexModel(context); refresh() async { _rank = null; model.refresh(); if (bluetooth?.isConnected == true) { bluetooth.queryDeviceStep(); await Future.delayed(Duration(seconds: 5)); model.refresh(); } } initList() { model.valueNotifier.addListener(() { print("list--------------------------${model.valueNotifier.value}"); if (model.valueNotifier.value.length > 0) { Navigator.push(context, new PopRoute(child: sharePopup(model.valueNotifier.value))); } }); } @override Widget build(BuildContext context) { super.build(context); const _margin = EdgeInsets.fromLTRB(ui_padding, ui_padding, ui_padding, 0.0); return Scaffold( appBar: AppBar( titleSpacing: 0.0, backgroundColor: Theme.of(context).scaffoldBackgroundColor, title: _buildDeviceInfoWidget(), ), body: ProviderWidget( model: this.model, onModelReady: (v) => v.initData(), builder: (BuildContext context, SportIndexModel model, Widget child) => EasyRefresh.custom( controller: _easyRefreshController, onRefresh: () => refresh(), header: buildClassicalHeader(), slivers: [ // // 暂时测试 // if (model.isIdle && value.data != null) // SliverToBoxAdapter( // child: Stack( // fit: StackFit.passthrough, // children: [ // CustomPaint( // painter: _HeaderBackground(), // child: Container( // height: 116, // ), // ), // Container( // margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0), // decoration: card(), // height: 156, // child: _buildSportWidget(value.data), // ) // ], // ), // ), // if (model.isIdle && value.data != null) // SliverToBoxAdapter( // child: _buildTopWidget(value.data), // ), if (model.isBusy) SliverToBoxAdapter( child: RequestLoadingWidget(), ), // if (value.data?.lastGame != null) // SliverToBoxAdapter( // child: _buildSportHistoryWidget(value.data.lastGame), // ), // SliverToBoxAdapter( // child: ValueListenableBuilder( // builder: (BuildContext context, value, Widget child) => value ? _buildGuideWidget() : Container(), valueListenable: _valueNotifierGuide), // ), SliverToBoxAdapter( child: Container( height: 250, child: Column( children: [ Container( height: 182, child: Selector( selector: (_, bluetooth) => bluetooth.device, builder: (_, device, ___) { return device == null ? CachedNetworkImage( height: 182, imageUrl: "http://static.ouj.com/hiyd/fb337b03b0929085f1d07c036cd40ec4_size750x281_115600.png", ) : StreamBuilder( stream: device.state, initialData: BluetoothDeviceState.connecting, builder: (c, snapshot) => CachedNetworkImage( height: 182, imageUrl: snapshot.data == BluetoothDeviceState.connected ? "http://static.ouj.com/hiyd/a856a17e1cd2006a6cd6e7df6b7f1357_size750x285_116051.png" : "http://static.ouj.com/hiyd/fb337b03b0929085f1d07c036cd40ec4_size750x281_115600.png", )); })), Space( height: 12, ), Selector( selector: (_, bluetooth) => bluetooth.device, builder: (_, device, ___) { return device != null ? PrimaryButton( content: "开始运动", width: 174, height: 40, // bold: true, callback: () { showDialog(context: context, child: SportListPage()); }, ) : PrimaryButton( content: "添加设备", width: 174, height: 40, callback: () { showDialog(context: context, child: SearchDeviceDialog()); }, ); }), Space( height: 12, ), ], ), ), ), SliverToBoxAdapter( child: Container( margin: _margin, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => StepPage()), behavior: HitTestBehavior.opaque, child: Container( decoration: circular(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( text: TextSpan(style: DefaultTextStyle.of(context).style, children: [ WidgetSpan( alignment: PlaceholderAlignment.middle, child: Image.asset( 'lib/assets/img/home_title_steps.png', ), ), TextSpan(text: '\t今日步数', style: Theme.of(context).textTheme.subtitle1) ]), ), Padding( padding: const EdgeInsets.all(20.0), child: Center( child: Row( children: [ Text( "${model.data?.today?.step ?? 0}", style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0), strutStyle: fixedLine, ), Text( "步", style: Theme.of(context).textTheme.subtitle2, strutStyle: fixedLine, ) ], crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, ), ), ) ], ), padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0), ), ), ), SizedBox( width: 12.0, ), Expanded( child: GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => ConsumePage()), behavior: HitTestBehavior.opaque, child: Container( decoration: circular(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( text: TextSpan(style: DefaultTextStyle.of(context).style, children: [ WidgetSpan( alignment: PlaceholderAlignment.middle, child: Image.asset( 'lib/assets/img/home_title_consume.png', ), ), TextSpan(text: '\t今日消耗', style: Theme.of(context).textTheme.subtitle1) ]), ), Padding( padding: const EdgeInsets.all(20.0), child: Center( child: Row( children: [ Text( "${model.data?.today?.consume ?? 0}", style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0), strutStyle: fixedLine, ), Text( "卡", style: Theme.of(context).textTheme.subtitle2, strutStyle: fixedLine, ) ], crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, ), ), ) ], ), padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0), ), ), ), ], ), ), ), SliverToBoxAdapter( child: GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => DurationPage()), child: Container( margin: _margin, decoration: circular(), child: Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ RichText( text: TextSpan(style: DefaultTextStyle.of(context).style, children: [ WidgetSpan( alignment: PlaceholderAlignment.middle, child: Image.asset( 'lib/assets/img/home_title_duration.png', ), ), TextSpan(text: '\t运动目标', style: Theme.of(context).textTheme.subtitle1) ]), ), arrowRight4() ], mainAxisAlignment: MainAxisAlignment.spaceBetween, ), ), Divider( height: 0.5, ), Padding( padding: const EdgeInsets.fromLTRB(15.0, 8.0, 15.0, 8.0), child: Row( children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 3.0), child: CircularPercentIndicator( radius: 100.0, lineWidth: 10.0, percent: 1.0 * (model?.data?.today?.value(model.data?.target?.type) ?? 0) / (model.data?.target?.valueTarget ?? 0.01), center: Column( children: [ SizedBox( height: 10.0, ), Text( "${model.data?.today?.value(model.data?.target?.type) ?? 0}", style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0), strutStyle: fixedLine, ), Text( "${model.data?.target?.label}", style: Theme.of(context).textTheme.subtitle2, strutStyle: fixedLine, ) ], mainAxisSize: MainAxisSize.min, ), animation: true, animationDuration: 1000, animateFromLastPercent: true, startAngle: 200.0, arcType: ArcType.CUSTOM, backgroundColor: Color(0xfff1f1f1), circularStrokeCap: CircularStrokeCap.round, rotateLinearGradient: true, linearGradient: LinearGradient( colors: [Color(0xff8DF7FF), Color(0xff16A2FF)], ), ), ), SizedBox( width: 15.0, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "今日目标:${model.data?.target?.valueTarget ?? 0} ${model.data?.target?.label}", style: Theme.of(context).textTheme.subtitle1, ), SizedBox( height: 8.0, ), Text( "月达标天数:${model.data?.target?.monthFinish ?? 0}天", style: Theme.of(context).textTheme.subtitle1, ) ], ) ], ), ), ], ), ), ), ), SliverToBoxAdapter( child: GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => StrengthPage()), child: Container( margin: _margin, decoration: circular(), child: Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ RichText( text: TextSpan(style: DefaultTextStyle.of(context).style, children: [ WidgetSpan( alignment: PlaceholderAlignment.middle, child: Image.asset( 'lib/assets/img/home_title_strength.png', ), ), TextSpan(text: '\t运动强度', style: Theme.of(context).textTheme.subtitle1) ]), ), arrowRight4() ], mainAxisAlignment: MainAxisAlignment.spaceBetween, ), ), Divider( height: 0.5, ), Padding( padding: const EdgeInsets.fromLTRB(15.0, 8.0, 15.0, 8.0), child: Row( children: [ CircularProgressBar( percent: (model.data?.today?.strength ?? 0) / 12.0, center: Column( children: [ SizedBox( height: 10.0, ), Text( "${model.data?.today?.strength?.toStringAsFixed(1) ?? 0}", style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0), strutStyle: fixedLine, ), Text( "卡/分", style: Theme.of(context).textTheme.subtitle2, strutStyle: fixedLine, ) ], mainAxisSize: MainAxisSize.min, ), ), SizedBox( width: 15.0, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "强度评级:${model.data?.today?.strengthLabel()}", style: Theme.of(context).textTheme.subtitle1, ), SizedBox( height: 8.0, ), Text( "已经运动:${(model.data?.today?.duration ?? 0) == 0 ? '无' : "${model.data?.today?.duration}分钟"}", style: Theme.of(context).textTheme.subtitle1, ) ], ) ], ), ), ], ), ), ), ), SliverToBoxAdapter( child: Container( padding: EdgeInsets.fromLTRB(12.0,16.0, 12.0,4.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "今日运动", style: Theme.of(context).textTheme.headline1.copyWith(fontSize: 16.0), ), GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => SportHistoryAllPage()), child: RichText( text: TextSpan(style: DefaultTextStyle.of(context).style, children: [ TextSpan(text: '更多\t', style: Theme.of(context).textTheme.bodyText1), WidgetSpan( alignment: PlaceholderAlignment.middle, child: arrowRight4(), ), ]), ), ), ], )), ), // if(model.data?.records?.isNotEmpty == true) SliverPadding( padding: const EdgeInsets.symmetric(horizontal: ui_padding), sliver: SliverList( delegate: SliverChildBuilderDelegate((content, index) { if (index == 0) { return _buildSportRecord(RecordsToday(id: -1, name: "跑步训练", userCount: 144234, cover: "lib/assets/img/home_game_run.png"), null, 0); } var item = model.data?.records[index - 1]; var game = model.data?.games?.firstWhere((element) => element.id == item.gameId); return GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => SportHistoryPage(game)), child: _buildSportRecord(item, game, 1)); }, childCount: (model.data?.records?.length ?? 0) + 1), )), // SliverToBoxAdapter( // child: Container( // margin: const EdgeInsets.fromLTRB(ui_padding, 14.0, ui_padding, 14.0), // decoration: circular(), // child: RequestErrorWidget( // null, // assets: RequestErrorWidget.ASSETS_NO_MOTION, // msg: "您还未进行任何运动", // ))), // if (value.data?.games != null) // SliverToBoxAdapter( // child: _buildLabelWidget("SPORTS"), // ), // if (value.data?.games != null) // SliverPadding( // padding: const EdgeInsets.symmetric(horizontal: ui_padding), // sliver: SliverGrid( // gridDelegate: // SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, childAspectRatio: 170.0 / 226), // delegate: SliverChildBuilderDelegate((BuildContext context, int index) { // return InkWell( // onTap: () => NavigatorUtil.goGameDetails(context, details: value.data.games[index]), // child: _buildSportItemWidget(value.data.games[index])); // }, childCount: value.data.games.length), // ), // ), // if (value.data?.rank != null) // SliverToBoxAdapter( // child: Padding( // padding: const EdgeInsets.only(top: 12.0), // child: Row( // children: [ // Expanded(child: _buildLabelWidget("CHARTS")), // PopupMenuButton( // onSelected: (v) { // value.game(v); // }, // itemBuilder: (BuildContext context) { // return divideMenus(value.data.games.map((e) => menuItemSelected(e.id, e.name, e.id == value.gameId)).toList()); // }, // child: Row( // mainAxisSize: MainAxisSize.min, // children: [ // Text( // value.data.games.where((element) => element.id == value.gameId).map((e) => e.name).first, // style: Theme.of(context).textTheme.bodyText1, // ), // Padding( // padding: const EdgeInsets.all(6.0), // child: arrowBottom(), // ) // ], // ), // ), // SizedBox( // width: 12, // ), // NotificationListener( // onNotification: (n) { // _rank = n.rank; // request(context, () async { // await value.area( // n.all ? null : n.provinceId, n.all ? null : n.province ? null : n.cityId, n.all ? null : n.province ? null : n.districtId); // }); // return true; // }, // child: AreaPage( // area: _rank, // ), // ) // ], // ), // ), // ), // if (value.data?.rank != null) // SliverToBoxAdapter( // child: _buildRankWidget(value.data.rank), // ), // if (value.data?.rank != null) // SliverPadding( // padding: const EdgeInsets.symmetric(horizontal: ui_padding), // sliver: SliverList( // delegate: SliverChildBuilderDelegate((content, index) { // return (value.data.rank.records.length - 3) > index // ? _buildRankItemWidget(value.data.rank.records[index + 3], index) // : _buildRankItemWidget(null, index); // }, childCount: 7), // )), // if (value.data?.rank != null) // SliverToBoxAdapter( // child: Center( // child: InkWell( // onTap: () { // NavigatorUtil.goRankDetails(context, value.data.rank.rank.gameId, 1); // }, // child: Padding( // padding: const EdgeInsets.all(16.0), // child: Text( // "查看完整榜单", // style: Theme.of(context).textTheme.bodyText1.copyWith(color: Color(0xffcecece)), // ), // ), // )), // ), SliverToBoxAdapter( child: SizedBox( height: 12, ), ), ], ), ), ); } @override bool get wantKeepAlive => true; Widget _buildDeviceInfoWidget() { return Padding( padding: const EdgeInsets.only(left: ui_padding), child: Selector( selector: (_, bluetooth) => bluetooth.device, builder: (_, device, ___) { return device == null ? _buildDeviceInfoConnectFailWidget() : _buildDeviceInfoStateWidget(device); }), ); } Widget _buildDeviceInfoStateWidget(BluetoothDevice device) { return StreamBuilder( stream: device.state, initialData: BluetoothDeviceState.connecting, builder: (c, snapshot) => _buildDeviceInfoConnectedWidget(snapshot.data == BluetoothDeviceState.connected)); } Widget _buildDeviceInfoConnectedWidget(bool connect) { const Color _color = Color(0xffFF5B1D); return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => NavigatorUtil.go(context, Routes.deviceInfo), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( width: 10, height: 10, decoration: BoxDecoration(shape: BoxShape.circle, color: connect ? Color(0xff00DC42) : _color), ), SizedBox( width: 6.0, ), Text("设备:${bluetooth?.device?.name}", style: Theme.of(context).textTheme.subtitle1, strutStyle: StrutStyle(forceStrutHeight: true)), ], ), ValueListenableBuilder( valueListenable: Provider.of(context, listen: false).electricityNotifier, builder: (_, data, ___) => Container( padding: EdgeInsets.fromLTRB(15.0, 6.0, 10.0, 6.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only(topLeft: Radius.circular(30), bottomLeft: Radius.circular(30)), border: Border.all( color: Theme.of(context).scaffoldBackgroundColor, width: .5, ), ), child: Row( children: [ Stack( alignment: Alignment.bottomCenter, children: [ Image.asset( "lib/assets/img/battery.png", color: const Color(0xff241D19), ), Container( width: 5.5, height: 8 * (data / 100), margin: EdgeInsets.only(bottom: 2, right: 0.5), decoration: BoxDecoration(color: const Color(0xff241D19)), ), ], ), const SizedBox( width: 5, ), Text( "$data%", style: Theme.of(context).textTheme.subtitle1, ) ], ), )), ], )); } Widget _buildDeviceInfoConnectFailWidget() { const Color _color = Color(0xffFF5B1D); return Row( children: [ Container( width: 10, height: 10, decoration: BoxDecoration(shape: BoxShape.circle, color: _color), ), SizedBox( width: 6.0, ), Text("未连接设备", style: TextStyle( color: _color, fontSize: 14, ), strutStyle: StrutStyle(forceStrutHeight: true)), ], ); } Widget _buildDeviceWidget() { return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset("lib/assets/img/home_image_connect.png"), Space( height: 8, ), Text( "请先连接设备鞋子", style: Theme.of(context).textTheme.bodyText1, ), Space( height: 12, ), PrimaryButton( content: "搜索设备", width: 132, height: 35, callback: () { showDialog(context: context, child: SearchDeviceDialog()); }, ) ], ); } Widget _buildTopWidget(SportIndex item) { return Stack( fit: StackFit.passthrough, children: [ CustomPaint( painter: _HeaderBackground(), child: Container( height: 116, ), ), Container( margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0), decoration: card(), height: 156, child: Selector( selector: (_, bluetooth) => bluetooth.device, builder: (_, device, ___) { return device != null ? _buildSportWidget(item) : _buildDeviceWidget(); }), ) ], ); } Widget _buildSportWidget(SportIndex item) { return InkWell( onTap: () => NavigatorUtil.go(context, Routes.sportDetail), child: Stack( alignment: Alignment.center, children: [ Row( children: [ Expanded( flex: 5, child: Padding( padding: const EdgeInsets.all(16.0), child: ValueListenableBuilder( valueListenable: Provider.of(context, listen: false).durationTarget, builder: (_, v, __) => ProgressManager(min(.9, v <= 0 ? 0 : item.duration / v))), )), Expanded( flex: 6, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "今日已运动", style: Theme.of(context).textTheme.subtitle1, ), SizedBox( height: 5, ), RichText( text: TextSpan(style: Theme.of(context).textTheme.subtitle2, children: [ TextSpan(text: '${item.duration}', style: Theme.of(context).textTheme.subtitle2.copyWith(fontWeight: FontWeight.bold, fontSize: 40)), TextSpan(text: '分钟'), ]), ), SizedBox( height: 5, ), Text("共消耗 ${item.consume} kal", style: Theme.of(context).textTheme.bodyText1), SizedBox( height: 5, ), Text(item.beyond > 0 ? "已超越了${item.beyond}人,请再接再厉" : "${item.inspire}", style: Theme.of(context).textTheme.bodyText1), ], ), ) ], ), Positioned( right: 0, child: Image.asset("lib/assets/img/home_icon_details.png"), ) ], ), ); } Widget _buildSportRecord(RecordsToday item, GameInfoData game, int type) { return Container( margin: const EdgeInsets.only(top: ui_padding), decoration: circular(), child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(ui_padding, 14.0, ui_padding, 14.0), child: Row( children: [ item.cover?.startsWith("http") == true ? GestureDetector( onTap: () => NavigatorUtil.goPage(context, (context) => GameDetailsPage(game)), child: ClipRRect( child: CachedNetworkImage( width: 50, height: 50, imageUrl: item.cover ?? '', fit: BoxFit.cover, ), // 也可控件一边圆角大小 borderRadius: new BorderRadius.all(Radius.circular(6.0)), ), ) : Image.asset( item.cover, width: 50.0, height: 50.0, ), SizedBox( width: ui_padding, ), Expanded( child: Column( children: [ Text(item.name, style: Theme.of(context).textTheme.headline1.copyWith(fontSize: 16.0)), const SizedBox( height: 6.0, ), Text( "${item.userCount}人在练", style: Theme.of(context).textTheme.bodyText1, ), ], crossAxisAlignment: CrossAxisAlignment.start, )), PrimaryButton( width: 72, height: 35, content: type == 0 ? "跑步" : "继续", callback: type == 0 ? () { startRun(context); } : () => startGame(context, game), ) ], ), ), Divider( height: 0.5, ), Padding( padding: const EdgeInsets.fromLTRB(ui_padding, 15.0, ui_padding, 15.0), child: Row( children: [ Column( children: [ Text(type == 0 ? "38.0" : "${item.durationTotalMinute}", style: Theme.of(context).textTheme.headline1), const SizedBox( height: 2.0, ), Text( type == 0 ? "总里程(公里)" : "总时长(分钟)", style: Theme.of(context).textTheme.bodyText1, ), ], ), Column( children: [ type == 0 ? RichText( text: TextSpan(style: Theme.of(context).textTheme.headline1, children: [ TextSpan(text: "30′"), WidgetSpan( alignment: PlaceholderAlignment.bottom, child: Padding( padding: const EdgeInsets.only(bottom: 1.0), child: Text("38″", style: Theme.of(context).textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600)), )) ]), ) : Text("${item.todayScoreMax.toStringAsFixed(1)}", style: Theme.of(context).textTheme.headline1), const SizedBox( height: 2.0, ), Text( type == 0 ? "总时长" : "今日最高评分", style: Theme.of(context).textTheme.bodyText1, ), ], ), Column( children: [ type == 0 ? RichText( text: TextSpan(style: Theme.of(context).textTheme.headline1, children: [ TextSpan(text: "12′"), WidgetSpan( alignment: PlaceholderAlignment.bottom, child: Padding( padding: const EdgeInsets.only(bottom: 1.0), child: Text("38″", style: Theme.of(context).textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600)), ), ) ]), ) : Text("${item.times}", style: Theme.of(context).textTheme.headline1), const SizedBox( height: 2.0, ), Text( type == 0 ? "平均配速" : "运动次数(次)", style: Theme.of(context).textTheme.bodyText1, ), ], ) ], mainAxisAlignment: MainAxisAlignment.spaceAround, ), ), ], )); } Widget _buildRankWidget(RankInfo rankInfo) { return Column( children: [ if (rankInfo?.user != null) GestureDetector( onTap: () => NavigatorUtil.goRankPeopleDetails(context, details: rankInfo.user), child: Container( margin: EdgeInsets.all(ui_padding), padding: EdgeInsets.all(ui_padding), decoration: card(), child: Row( children: [ Padding( padding: const EdgeInsets.only(right: 8), child: Text( "${rankInfo.user.position}", ), ), if (rankInfo.user.up != 0 && rankInfo.user.upNew != 0) Padding( padding: const EdgeInsets.only(right: 8), child: Image.asset("lib/assets/img/rand_icon_${rankInfo.user.up >= 0 ? 'top' : 'down'}.png"), ), Padding( padding: const EdgeInsets.only(right: 10.0), child: CircleAvatar(backgroundImage: userAvatarProvider(rankInfo?.user?.userAvatar), radius: 22), ), Expanded( child: Text( rankInfo.user.userName, style: Theme.of(context).textTheme.subtitle1, ), ), Text( "${rankInfo.user.score.toStringAsFixed(1)}分", style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor), ), ], ), ), ), Center( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.end, children: [ Container(width: 94, child: rankInfo.records.length >= 2 ? _buildRankHeaderWidget(rankInfo.records[1], 2) : _buildRankHeaderWidget(null, 2)), Container(width: 120, child: rankInfo.records.length >= 1 ? _buildRankHeaderWidget(rankInfo.records[0], 1) : _buildRankHeaderWidget(null, 1)), Container(width: 94, child: rankInfo.records.length >= 3 ? _buildRankHeaderWidget(rankInfo.records[2], 3) : _buildRankHeaderWidget(null, 3)), ], ), ), Space( height: 10, ), Divider( indent: 12.0, endIndent: 12.0, ) ], ); } Widget _buildRankHeaderWidget(User item, int rank) { return InkWell( onTap: item == null ? null : () => NavigatorUtil.goRankPeopleDetails(context, details: item), child: Column( children: [ rank == 1 ? Stack( alignment: Alignment.center, children: [ CircleAvatar(backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar), radius: 46.0), Image.asset( "lib/assets/img/rank_image_no$rank.png", ) ], ) : Stack( alignment: Alignment.topCenter, children: [ Positioned(top: 3, child: CircleAvatar(backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar), radius: 35.0)), Image.asset( "lib/assets/img/rank_image_no$rank.png", ) ], ), Space( height: 6, ), Text( item == null ? "虚位以待" : item.userName, style: Theme.of(context).textTheme.subtitle1, maxLines: 1, ), Space( height: 4, ), Row( mainAxisSize: MainAxisSize.min, children: [ Text( item == null ? "" : "${item.score.toStringAsFixed(1)}分", style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor), ), ], ), ], ), ); } Widget _buildLabelWidget(String title) { return Container( padding: EdgeInsets.fromLTRB(ui_padding, 10.0, ui_padding, 10.0), child: Text( title, style: Theme.of(context).textTheme.headline1, )); } Widget _buildSportItemWidget(game.GameInfoData item) { return Container( decoration: BoxDecoration( image: DecorationImage( image: CachedNetworkImageProvider(item.coverVertical), fit: BoxFit.cover, ), borderRadius: BorderRadius.all(Radius.circular(10.0))), child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.end, children: [ Container( width: double.infinity, padding: EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0), decoration: BoxDecoration( gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0x00000000), Color(0x912c2c2c), Color(0xCF000000)]), borderRadius: BorderRadius.vertical(bottom: Radius.circular(10.0)), ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.name, style: Theme.of(context).textTheme.headline3.copyWith(color: Colors.white), ), Space( height: 5, ), if (item.tags != null) Wrap( spacing: 4, runSpacing: 4, children: item.tags.map((e) => gameTag(context, e)).toList(), ), Space( height: 5, ), Text( "${item.userCount}人在玩", style: Theme.of(context).textTheme.bodyText1.copyWith(color: Colors.white), ), ], ), ), ], )); } Widget _buildRankItemWidget(User item, int index) { return Column( children: [ InkWell( onTap: item == null ? null : () => NavigatorUtil.goRankPeopleDetails(context, details: item), child: Container( child: Row( children: [ Container( width: 24, child: Text( "${index + 4}", style: Theme.of(context).textTheme.bodyText2, ), ), Padding( padding: const EdgeInsets.fromLTRB(0, 6, 10.0, 6), child: CircleAvatar( backgroundColor: Colors.transparent, backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar ?? ""), radius: 22), ), Expanded( child: Text( item == null ? "虚位以待" : item.userName, style: Theme.of(context).textTheme.subtitle1, maxLines: 1, ), ), Text( item == null ? "" : "${item.score.toStringAsFixed(1)}分", style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor), ), ], ), )), Divider(), ], ); } Widget _buildGuideWidget() { return Container( decoration: card(), margin: ui_margin_list, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => NavigatorUtil.goPage(context, (context) => GuidePage()), child: Stack(children: [ Padding( padding: EdgeInsets.all(ui_padding), child: Row( children: [ Image.asset("lib/assets/img/home_image_guidance.png"), Space( width: 12, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Text( "新手指引", style: Theme.of(context).textTheme.headline3.copyWith(fontSize: 14.0), ), Space( height: 4, ), Text("帮助您快速了解并使用相关功能", style: Theme.of(context).textTheme.bodyText1), ], ), ), ], ), ), Positioned( right: 0, child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () async { SharedPreferences preferences = await SharedPreferences.getInstance(); preferences.setBool(GUIDE, false); _valueNotifierGuide.value = false; }, child: Padding( padding: const EdgeInsets.all(12.0), child: Image.asset("lib/assets/img/btn_close_small.png"), ), ), ) ])), ); } Widget _buildSportHistoryWidget(game.GameInfoData item) { return GameItem( type: 1, imgUrl: item.cover, name: item.name, time: item.playTime, id: item.id, duration: item.durationTotal, data: item, bold: true, ); } } class _HeaderBackground extends CustomPainter { final Paint _paint = Paint() ..color = Color(0xff241D19) ..isAntiAlias = true; @override void paint(Canvas canvas, Size size) { Path path = Path(); path.lineTo(size.width, 0); path.lineTo(size.width, size.height); path.lineTo(0, size.height / 3 * 2); path.close(); canvas.drawPath(path, _paint); } @override bool shouldRepaint(CustomPainter oldDelegate) { return false; } }