home_info_page.dart 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. import 'dart:math';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_blue/flutter_blue.dart';
  5. import 'package:flutter_easyrefresh/easy_refresh.dart';
  6. import 'package:provider/provider.dart';
  7. import 'package:shared_preferences/shared_preferences.dart';
  8. import 'package:sport/bean/game.dart' as game;
  9. import 'package:sport/bean/game.dart';
  10. import 'package:sport/bean/sport_detail.dart';
  11. import 'package:sport/bean/sport_index.dart';
  12. import 'package:sport/bean/user.dart';
  13. import 'package:sport/constant/ui.dart' show ui_padding, ui_margin_list;
  14. import 'package:sport/pages/game/detail_bottom.dart';
  15. import 'package:sport/pages/game/game_detail.dart';
  16. import 'package:sport/pages/game/game_info.dart';
  17. import 'package:sport/pages/home/consume_page.dart';
  18. import 'package:sport/pages/home/duration_page.dart';
  19. import 'package:sport/pages/home/guide_page.dart';
  20. import 'package:sport/pages/home/sport_history_all_page.dart';
  21. import 'package:sport/pages/home/sport_history_page.dart';
  22. import 'package:sport/pages/home/sport_list_page.dart';
  23. import 'package:sport/pages/home/step_page.dart';
  24. import 'package:sport/pages/home/strength_page.dart';
  25. import 'package:sport/provider/bluetooth.dart';
  26. import 'package:sport/provider/lib/provider_widget.dart';
  27. import 'package:sport/provider/lib/view_state_lifecycle.dart';
  28. import 'package:sport/provider/sport_index_model.dart';
  29. import 'package:sport/provider/user_model.dart';
  30. import 'package:sport/router/navigator_util.dart';
  31. import 'package:sport/router/routes.dart';
  32. import 'package:sport/widgets/button_primary.dart';
  33. import 'package:sport/widgets/circular_percent_indicator.dart';
  34. import 'package:sport/widgets/decoration.dart';
  35. import 'package:sport/widgets/dialog/search_device.dart';
  36. import 'package:sport/widgets/dialog/share_popup.dart';
  37. import 'package:sport/widgets/error.dart';
  38. import 'package:sport/widgets/game_run.dart';
  39. import 'package:sport/widgets/image.dart';
  40. import 'package:sport/widgets/loading.dart';
  41. import 'package:sport/widgets/misc.dart';
  42. import 'package:sport/widgets/progress_bar.dart';
  43. import 'package:sport/widgets/space.dart';
  44. import 'package:sport/widgets/wave_painter.dart';
  45. class HomeInfoPage extends StatefulWidget {
  46. @override
  47. State<StatefulWidget> createState() => _PageState();
  48. }
  49. class _PageState extends ViewStateLifecycle<HomeInfoPage, SportIndexModel> with AutomaticKeepAliveClientMixin {
  50. String _rank;
  51. ValueNotifier<bool> _valueNotifierGuide = ValueNotifier(false);
  52. static const String GUIDE = "guide";
  53. EasyRefreshController _easyRefreshController;
  54. Bluetooth bluetooth;
  55. @override
  56. void initState() {
  57. super.initState();
  58. _easyRefreshController = EasyRefreshController();
  59. loadSetting();
  60. initList();
  61. bluetooth = Provider.of<Bluetooth>(context, listen: false);
  62. }
  63. void loadSetting() async {
  64. SharedPreferences preferences = await SharedPreferences.getInstance();
  65. _valueNotifierGuide.value = preferences.getBool(GUIDE) ?? true;
  66. }
  67. @override
  68. SportIndexModel createModel() => SportIndexModel(context);
  69. refresh() async {
  70. _rank = null;
  71. model.refresh();
  72. if (bluetooth?.isConnected == true) {
  73. bluetooth.queryDeviceStep();
  74. await Future.delayed(Duration(seconds: 5));
  75. model.refresh();
  76. }
  77. }
  78. initList() {
  79. model.valueNotifier.addListener(() {
  80. print("list--------------------------${model.valueNotifier.value}");
  81. if (model.valueNotifier.value.length > 0) {
  82. Navigator.push(context, new PopRoute(child: sharePopup(model.valueNotifier.value)));
  83. }
  84. });
  85. }
  86. @override
  87. Widget build(BuildContext context) {
  88. super.build(context);
  89. const _margin = EdgeInsets.fromLTRB(ui_padding, ui_padding, ui_padding, 0.0);
  90. return Scaffold(
  91. appBar: AppBar(
  92. titleSpacing: 0.0,
  93. backgroundColor: Theme.of(context).scaffoldBackgroundColor,
  94. title: _buildDeviceInfoWidget(),
  95. ),
  96. body: ProviderWidget<SportIndexModel>(
  97. model: this.model,
  98. onModelReady: (v) => v.initData(),
  99. builder: (BuildContext context, SportIndexModel model, Widget child) => EasyRefresh.custom(
  100. controller: _easyRefreshController,
  101. onRefresh: () => refresh(),
  102. header: buildClassicalHeader(),
  103. slivers: <Widget>[
  104. // // 暂时测试
  105. // if (model.isIdle && value.data != null)
  106. // SliverToBoxAdapter(
  107. // child: Stack(
  108. // fit: StackFit.passthrough,
  109. // children: <Widget>[
  110. // CustomPaint(
  111. // painter: _HeaderBackground(),
  112. // child: Container(
  113. // height: 116,
  114. // ),
  115. // ),
  116. // Container(
  117. // margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0),
  118. // decoration: card(),
  119. // height: 156,
  120. // child: _buildSportWidget(value.data),
  121. // )
  122. // ],
  123. // ),
  124. // ),
  125. // if (model.isIdle && value.data != null)
  126. // SliverToBoxAdapter(
  127. // child: _buildTopWidget(value.data),
  128. // ),
  129. if (model.isBusy)
  130. SliverToBoxAdapter(
  131. child: RequestLoadingWidget(),
  132. ),
  133. // if (value.data?.lastGame != null)
  134. // SliverToBoxAdapter(
  135. // child: _buildSportHistoryWidget(value.data.lastGame),
  136. // ),
  137. // SliverToBoxAdapter(
  138. // child: ValueListenableBuilder<bool>(
  139. // builder: (BuildContext context, value, Widget child) => value ? _buildGuideWidget() : Container(), valueListenable: _valueNotifierGuide),
  140. // ),
  141. SliverToBoxAdapter(
  142. child: Container(
  143. height: 250,
  144. child: Column(
  145. children: <Widget>[
  146. Container(
  147. height: 182,
  148. child: Selector<Bluetooth, BluetoothDevice>(
  149. selector: (_, bluetooth) => bluetooth.device,
  150. builder: (_, device, ___) {
  151. return device == null
  152. ? CachedNetworkImage(
  153. height: 182,
  154. imageUrl: "http://static.ouj.com/hiyd/fb337b03b0929085f1d07c036cd40ec4_size750x281_115600.png",
  155. )
  156. : StreamBuilder<BluetoothDeviceState>(
  157. stream: device.state,
  158. initialData: BluetoothDeviceState.connecting,
  159. builder: (c, snapshot) => CachedNetworkImage(
  160. height: 182,
  161. imageUrl: snapshot.data == BluetoothDeviceState.connected
  162. ? "http://static.ouj.com/hiyd/a856a17e1cd2006a6cd6e7df6b7f1357_size750x285_116051.png"
  163. : "http://static.ouj.com/hiyd/fb337b03b0929085f1d07c036cd40ec4_size750x281_115600.png",
  164. ));
  165. })),
  166. Space(
  167. height: 12,
  168. ),
  169. Selector<Bluetooth, BluetoothDevice>(
  170. selector: (_, bluetooth) => bluetooth.device,
  171. builder: (_, device, ___) {
  172. return device != null
  173. ? PrimaryButton(
  174. content: "开始运动",
  175. width: 174,
  176. height: 40,
  177. // bold: true,
  178. callback: () {
  179. showDialog(context: context, child: SportListPage());
  180. },
  181. )
  182. : PrimaryButton(
  183. content: "添加设备",
  184. width: 174,
  185. height: 40,
  186. callback: () {
  187. showDialog(context: context, child: SearchDeviceDialog());
  188. },
  189. );
  190. }),
  191. Space(
  192. height: 12,
  193. ),
  194. ],
  195. ),
  196. ),
  197. ),
  198. SliverToBoxAdapter(
  199. child: Container(
  200. margin: _margin,
  201. child: Row(
  202. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  203. children: <Widget>[
  204. Expanded(
  205. child: GestureDetector(
  206. onTap: () => NavigatorUtil.goPage(context, (context) => StepPage()),
  207. behavior: HitTestBehavior.opaque,
  208. child: Container(
  209. decoration: circular(),
  210. child: Column(
  211. crossAxisAlignment: CrossAxisAlignment.start,
  212. children: <Widget>[
  213. RichText(
  214. text: TextSpan(style: DefaultTextStyle.of(context).style, children: <InlineSpan>[
  215. WidgetSpan(
  216. alignment: PlaceholderAlignment.middle,
  217. child: Image.asset(
  218. 'lib/assets/img/home_title_steps.png',
  219. ),
  220. ),
  221. TextSpan(text: '\t今日步数', style: Theme.of(context).textTheme.subtitle1)
  222. ]),
  223. ),
  224. Padding(
  225. padding: const EdgeInsets.all(20.0),
  226. child: Center(
  227. child: Row(
  228. children: <Widget>[
  229. Text(
  230. "${model.data?.today?.step ?? 0}",
  231. style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0),
  232. strutStyle: fixedLine,
  233. ),
  234. Text(
  235. "步",
  236. style: Theme.of(context).textTheme.subtitle2,
  237. strutStyle: fixedLine,
  238. )
  239. ],
  240. crossAxisAlignment: CrossAxisAlignment.end,
  241. mainAxisSize: MainAxisSize.min,
  242. ),
  243. ),
  244. )
  245. ],
  246. ),
  247. padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0),
  248. ),
  249. ),
  250. ),
  251. SizedBox(
  252. width: 12.0,
  253. ),
  254. Expanded(
  255. child: GestureDetector(
  256. onTap: () => NavigatorUtil.goPage(context, (context) => ConsumePage()),
  257. behavior: HitTestBehavior.opaque,
  258. child: Container(
  259. decoration: circular(),
  260. child: Column(
  261. crossAxisAlignment: CrossAxisAlignment.start,
  262. children: <Widget>[
  263. RichText(
  264. text: TextSpan(style: DefaultTextStyle.of(context).style, children: <InlineSpan>[
  265. WidgetSpan(
  266. alignment: PlaceholderAlignment.middle,
  267. child: Image.asset(
  268. 'lib/assets/img/home_title_consume.png',
  269. ),
  270. ),
  271. TextSpan(text: '\t今日消耗', style: Theme.of(context).textTheme.subtitle1)
  272. ]),
  273. ),
  274. Padding(
  275. padding: const EdgeInsets.all(20.0),
  276. child: Center(
  277. child: Row(
  278. children: <Widget>[
  279. Text(
  280. "${model.data?.today?.consume ?? 0}",
  281. style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0),
  282. strutStyle: fixedLine,
  283. ),
  284. Text(
  285. "卡",
  286. style: Theme.of(context).textTheme.subtitle2,
  287. strutStyle: fixedLine,
  288. )
  289. ],
  290. crossAxisAlignment: CrossAxisAlignment.end,
  291. mainAxisSize: MainAxisSize.min,
  292. ),
  293. ),
  294. )
  295. ],
  296. ),
  297. padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0),
  298. ),
  299. ),
  300. ),
  301. ],
  302. ),
  303. ),
  304. ),
  305. SliverToBoxAdapter(
  306. child: GestureDetector(
  307. onTap: () => NavigatorUtil.goPage(context, (context) => DurationPage()),
  308. child: Container(
  309. margin: _margin,
  310. decoration: circular(),
  311. child: Column(
  312. children: <Widget>[
  313. Padding(
  314. padding: const EdgeInsets.all(8.0),
  315. child: Row(
  316. children: <Widget>[
  317. RichText(
  318. text: TextSpan(style: DefaultTextStyle.of(context).style, children: <InlineSpan>[
  319. WidgetSpan(
  320. alignment: PlaceholderAlignment.middle,
  321. child: Image.asset(
  322. 'lib/assets/img/home_title_duration.png',
  323. ),
  324. ),
  325. TextSpan(text: '\t运动目标', style: Theme.of(context).textTheme.subtitle1)
  326. ]),
  327. ),
  328. arrowRight4()
  329. ],
  330. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  331. ),
  332. ),
  333. Divider(
  334. height: 0.5,
  335. ),
  336. Padding(
  337. padding: const EdgeInsets.fromLTRB(15.0, 8.0, 15.0, 8.0),
  338. child: Row(
  339. children: <Widget>[
  340. Padding(
  341. padding: const EdgeInsets.symmetric(vertical: 3.0),
  342. child: CircularPercentIndicator(
  343. radius: 100.0,
  344. lineWidth: 10.0,
  345. percent: 1.0 * (model?.data?.today?.value(model.data?.target?.type) ?? 0) / (model.data?.target?.valueTarget ?? 0.01),
  346. center: Column(
  347. children: <Widget>[
  348. SizedBox(
  349. height: 10.0,
  350. ),
  351. Text(
  352. "${model.data?.today?.value(model.data?.target?.type) ?? 0}",
  353. style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0),
  354. strutStyle: fixedLine,
  355. ),
  356. Text(
  357. "${model.data?.target?.label}",
  358. style: Theme.of(context).textTheme.subtitle2,
  359. strutStyle: fixedLine,
  360. )
  361. ],
  362. mainAxisSize: MainAxisSize.min,
  363. ),
  364. animation: true,
  365. animationDuration: 1000,
  366. animateFromLastPercent: true,
  367. startAngle: 200.0,
  368. arcType: ArcType.CUSTOM,
  369. backgroundColor: Color(0xfff1f1f1),
  370. circularStrokeCap: CircularStrokeCap.round,
  371. rotateLinearGradient: true,
  372. linearGradient: LinearGradient(
  373. colors: <Color>[Color(0xff8DF7FF), Color(0xff16A2FF)],
  374. ),
  375. ),
  376. ),
  377. SizedBox(
  378. width: 15.0,
  379. ),
  380. Column(
  381. crossAxisAlignment: CrossAxisAlignment.start,
  382. children: <Widget>[
  383. Text(
  384. "今日目标:${model.data?.target?.valueTarget ?? 0} ${model.data?.target?.label}",
  385. style: Theme.of(context).textTheme.subtitle1,
  386. ),
  387. SizedBox(
  388. height: 8.0,
  389. ),
  390. Text(
  391. "月达标天数:${model.data?.target?.monthFinish ?? 0}天",
  392. style: Theme.of(context).textTheme.subtitle1,
  393. )
  394. ],
  395. )
  396. ],
  397. ),
  398. ),
  399. ],
  400. ),
  401. ),
  402. ),
  403. ),
  404. SliverToBoxAdapter(
  405. child: GestureDetector(
  406. onTap: () => NavigatorUtil.goPage(context, (context) => StrengthPage()),
  407. child: Container(
  408. margin: _margin,
  409. decoration: circular(),
  410. child: Column(
  411. children: <Widget>[
  412. Padding(
  413. padding: const EdgeInsets.all(8.0),
  414. child: Row(
  415. children: <Widget>[
  416. RichText(
  417. text: TextSpan(style: DefaultTextStyle.of(context).style, children: <InlineSpan>[
  418. WidgetSpan(
  419. alignment: PlaceholderAlignment.middle,
  420. child: Image.asset(
  421. 'lib/assets/img/home_title_strength.png',
  422. ),
  423. ),
  424. TextSpan(text: '\t运动强度', style: Theme.of(context).textTheme.subtitle1)
  425. ]),
  426. ),
  427. arrowRight4()
  428. ],
  429. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  430. ),
  431. ),
  432. Divider(
  433. height: 0.5,
  434. ),
  435. Padding(
  436. padding: const EdgeInsets.fromLTRB(15.0, 8.0, 15.0, 8.0),
  437. child: Row(
  438. children: <Widget>[
  439. CircularProgressBar(
  440. percent: (model.data?.today?.strength ?? 0) / 12.0,
  441. center: Column(
  442. children: <Widget>[
  443. SizedBox(
  444. height: 10.0,
  445. ),
  446. Text(
  447. "${model.data?.today?.strength?.toStringAsFixed(1) ?? 0}",
  448. style: Theme.of(context).textTheme.headline2.copyWith(fontSize: 26.0),
  449. strutStyle: fixedLine,
  450. ),
  451. Text(
  452. "卡/分",
  453. style: Theme.of(context).textTheme.subtitle2,
  454. strutStyle: fixedLine,
  455. )
  456. ],
  457. mainAxisSize: MainAxisSize.min,
  458. ),
  459. ),
  460. SizedBox(
  461. width: 15.0,
  462. ),
  463. Column(
  464. crossAxisAlignment: CrossAxisAlignment.start,
  465. children: <Widget>[
  466. Text(
  467. "强度评级:${model.data?.today?.strengthLabel()}",
  468. style: Theme.of(context).textTheme.subtitle1,
  469. ),
  470. SizedBox(
  471. height: 8.0,
  472. ),
  473. Text(
  474. "已经运动:${(model.data?.today?.duration ?? 0) == 0 ? '无' : "${model.data?.today?.duration}分钟"}",
  475. style: Theme.of(context).textTheme.subtitle1,
  476. )
  477. ],
  478. )
  479. ],
  480. ),
  481. ),
  482. ],
  483. ),
  484. ),
  485. ),
  486. ),
  487. SliverToBoxAdapter(
  488. child: Container(
  489. padding: EdgeInsets.fromLTRB(12.0,16.0, 12.0,4.0),
  490. child: Row(
  491. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  492. children: <Widget>[
  493. Text(
  494. "今日运动",
  495. style: Theme.of(context).textTheme.headline1.copyWith(fontSize: 16.0),
  496. ),
  497. GestureDetector(
  498. onTap: () => NavigatorUtil.goPage(context, (context) => SportHistoryAllPage()),
  499. child: RichText(
  500. text: TextSpan(style: DefaultTextStyle.of(context).style, children: <InlineSpan>[
  501. TextSpan(text: '更多\t', style: Theme.of(context).textTheme.bodyText1),
  502. WidgetSpan(
  503. alignment: PlaceholderAlignment.middle,
  504. child: arrowRight4(),
  505. ),
  506. ]),
  507. ),
  508. ),
  509. ],
  510. )),
  511. ),
  512. // if(model.data?.records?.isNotEmpty == true)
  513. SliverPadding(
  514. padding: const EdgeInsets.symmetric(horizontal: ui_padding),
  515. sliver: SliverList(
  516. delegate: SliverChildBuilderDelegate((content, index) {
  517. if (index == 0) {
  518. return _buildSportRecord(RecordsToday(id: -1, name: "跑步训练", userCount: 144234, cover: "lib/assets/img/home_game_run.png"), null, 0);
  519. }
  520. var item = model.data?.records[index - 1];
  521. var game = model.data?.games?.firstWhere((element) => element.id == item.gameId);
  522. return GestureDetector(
  523. onTap: () => NavigatorUtil.goPage(context, (context) => SportHistoryPage(game)), child: _buildSportRecord(item, game, 1));
  524. }, childCount: (model.data?.records?.length ?? 0) + 1),
  525. )),
  526. // SliverToBoxAdapter(
  527. // child: Container(
  528. // margin: const EdgeInsets.fromLTRB(ui_padding, 14.0, ui_padding, 14.0),
  529. // decoration: circular(),
  530. // child: RequestErrorWidget(
  531. // null,
  532. // assets: RequestErrorWidget.ASSETS_NO_MOTION,
  533. // msg: "您还未进行任何运动",
  534. // ))),
  535. // if (value.data?.games != null)
  536. // SliverToBoxAdapter(
  537. // child: _buildLabelWidget("SPORTS"),
  538. // ),
  539. // if (value.data?.games != null)
  540. // SliverPadding(
  541. // padding: const EdgeInsets.symmetric(horizontal: ui_padding),
  542. // sliver: SliverGrid(
  543. // gridDelegate:
  544. // SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, childAspectRatio: 170.0 / 226),
  545. // delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
  546. // return InkWell(
  547. // onTap: () => NavigatorUtil.goGameDetails(context, details: value.data.games[index]),
  548. // child: _buildSportItemWidget(value.data.games[index]));
  549. // }, childCount: value.data.games.length),
  550. // ),
  551. // ),
  552. // if (value.data?.rank != null)
  553. // SliverToBoxAdapter(
  554. // child: Padding(
  555. // padding: const EdgeInsets.only(top: 12.0),
  556. // child: Row(
  557. // children: <Widget>[
  558. // Expanded(child: _buildLabelWidget("CHARTS")),
  559. // PopupMenuButton(
  560. // onSelected: (v) {
  561. // value.game(v);
  562. // },
  563. // itemBuilder: (BuildContext context) {
  564. // return divideMenus(value.data.games.map((e) => menuItemSelected(e.id, e.name, e.id == value.gameId)).toList());
  565. // },
  566. // child: Row(
  567. // mainAxisSize: MainAxisSize.min,
  568. // children: <Widget>[
  569. // Text(
  570. // value.data.games.where((element) => element.id == value.gameId).map((e) => e.name).first,
  571. // style: Theme.of(context).textTheme.bodyText1,
  572. // ),
  573. // Padding(
  574. // padding: const EdgeInsets.all(6.0),
  575. // child: arrowBottom(),
  576. // )
  577. // ],
  578. // ),
  579. // ),
  580. // SizedBox(
  581. // width: 12,
  582. // ),
  583. // NotificationListener<AreaNotification>(
  584. // onNotification: (n) {
  585. // _rank = n.rank;
  586. // request(context, () async {
  587. // await value.area(
  588. // n.all ? null : n.provinceId, n.all ? null : n.province ? null : n.cityId, n.all ? null : n.province ? null : n.districtId);
  589. // });
  590. // return true;
  591. // },
  592. // child: AreaPage(
  593. // area: _rank,
  594. // ),
  595. // )
  596. // ],
  597. // ),
  598. // ),
  599. // ),
  600. // if (value.data?.rank != null)
  601. // SliverToBoxAdapter(
  602. // child: _buildRankWidget(value.data.rank),
  603. // ),
  604. // if (value.data?.rank != null)
  605. // SliverPadding(
  606. // padding: const EdgeInsets.symmetric(horizontal: ui_padding),
  607. // sliver: SliverList(
  608. // delegate: SliverChildBuilderDelegate((content, index) {
  609. // return (value.data.rank.records.length - 3) > index
  610. // ? _buildRankItemWidget(value.data.rank.records[index + 3], index)
  611. // : _buildRankItemWidget(null, index);
  612. // }, childCount: 7),
  613. // )),
  614. // if (value.data?.rank != null)
  615. // SliverToBoxAdapter(
  616. // child: Center(
  617. // child: InkWell(
  618. // onTap: () {
  619. // NavigatorUtil.goRankDetails(context, value.data.rank.rank.gameId, 1);
  620. // },
  621. // child: Padding(
  622. // padding: const EdgeInsets.all(16.0),
  623. // child: Text(
  624. // "查看完整榜单",
  625. // style: Theme.of(context).textTheme.bodyText1.copyWith(color: Color(0xffcecece)),
  626. // ),
  627. // ),
  628. // )),
  629. // ),
  630. SliverToBoxAdapter(
  631. child: SizedBox(
  632. height: 12,
  633. ),
  634. ),
  635. ],
  636. ),
  637. ),
  638. );
  639. }
  640. @override
  641. bool get wantKeepAlive => true;
  642. Widget _buildDeviceInfoWidget() {
  643. return Padding(
  644. padding: const EdgeInsets.only(left: ui_padding),
  645. child: Selector<Bluetooth, BluetoothDevice>(
  646. selector: (_, bluetooth) => bluetooth.device,
  647. builder: (_, device, ___) {
  648. return device == null ? _buildDeviceInfoConnectFailWidget() : _buildDeviceInfoStateWidget(device);
  649. }),
  650. );
  651. }
  652. Widget _buildDeviceInfoStateWidget(BluetoothDevice device) {
  653. return StreamBuilder<BluetoothDeviceState>(
  654. stream: device.state,
  655. initialData: BluetoothDeviceState.connecting,
  656. builder: (c, snapshot) => _buildDeviceInfoConnectedWidget(snapshot.data == BluetoothDeviceState.connected));
  657. }
  658. Widget _buildDeviceInfoConnectedWidget(bool connect) {
  659. const Color _color = Color(0xffFF5B1D);
  660. return GestureDetector(
  661. behavior: HitTestBehavior.opaque,
  662. onTap: () => NavigatorUtil.go(context, Routes.deviceInfo),
  663. child: Row(
  664. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  665. children: <Widget>[
  666. Row(
  667. children: <Widget>[
  668. Container(
  669. width: 10,
  670. height: 10,
  671. decoration: BoxDecoration(shape: BoxShape.circle, color: connect ? Color(0xff00DC42) : _color),
  672. ),
  673. SizedBox(
  674. width: 6.0,
  675. ),
  676. Text("设备:${bluetooth?.device?.name}", style: Theme.of(context).textTheme.subtitle1, strutStyle: StrutStyle(forceStrutHeight: true)),
  677. ],
  678. ),
  679. ValueListenableBuilder(
  680. valueListenable: Provider.of<Bluetooth>(context, listen: false).electricityNotifier,
  681. builder: (_, data, ___) => Container(
  682. padding: EdgeInsets.fromLTRB(15.0, 6.0, 10.0, 6.0),
  683. decoration: BoxDecoration(
  684. color: Colors.white,
  685. borderRadius: BorderRadius.only(topLeft: Radius.circular(30), bottomLeft: Radius.circular(30)),
  686. border: Border.all(
  687. color: Theme.of(context).scaffoldBackgroundColor,
  688. width: .5,
  689. ),
  690. ),
  691. child: Row(
  692. children: <Widget>[
  693. Stack(
  694. alignment: Alignment.bottomCenter,
  695. children: <Widget>[
  696. Image.asset(
  697. "lib/assets/img/battery.png",
  698. color: const Color(0xff241D19),
  699. ),
  700. Container(
  701. width: 5.5,
  702. height: 8 * (data / 100),
  703. margin: EdgeInsets.only(bottom: 2, right: 0.5),
  704. decoration: BoxDecoration(color: const Color(0xff241D19)),
  705. ),
  706. ],
  707. ),
  708. const SizedBox(
  709. width: 5,
  710. ),
  711. Text(
  712. "$data%",
  713. style: Theme.of(context).textTheme.subtitle1,
  714. )
  715. ],
  716. ),
  717. )),
  718. ],
  719. ));
  720. }
  721. Widget _buildDeviceInfoConnectFailWidget() {
  722. const Color _color = Color(0xffFF5B1D);
  723. return Row(
  724. children: <Widget>[
  725. Container(
  726. width: 10,
  727. height: 10,
  728. decoration: BoxDecoration(shape: BoxShape.circle, color: _color),
  729. ),
  730. SizedBox(
  731. width: 6.0,
  732. ),
  733. Text("未连接设备",
  734. style: TextStyle(
  735. color: _color,
  736. fontSize: 14,
  737. ),
  738. strutStyle: StrutStyle(forceStrutHeight: true)),
  739. ],
  740. );
  741. }
  742. Widget _buildDeviceWidget() {
  743. return Column(
  744. mainAxisAlignment: MainAxisAlignment.center,
  745. crossAxisAlignment: CrossAxisAlignment.center,
  746. children: <Widget>[
  747. Image.asset("lib/assets/img/home_image_connect.png"),
  748. Space(
  749. height: 8,
  750. ),
  751. Text(
  752. "请先连接设备鞋子",
  753. style: Theme.of(context).textTheme.bodyText1,
  754. ),
  755. Space(
  756. height: 12,
  757. ),
  758. PrimaryButton(
  759. content: "搜索设备",
  760. width: 132,
  761. height: 35,
  762. callback: () {
  763. showDialog(context: context, child: SearchDeviceDialog());
  764. },
  765. )
  766. ],
  767. );
  768. }
  769. Widget _buildTopWidget(SportIndex item) {
  770. return Stack(
  771. fit: StackFit.passthrough,
  772. children: <Widget>[
  773. CustomPaint(
  774. painter: _HeaderBackground(),
  775. child: Container(
  776. height: 116,
  777. ),
  778. ),
  779. Container(
  780. margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0),
  781. decoration: card(),
  782. height: 156,
  783. child: Selector<Bluetooth, BluetoothDevice>(
  784. selector: (_, bluetooth) => bluetooth.device,
  785. builder: (_, device, ___) {
  786. return device != null ? _buildSportWidget(item) : _buildDeviceWidget();
  787. }),
  788. )
  789. ],
  790. );
  791. }
  792. Widget _buildSportWidget(SportIndex item) {
  793. return InkWell(
  794. onTap: () => NavigatorUtil.go(context, Routes.sportDetail),
  795. child: Stack(
  796. alignment: Alignment.center,
  797. children: <Widget>[
  798. Row(
  799. children: <Widget>[
  800. Expanded(
  801. flex: 5,
  802. child: Padding(
  803. padding: const EdgeInsets.all(16.0),
  804. child: ValueListenableBuilder(
  805. valueListenable: Provider.of<UserModel>(context, listen: false).durationTarget,
  806. builder: (_, v, __) => ProgressManager(min(.9, v <= 0 ? 0 : item.duration / v))),
  807. )),
  808. Expanded(
  809. flex: 6,
  810. child: Column(
  811. mainAxisAlignment: MainAxisAlignment.center,
  812. crossAxisAlignment: CrossAxisAlignment.start,
  813. children: <Widget>[
  814. Text(
  815. "今日已运动",
  816. style: Theme.of(context).textTheme.subtitle1,
  817. ),
  818. SizedBox(
  819. height: 5,
  820. ),
  821. RichText(
  822. text: TextSpan(style: Theme.of(context).textTheme.subtitle2, children: <InlineSpan>[
  823. TextSpan(text: '${item.duration}', style: Theme.of(context).textTheme.subtitle2.copyWith(fontWeight: FontWeight.bold, fontSize: 40)),
  824. TextSpan(text: '分钟'),
  825. ]),
  826. ),
  827. SizedBox(
  828. height: 5,
  829. ),
  830. Text("共消耗 ${item.consume} kal", style: Theme.of(context).textTheme.bodyText1),
  831. SizedBox(
  832. height: 5,
  833. ),
  834. Text(item.beyond > 0 ? "已超越了${item.beyond}人,请再接再厉" : "${item.inspire}", style: Theme.of(context).textTheme.bodyText1),
  835. ],
  836. ),
  837. )
  838. ],
  839. ),
  840. Positioned(
  841. right: 0,
  842. child: Image.asset("lib/assets/img/home_icon_details.png"),
  843. )
  844. ],
  845. ),
  846. );
  847. }
  848. Widget _buildSportRecord(RecordsToday item, GameInfoData game, int type) {
  849. return Container(
  850. margin: const EdgeInsets.only(top: ui_padding),
  851. decoration: circular(),
  852. child: Column(
  853. children: <Widget>[
  854. Padding(
  855. padding: const EdgeInsets.fromLTRB(ui_padding, 14.0, ui_padding, 14.0),
  856. child: Row(
  857. children: <Widget>[
  858. item.cover?.startsWith("http") == true
  859. ? GestureDetector(
  860. onTap: () => NavigatorUtil.goPage(context, (context) => GameDetailsPage(game)),
  861. child: ClipRRect(
  862. child: CachedNetworkImage(
  863. width: 50,
  864. height: 50,
  865. imageUrl: item.cover ?? '',
  866. fit: BoxFit.cover,
  867. ),
  868. // 也可控件一边圆角大小
  869. borderRadius: new BorderRadius.all(Radius.circular(6.0)),
  870. ),
  871. )
  872. : Image.asset(
  873. item.cover,
  874. width: 50.0,
  875. height: 50.0,
  876. ),
  877. SizedBox(
  878. width: ui_padding,
  879. ),
  880. Expanded(
  881. child: Column(
  882. children: <Widget>[
  883. Text(item.name, style: Theme.of(context).textTheme.headline1.copyWith(fontSize: 16.0)),
  884. const SizedBox(
  885. height: 6.0,
  886. ),
  887. Text(
  888. "${item.userCount}人在练",
  889. style: Theme.of(context).textTheme.bodyText1,
  890. ),
  891. ],
  892. crossAxisAlignment: CrossAxisAlignment.start,
  893. )),
  894. PrimaryButton(
  895. width: 72,
  896. height: 35,
  897. content: type == 0 ? "跑步" : "继续",
  898. callback: type == 0
  899. ? () {
  900. startRun(context);
  901. }
  902. : () => startGame(context, game),
  903. )
  904. ],
  905. ),
  906. ),
  907. Divider(
  908. height: 0.5,
  909. ),
  910. Padding(
  911. padding: const EdgeInsets.fromLTRB(ui_padding, 15.0, ui_padding, 15.0),
  912. child: Row(
  913. children: <Widget>[
  914. Column(
  915. children: <Widget>[
  916. Text(type == 0 ? "38.0" : "${item.durationTotalMinute}", style: Theme.of(context).textTheme.headline1),
  917. const SizedBox(
  918. height: 2.0,
  919. ),
  920. Text(
  921. type == 0 ? "总里程(公里)" : "总时长(分钟)",
  922. style: Theme.of(context).textTheme.bodyText1,
  923. ),
  924. ],
  925. ),
  926. Column(
  927. children: <Widget>[
  928. type == 0
  929. ? RichText(
  930. text: TextSpan(style: Theme.of(context).textTheme.headline1, children: <InlineSpan>[
  931. TextSpan(text: "30′"),
  932. WidgetSpan(
  933. alignment: PlaceholderAlignment.bottom,
  934. child: Padding(
  935. padding: const EdgeInsets.only(bottom: 1.0),
  936. child: Text("38″", style: Theme.of(context).textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600)),
  937. ))
  938. ]),
  939. )
  940. : Text("${item.todayScoreMax.toStringAsFixed(1)}", style: Theme.of(context).textTheme.headline1),
  941. const SizedBox(
  942. height: 2.0,
  943. ),
  944. Text(
  945. type == 0 ? "总时长" : "今日最高评分",
  946. style: Theme.of(context).textTheme.bodyText1,
  947. ),
  948. ],
  949. ),
  950. Column(
  951. children: <Widget>[
  952. type == 0
  953. ? RichText(
  954. text: TextSpan(style: Theme.of(context).textTheme.headline1, children: <InlineSpan>[
  955. TextSpan(text: "12′"),
  956. WidgetSpan(
  957. alignment: PlaceholderAlignment.bottom,
  958. child: Padding(
  959. padding: const EdgeInsets.only(bottom: 1.0),
  960. child: Text("38″", style: Theme.of(context).textTheme.subtitle1.copyWith(fontWeight: FontWeight.w600)),
  961. ),
  962. )
  963. ]),
  964. )
  965. : Text("${item.times}", style: Theme.of(context).textTheme.headline1),
  966. const SizedBox(
  967. height: 2.0,
  968. ),
  969. Text(
  970. type == 0 ? "平均配速" : "运动次数(次)",
  971. style: Theme.of(context).textTheme.bodyText1,
  972. ),
  973. ],
  974. )
  975. ],
  976. mainAxisAlignment: MainAxisAlignment.spaceAround,
  977. ),
  978. ),
  979. ],
  980. ));
  981. }
  982. Widget _buildRankWidget(RankInfo rankInfo) {
  983. return Column(
  984. children: <Widget>[
  985. if (rankInfo?.user != null)
  986. GestureDetector(
  987. onTap: () => NavigatorUtil.goRankPeopleDetails(context, details: rankInfo.user),
  988. child: Container(
  989. margin: EdgeInsets.all(ui_padding),
  990. padding: EdgeInsets.all(ui_padding),
  991. decoration: card(),
  992. child: Row(
  993. children: <Widget>[
  994. Padding(
  995. padding: const EdgeInsets.only(right: 8),
  996. child: Text(
  997. "${rankInfo.user.position}",
  998. ),
  999. ),
  1000. if (rankInfo.user.up != 0 && rankInfo.user.upNew != 0)
  1001. Padding(
  1002. padding: const EdgeInsets.only(right: 8),
  1003. child: Image.asset("lib/assets/img/rand_icon_${rankInfo.user.up >= 0 ? 'top' : 'down'}.png"),
  1004. ),
  1005. Padding(
  1006. padding: const EdgeInsets.only(right: 10.0),
  1007. child: CircleAvatar(backgroundImage: userAvatarProvider(rankInfo?.user?.userAvatar), radius: 22),
  1008. ),
  1009. Expanded(
  1010. child: Text(
  1011. rankInfo.user.userName,
  1012. style: Theme.of(context).textTheme.subtitle1,
  1013. ),
  1014. ),
  1015. Text(
  1016. "${rankInfo.user.score.toStringAsFixed(1)}分",
  1017. style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
  1018. ),
  1019. ],
  1020. ),
  1021. ),
  1022. ),
  1023. Center(
  1024. child: Row(
  1025. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  1026. crossAxisAlignment: CrossAxisAlignment.end,
  1027. children: <Widget>[
  1028. Container(width: 94, child: rankInfo.records.length >= 2 ? _buildRankHeaderWidget(rankInfo.records[1], 2) : _buildRankHeaderWidget(null, 2)),
  1029. Container(width: 120, child: rankInfo.records.length >= 1 ? _buildRankHeaderWidget(rankInfo.records[0], 1) : _buildRankHeaderWidget(null, 1)),
  1030. Container(width: 94, child: rankInfo.records.length >= 3 ? _buildRankHeaderWidget(rankInfo.records[2], 3) : _buildRankHeaderWidget(null, 3)),
  1031. ],
  1032. ),
  1033. ),
  1034. Space(
  1035. height: 10,
  1036. ),
  1037. Divider(
  1038. indent: 12.0,
  1039. endIndent: 12.0,
  1040. )
  1041. ],
  1042. );
  1043. }
  1044. Widget _buildRankHeaderWidget(User item, int rank) {
  1045. return InkWell(
  1046. onTap: item == null ? null : () => NavigatorUtil.goRankPeopleDetails(context, details: item),
  1047. child: Column(
  1048. children: <Widget>[
  1049. rank == 1
  1050. ? Stack(
  1051. alignment: Alignment.center,
  1052. children: <Widget>[
  1053. CircleAvatar(backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar), radius: 46.0),
  1054. Image.asset(
  1055. "lib/assets/img/rank_image_no$rank.png",
  1056. )
  1057. ],
  1058. )
  1059. : Stack(
  1060. alignment: Alignment.topCenter,
  1061. children: <Widget>[
  1062. Positioned(top: 3, child: CircleAvatar(backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar), radius: 35.0)),
  1063. Image.asset(
  1064. "lib/assets/img/rank_image_no$rank.png",
  1065. )
  1066. ],
  1067. ),
  1068. Space(
  1069. height: 6,
  1070. ),
  1071. Text(
  1072. item == null ? "虚位以待" : item.userName,
  1073. style: Theme.of(context).textTheme.subtitle1,
  1074. maxLines: 1,
  1075. ),
  1076. Space(
  1077. height: 4,
  1078. ),
  1079. Row(
  1080. mainAxisSize: MainAxisSize.min,
  1081. children: <Widget>[
  1082. Text(
  1083. item == null ? "" : "${item.score.toStringAsFixed(1)}分",
  1084. style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
  1085. ),
  1086. ],
  1087. ),
  1088. ],
  1089. ),
  1090. );
  1091. }
  1092. Widget _buildLabelWidget(String title) {
  1093. return Container(
  1094. padding: EdgeInsets.fromLTRB(ui_padding, 10.0, ui_padding, 10.0),
  1095. child: Text(
  1096. title,
  1097. style: Theme.of(context).textTheme.headline1,
  1098. ));
  1099. }
  1100. Widget _buildSportItemWidget(game.GameInfoData item) {
  1101. return Container(
  1102. decoration: BoxDecoration(
  1103. image: DecorationImage(
  1104. image: CachedNetworkImageProvider(item.coverVertical),
  1105. fit: BoxFit.cover,
  1106. ),
  1107. borderRadius: BorderRadius.all(Radius.circular(10.0))),
  1108. child: Column(
  1109. mainAxisSize: MainAxisSize.min,
  1110. mainAxisAlignment: MainAxisAlignment.end,
  1111. children: <Widget>[
  1112. Container(
  1113. width: double.infinity,
  1114. padding: EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0),
  1115. decoration: BoxDecoration(
  1116. gradient:
  1117. LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0x00000000), Color(0x912c2c2c), Color(0xCF000000)]),
  1118. borderRadius: BorderRadius.vertical(bottom: Radius.circular(10.0)),
  1119. ),
  1120. child: Column(
  1121. mainAxisSize: MainAxisSize.min,
  1122. crossAxisAlignment: CrossAxisAlignment.start,
  1123. children: <Widget>[
  1124. Text(
  1125. item.name,
  1126. style: Theme.of(context).textTheme.headline3.copyWith(color: Colors.white),
  1127. ),
  1128. Space(
  1129. height: 5,
  1130. ),
  1131. if (item.tags != null)
  1132. Wrap(
  1133. spacing: 4,
  1134. runSpacing: 4,
  1135. children: item.tags.map((e) => gameTag(context, e)).toList(),
  1136. ),
  1137. Space(
  1138. height: 5,
  1139. ),
  1140. Text(
  1141. "${item.userCount}人在玩",
  1142. style: Theme.of(context).textTheme.bodyText1.copyWith(color: Colors.white),
  1143. ),
  1144. ],
  1145. ),
  1146. ),
  1147. ],
  1148. ));
  1149. }
  1150. Widget _buildRankItemWidget(User item, int index) {
  1151. return Column(
  1152. children: <Widget>[
  1153. InkWell(
  1154. onTap: item == null ? null : () => NavigatorUtil.goRankPeopleDetails(context, details: item),
  1155. child: Container(
  1156. child: Row(
  1157. children: <Widget>[
  1158. Container(
  1159. width: 24,
  1160. child: Text(
  1161. "${index + 4}",
  1162. style: Theme.of(context).textTheme.bodyText2,
  1163. ),
  1164. ),
  1165. Padding(
  1166. padding: const EdgeInsets.fromLTRB(0, 6, 10.0, 6),
  1167. child: CircleAvatar(
  1168. backgroundColor: Colors.transparent, backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar ?? ""), radius: 22),
  1169. ),
  1170. Expanded(
  1171. child: Text(
  1172. item == null ? "虚位以待" : item.userName,
  1173. style: Theme.of(context).textTheme.subtitle1,
  1174. maxLines: 1,
  1175. ),
  1176. ),
  1177. Text(
  1178. item == null ? "" : "${item.score.toStringAsFixed(1)}分",
  1179. style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
  1180. ),
  1181. ],
  1182. ),
  1183. )),
  1184. Divider(),
  1185. ],
  1186. );
  1187. }
  1188. Widget _buildGuideWidget() {
  1189. return Container(
  1190. decoration: card(),
  1191. margin: ui_margin_list,
  1192. child: GestureDetector(
  1193. behavior: HitTestBehavior.opaque,
  1194. onTap: () => NavigatorUtil.goPage(context, (context) => GuidePage()),
  1195. child: Stack(children: <Widget>[
  1196. Padding(
  1197. padding: EdgeInsets.all(ui_padding),
  1198. child: Row(
  1199. children: <Widget>[
  1200. Image.asset("lib/assets/img/home_image_guidance.png"),
  1201. Space(
  1202. width: 12,
  1203. ),
  1204. Expanded(
  1205. child: Column(
  1206. crossAxisAlignment: CrossAxisAlignment.start,
  1207. mainAxisAlignment: MainAxisAlignment.spaceAround,
  1208. children: <Widget>[
  1209. Text(
  1210. "新手指引",
  1211. style: Theme.of(context).textTheme.headline3.copyWith(fontSize: 14.0),
  1212. ),
  1213. Space(
  1214. height: 4,
  1215. ),
  1216. Text("帮助您快速了解并使用相关功能", style: Theme.of(context).textTheme.bodyText1),
  1217. ],
  1218. ),
  1219. ),
  1220. ],
  1221. ),
  1222. ),
  1223. Positioned(
  1224. right: 0,
  1225. child: GestureDetector(
  1226. behavior: HitTestBehavior.opaque,
  1227. onTap: () async {
  1228. SharedPreferences preferences = await SharedPreferences.getInstance();
  1229. preferences.setBool(GUIDE, false);
  1230. _valueNotifierGuide.value = false;
  1231. },
  1232. child: Padding(
  1233. padding: const EdgeInsets.all(12.0),
  1234. child: Image.asset("lib/assets/img/btn_close_small.png"),
  1235. ),
  1236. ),
  1237. )
  1238. ])),
  1239. );
  1240. }
  1241. Widget _buildSportHistoryWidget(game.GameInfoData item) {
  1242. return GameItem(
  1243. type: 1,
  1244. imgUrl: item.cover,
  1245. name: item.name,
  1246. time: item.playTime,
  1247. id: item.id,
  1248. duration: item.durationTotal,
  1249. data: item,
  1250. bold: true,
  1251. );
  1252. }
  1253. }
  1254. class _HeaderBackground extends CustomPainter {
  1255. final Paint _paint = Paint()
  1256. ..color = Color(0xff241D19)
  1257. ..isAntiAlias = true;
  1258. @override
  1259. void paint(Canvas canvas, Size size) {
  1260. Path path = Path();
  1261. path.lineTo(size.width, 0);
  1262. path.lineTo(size.width, size.height);
  1263. path.lineTo(0, size.height / 3 * 2);
  1264. path.close();
  1265. canvas.drawPath(path, _paint);
  1266. }
  1267. @override
  1268. bool shouldRepaint(CustomPainter oldDelegate) {
  1269. return false;
  1270. }
  1271. }