home_info_page.dart 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  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/sport_index.dart';
  10. import 'package:sport/bean/user.dart';
  11. import 'package:sport/constant/ui.dart' show ui_padding, ui_margin_list;
  12. import 'package:sport/pages/game/detail_bottom.dart';
  13. import 'package:sport/pages/game/game_info.dart';
  14. import 'package:sport/pages/home/guide_page.dart';
  15. import 'package:sport/provider/bluetooth.dart';
  16. import 'package:sport/provider/lib/provider_widget.dart';
  17. import 'package:sport/provider/lib/view_state_lifecycle.dart';
  18. import 'package:sport/provider/sport_index_model.dart';
  19. import 'package:sport/provider/user_model.dart';
  20. import 'package:sport/router/navigator_util.dart';
  21. import 'package:sport/router/routes.dart';
  22. import 'package:sport/widgets/area.dart';
  23. import 'package:sport/widgets/button_primary.dart';
  24. import 'package:sport/widgets/decoration.dart';
  25. import 'package:sport/widgets/dialog/request_dialog.dart';
  26. import 'package:sport/widgets/dialog/search_device.dart';
  27. import 'package:sport/widgets/dialog/share_popup.dart';
  28. import 'package:sport/widgets/image.dart';
  29. import 'package:sport/widgets/loading.dart';
  30. import 'package:sport/widgets/misc.dart';
  31. import 'package:sport/widgets/space.dart';
  32. import 'package:sport/widgets/wave_painter.dart';
  33. class HomeInfoPage extends StatefulWidget {
  34. @override
  35. State<StatefulWidget> createState() => _PageState();
  36. }
  37. class _PageState extends ViewStateLifecycle<HomeInfoPage, SportIndexModel> with AutomaticKeepAliveClientMixin {
  38. String _rank;
  39. ValueNotifier<bool> _valueNotifierGuide = ValueNotifier(false);
  40. static const String GUIDE = "guide";
  41. @override
  42. void initState() {
  43. super.initState();
  44. loadSetting();
  45. initList();
  46. }
  47. void loadSetting() async {
  48. SharedPreferences preferences = await SharedPreferences.getInstance();
  49. _valueNotifierGuide.value = preferences.getBool(GUIDE) ?? true;
  50. }
  51. @override
  52. SportIndexModel createModel() => SportIndexModel(context);
  53. refresh() {
  54. _rank = null;
  55. model.refresh();
  56. }
  57. initList(){
  58. model.valueNotifier.addListener(() {
  59. print("list--------------------------${model.valueNotifier.value}");
  60. if(model.valueNotifier.value.length > 0){
  61. Navigator.push( context, new PopRoute(child: sharePopup(model.valueNotifier.value)));
  62. }
  63. });
  64. }
  65. @override
  66. Widget build(BuildContext context) {
  67. super.build(context);
  68. return Scaffold(
  69. backgroundColor: Colors.white,
  70. appBar: PreferredSize(
  71. preferredSize: Size.fromHeight(40.0),
  72. child: AppBar(
  73. titleSpacing: ui_padding,
  74. brightness: Brightness.dark,
  75. backgroundColor: Color(0xff241D19),
  76. title: _buildDeviceInfoWidget(),
  77. ),
  78. ),
  79. body: ProviderWidget<SportIndexModel>(
  80. model: this.model,
  81. onModelReady: (v) => v.initData(),
  82. builder: (BuildContext context, SportIndexModel value, Widget child) => EasyRefresh.custom(
  83. enableControlFinishRefresh: true,
  84. onRefresh: () => refresh(),
  85. header: buildClassicalHeader(bgColor: Color(0xff241D19)),
  86. slivers: <Widget>[
  87. // // 暂时测试
  88. // if (model.isIdle && value.data != null)
  89. // SliverToBoxAdapter(
  90. // child: Stack(
  91. // fit: StackFit.passthrough,
  92. // children: <Widget>[
  93. // CustomPaint(
  94. // painter: _HeaderBackground(),
  95. // child: Container(
  96. // height: 116,
  97. // ),
  98. // ),
  99. // Container(
  100. // margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0),
  101. // decoration: card(),
  102. // height: 156,
  103. // child: _buildSportWidget(value.data),
  104. // )
  105. // ],
  106. // ),
  107. // ),
  108. if (model.isIdle && value.data != null)
  109. SliverToBoxAdapter(
  110. child: _buildTopWidget(value.data),
  111. ),
  112. if (model.isBusy)
  113. SliverToBoxAdapter(
  114. child: RequestLoadingWidget(),
  115. ),
  116. if (value.data?.lastGame != null)
  117. SliverToBoxAdapter(
  118. child: _buildSportHistoryWidget(value.data.lastGame),
  119. ),
  120. SliverToBoxAdapter(
  121. child: ValueListenableBuilder<bool>(
  122. builder: (BuildContext context, value, Widget child) => value ? _buildGuideWidget() : Container(), valueListenable: _valueNotifierGuide),
  123. ),
  124. if (value.data?.games != null)
  125. SliverToBoxAdapter(
  126. child: _buildLabelWidget("SPORTS"),
  127. ),
  128. if (value.data?.games != null)
  129. SliverPadding(
  130. padding: const EdgeInsets.symmetric(horizontal: ui_padding),
  131. sliver: SliverGrid(
  132. gridDelegate:
  133. SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 10, mainAxisSpacing: 10, childAspectRatio: 170.0 / 226),
  134. delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
  135. return InkWell(
  136. onTap: () => NavigatorUtil.goGameDetails(context, details: value.data.games[index]),
  137. child: _buildSportItemWidget(value.data.games[index]));
  138. }, childCount: value.data.games.length),
  139. ),
  140. ),
  141. if (value.data?.rank != null)
  142. SliverToBoxAdapter(
  143. child: Padding(
  144. padding: const EdgeInsets.only(top: 12.0),
  145. child: Row(
  146. children: <Widget>[
  147. Expanded(child: _buildLabelWidget("CHARTS")),
  148. PopupMenuButton(
  149. onSelected: (v) {
  150. value.game(v);
  151. },
  152. itemBuilder: (BuildContext context) {
  153. return divideMenus(value.data.games
  154. .map((e) => menuItemSelected(e.id, e.name, e.id == value.gameId))
  155. .toList());
  156. },
  157. child: Row(
  158. mainAxisSize: MainAxisSize.min,
  159. children: <Widget>[
  160. Text(value.data.games.where((element) => element.id == value.gameId).map((e) => e.name).first, style: Theme.of(context).textTheme.bodyText1,),
  161. Padding(
  162. padding: const EdgeInsets.all(6.0),
  163. child: arrowBottom(),
  164. )
  165. ],
  166. ),
  167. ),
  168. SizedBox(
  169. width: 12,
  170. ),
  171. NotificationListener<AreaNotification>(
  172. onNotification: (n) {
  173. _rank = n.rank;
  174. request(context, () async {
  175. await value.area(
  176. n.all ? null : n.provinceId, n.all ? null : n.province ? null : n.cityId, n.all ? null : n.province ? null : n.districtId);
  177. });
  178. return true;
  179. },
  180. child: AreaPage(
  181. area: _rank,
  182. ),
  183. )
  184. ],
  185. ),
  186. ),
  187. ),
  188. if (value.data?.rank != null)
  189. SliverToBoxAdapter(
  190. child: _buildRankWidget(value.data.rank),
  191. ),
  192. if (value.data?.rank != null)
  193. SliverPadding(
  194. padding: const EdgeInsets.symmetric(horizontal: ui_padding),
  195. sliver: SliverList(
  196. delegate: SliverChildBuilderDelegate((content, index) {
  197. return (value.data.rank.records.length - 3) > index
  198. ? _buildRankItemWidget(value.data.rank.records[index + 3], index)
  199. : _buildRankItemWidget(null, index);
  200. }, childCount: 7),
  201. )),
  202. if (value.data?.rank != null)
  203. SliverToBoxAdapter(
  204. child: Center(
  205. child: InkWell(
  206. onTap: () {
  207. NavigatorUtil.goRankDetails(context, value.data.rank.rank.gameId, 1);
  208. },
  209. child: Padding(
  210. padding: const EdgeInsets.all(16.0),
  211. child: Text(
  212. "查看完整榜单",
  213. style: Theme.of(context).textTheme.bodyText1.copyWith(color: Color(0xffcecece)),
  214. ),
  215. ),
  216. )),
  217. ),
  218. SliverToBoxAdapter(
  219. child: SizedBox(
  220. height: 50,
  221. ),
  222. ),
  223. ],
  224. ),
  225. ),
  226. );
  227. }
  228. @override
  229. bool get wantKeepAlive => true;
  230. Widget _buildDeviceInfoWidget() {
  231. return Selector<Bluetooth, BluetoothDevice>(
  232. selector: (_, bluetooth) => bluetooth.device,
  233. builder: (_, device, ___) {
  234. return device == null ? _buildDeviceInfoConnectFailWidget() : _buildDeviceInfoStateWidget(device);
  235. });
  236. }
  237. Widget _buildDeviceInfoStateWidget(BluetoothDevice device) {
  238. return StreamBuilder<BluetoothDeviceState>(
  239. stream: device.state,
  240. initialData: BluetoothDeviceState.connecting,
  241. builder: (c, snapshot) => _buildDeviceInfoConnectedWidget(snapshot.data == BluetoothDeviceState.connected));
  242. }
  243. Widget _buildDeviceInfoConnectedWidget(bool connect) {
  244. return Row(
  245. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  246. children: <Widget>[
  247. Row(
  248. children: <Widget>[
  249. Container(
  250. width: 10,
  251. height: 10,
  252. decoration: BoxDecoration(shape: BoxShape.circle, color: connect ? Color(0xff00DC42) : Colors.red),
  253. ),
  254. SizedBox(
  255. width: 6.0,
  256. ),
  257. Text(connect ? "设备已连接" : "未连接设备",
  258. style: TextStyle(
  259. color: Colors.white,
  260. fontSize: 14,
  261. ),
  262. strutStyle: StrutStyle(forceStrutHeight: true)),
  263. SizedBox(
  264. width: 4.0,
  265. ),
  266. if (connect)
  267. Stack(
  268. alignment: Alignment.bottomCenter,
  269. children: <Widget>[
  270. Image.asset("lib/assets/img/battery.png"),
  271. ValueListenableBuilder(
  272. valueListenable: Provider.of<Bluetooth>(context, listen: false).electricityNotifier,
  273. builder: (_, data, ___) => Container(
  274. width: 5.5,
  275. height: 8 * (data / 100),
  276. margin: EdgeInsets.only(bottom: 2, right: 0.5),
  277. decoration: BoxDecoration(color: Color(0xff00DC42)),
  278. )),
  279. ],
  280. )
  281. ],
  282. ),
  283. GestureDetector(
  284. onTap: () => NavigatorUtil.go(context, Routes.deviceInfo),
  285. child: Row(
  286. children: <Widget>[
  287. Text(
  288. "查看设备详情",
  289. style: TextStyle(
  290. color: Colors.white,
  291. fontSize: 14,
  292. ),
  293. strutStyle: StrutStyle(forceStrutHeight: true),
  294. ),
  295. SizedBox(
  296. width: 6,
  297. ),
  298. arrowRight1()
  299. ],
  300. ),
  301. ),
  302. ],
  303. );
  304. }
  305. Widget _buildDeviceInfoConnectFailWidget() {
  306. return Row(
  307. children: <Widget>[
  308. Container(
  309. width: 10,
  310. height: 10,
  311. decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.red),
  312. ),
  313. SizedBox(
  314. width: 6.0,
  315. ),
  316. Text("未连接设备",
  317. style: TextStyle(
  318. color: Colors.white,
  319. fontSize: 14,
  320. ),
  321. strutStyle: StrutStyle(forceStrutHeight: true)),
  322. ],
  323. );
  324. }
  325. Widget _buildDeviceWidget() {
  326. return Column(
  327. mainAxisAlignment: MainAxisAlignment.center,
  328. crossAxisAlignment: CrossAxisAlignment.center,
  329. children: <Widget>[
  330. Image.asset("lib/assets/img/home_image_connect.png"),
  331. Space(
  332. height: 8,
  333. ),
  334. Text(
  335. "请先连接设备鞋子",
  336. style: Theme.of(context).textTheme.bodyText1,
  337. ),
  338. Space(
  339. height: 12,
  340. ),
  341. PrimaryButton(
  342. content: "搜索设备",
  343. width: 132,
  344. height: 35,
  345. callback: () {
  346. showDialog(context: context, child: SearchDeviceDialog());
  347. },
  348. )
  349. ],
  350. );
  351. }
  352. Widget _buildTopWidget(SportIndex item) {
  353. return Stack(
  354. fit: StackFit.passthrough,
  355. children: <Widget>[
  356. CustomPaint(
  357. painter: _HeaderBackground(),
  358. child: Container(
  359. height: 116,
  360. ),
  361. ),
  362. Container(
  363. margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0),
  364. decoration: card(),
  365. height: 156,
  366. child: Selector<Bluetooth, BluetoothDevice>(
  367. selector: (_, bluetooth) => bluetooth.device,
  368. builder: (_, device, ___) {
  369. return device != null ? _buildSportWidget(item) : _buildDeviceWidget();
  370. }),
  371. )
  372. ],
  373. );
  374. }
  375. Widget _buildSportWidget(SportIndex item) {
  376. return InkWell(
  377. onTap: () => NavigatorUtil.go(context, Routes.sportDetail),
  378. child: Stack(
  379. alignment: Alignment.center,
  380. children: <Widget>[
  381. Row(
  382. children: <Widget>[
  383. Expanded(
  384. flex: 5,
  385. child: Padding(
  386. padding: const EdgeInsets.all(16.0),
  387. child: ValueListenableBuilder(
  388. valueListenable: Provider.of<UserModel>(context, listen: false).durationTarget,
  389. builder: (_, v, __) => ProgressManager(min(.9, v <= 0 ? 0 : item.duration / v))),
  390. )),
  391. Expanded(
  392. flex: 6,
  393. child: Column(
  394. mainAxisAlignment: MainAxisAlignment.center,
  395. crossAxisAlignment: CrossAxisAlignment.start,
  396. children: <Widget>[
  397. Text(
  398. "今日已运动",
  399. style: Theme.of(context).textTheme.subtitle1,
  400. ),
  401. SizedBox(
  402. height: 5,
  403. ),
  404. RichText(
  405. text: TextSpan(style: Theme.of(context).textTheme.subtitle2, children: <InlineSpan>[
  406. TextSpan(text: '${item.duration}', style: Theme.of(context).textTheme.subtitle2.copyWith(fontWeight: FontWeight.bold, fontSize: 40)),
  407. TextSpan(text: '分钟'),
  408. ]),
  409. ),
  410. SizedBox(
  411. height: 5,
  412. ),
  413. Text("共消耗 ${item.consume} kal", style: Theme.of(context).textTheme.bodyText1),
  414. SizedBox(
  415. height: 5,
  416. ),
  417. Text(item.beyond > 0 ? "已超越了${item.beyond}人,请再接再厉" : "${item.inspire}", style: Theme.of(context).textTheme.bodyText1),
  418. ],
  419. ),
  420. )
  421. ],
  422. ),
  423. Positioned(
  424. right: 0,
  425. child: Image.asset("lib/assets/img/home_icon_details.png"),
  426. )
  427. ],
  428. ),
  429. );
  430. }
  431. Widget _buildRankWidget(RankInfo rankInfo) {
  432. return Column(
  433. children: <Widget>[
  434. if (rankInfo?.user != null)
  435. GestureDetector(
  436. onTap: () => NavigatorUtil.goRankPeopleDetails(context, details: rankInfo.user),
  437. child: Container(
  438. margin: EdgeInsets.all(ui_padding),
  439. padding: EdgeInsets.all(ui_padding),
  440. decoration: card(),
  441. child: Row(
  442. children: <Widget>[
  443. Padding(
  444. padding: const EdgeInsets.only(right: 8),
  445. child: Text(
  446. "${rankInfo.user.position}",
  447. ),
  448. ),
  449. if (rankInfo.user.up != 0 && rankInfo.user.upNew != 0)
  450. Padding(
  451. padding: const EdgeInsets.only(right: 8),
  452. child: Image.asset("lib/assets/img/rand_icon_${rankInfo.user.up >= 0 ? 'top' : 'down'}.png"),
  453. ),
  454. Padding(
  455. padding: const EdgeInsets.only(right: 10.0),
  456. child: CircleAvatar(backgroundImage: userAvatarProvider(rankInfo?.user?.userAvatar), radius: 22),
  457. ),
  458. Expanded(
  459. child: Text(
  460. rankInfo.user.userName,
  461. style: Theme.of(context).textTheme.subtitle1,
  462. ),
  463. ),
  464. Text(
  465. "${rankInfo.user.score.toStringAsFixed(1)}分",
  466. style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
  467. ),
  468. ],
  469. ),
  470. ),
  471. ),
  472. Center(
  473. child: Row(
  474. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  475. crossAxisAlignment: CrossAxisAlignment.end,
  476. children: <Widget>[
  477. Container(width: 94, child: rankInfo.records.length >= 2 ? _buildRankHeaderWidget(rankInfo.records[1], 2) : _buildRankHeaderWidget(null, 2)),
  478. Container(width: 120, child: rankInfo.records.length >= 1 ? _buildRankHeaderWidget(rankInfo.records[0], 1) : _buildRankHeaderWidget(null, 1)),
  479. Container(width: 94, child: rankInfo.records.length >= 3 ? _buildRankHeaderWidget(rankInfo.records[2], 3) : _buildRankHeaderWidget(null, 3)),
  480. ],
  481. ),
  482. ),
  483. Space(
  484. height: 10,
  485. ),
  486. Divider(
  487. indent: 12.0,
  488. endIndent: 12.0,
  489. )
  490. ],
  491. );
  492. }
  493. Widget _buildRankHeaderWidget(User item, int rank) {
  494. return InkWell(
  495. onTap: item == null ? null : () => NavigatorUtil.goRankPeopleDetails(context, details: item),
  496. child: Column(
  497. children: <Widget>[
  498. rank == 1
  499. ? Stack(
  500. alignment: Alignment.center,
  501. children: <Widget>[
  502. CircleAvatar(backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar), radius: 46.0),
  503. Image.asset(
  504. "lib/assets/img/rank_image_no$rank.png",
  505. )
  506. ],
  507. )
  508. : Stack(
  509. alignment: Alignment.topCenter,
  510. children: <Widget>[
  511. Positioned(top: 3, child: CircleAvatar(backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar), radius: 35.0)),
  512. Image.asset(
  513. "lib/assets/img/rank_image_no$rank.png",
  514. )
  515. ],
  516. ),
  517. Space(
  518. height: 6,
  519. ),
  520. Text(
  521. item == null ? "虚位以待" : item.userName,
  522. style: Theme.of(context).textTheme.subtitle1,
  523. maxLines: 1,
  524. ),
  525. Space(
  526. height: 4,
  527. ),
  528. Row(
  529. mainAxisSize: MainAxisSize.min,
  530. children: <Widget>[
  531. Text(
  532. item == null ? "" : "${item.score.toStringAsFixed(1)}分",
  533. style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
  534. ),
  535. ],
  536. ),
  537. ],
  538. ),
  539. );
  540. }
  541. Widget _buildLabelWidget(String title) {
  542. return Container(
  543. padding: EdgeInsets.fromLTRB(ui_padding, 10.0, ui_padding, 10.0),
  544. child: Text(
  545. title,
  546. style: Theme.of(context).textTheme.headline1,
  547. ));
  548. }
  549. Widget _buildSportItemWidget(game.GameInfoData item) {
  550. return Container(
  551. decoration: BoxDecoration(
  552. image: DecorationImage(
  553. image: CachedNetworkImageProvider(item.coverVertical),
  554. fit: BoxFit.cover,
  555. ),
  556. borderRadius: BorderRadius.all(Radius.circular(10.0))),
  557. child: Column(
  558. mainAxisSize: MainAxisSize.min,
  559. mainAxisAlignment: MainAxisAlignment.end,
  560. children: <Widget>[
  561. Container(
  562. width: double.infinity,
  563. padding: EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0),
  564. decoration: BoxDecoration(
  565. gradient:
  566. LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0x00000000), Color(0x912c2c2c), Color(0xCF000000)]),
  567. borderRadius: BorderRadius.vertical(bottom: Radius.circular(10.0)),
  568. ),
  569. child: Column(
  570. mainAxisSize: MainAxisSize.min,
  571. crossAxisAlignment: CrossAxisAlignment.start,
  572. children: <Widget>[
  573. Text(
  574. item.name,
  575. style: Theme.of(context).textTheme.headline3.copyWith(color: Colors.white),
  576. ),
  577. Space(
  578. height: 5,
  579. ),
  580. if (item.tags != null)
  581. Wrap(
  582. spacing: 4,
  583. runSpacing: 4,
  584. children: item.tags.map((e) => gameTag(context, e)).toList(),
  585. ),
  586. Space(
  587. height: 5,
  588. ),
  589. Text(
  590. "${item.userCount}人在玩",
  591. style: Theme.of(context).textTheme.bodyText1.copyWith(color: Colors.white),
  592. ),
  593. ],
  594. ),
  595. ),
  596. ],
  597. ));
  598. }
  599. Widget _buildRankItemWidget(User item, int index) {
  600. return Column(
  601. children: <Widget>[
  602. InkWell(
  603. onTap: item == null ? null : () => NavigatorUtil.goRankPeopleDetails(context, details: item),
  604. child: Container(
  605. child: Row(
  606. children: <Widget>[
  607. Container(
  608. width: 24,
  609. child: Text(
  610. "${index + 4}",
  611. style: Theme.of(context).textTheme.bodyText2,
  612. ),
  613. ),
  614. Padding(
  615. padding: const EdgeInsets.fromLTRB(0, 6, 10.0, 6),
  616. child: CircleAvatar(
  617. backgroundColor: Colors.transparent, backgroundImage: userAvatarProvider(item == null ? "" : item.userAvatar ?? ""), radius: 22),
  618. ),
  619. Expanded(
  620. child: Text(
  621. item == null ? "虚位以待" : item.userName,
  622. style: Theme.of(context).textTheme.subtitle1,
  623. maxLines: 1,
  624. ),
  625. ),
  626. Text(
  627. item == null ? "" : "${item.score.toStringAsFixed(1)}分",
  628. style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
  629. ),
  630. ],
  631. ),
  632. )),
  633. Divider(),
  634. ],
  635. );
  636. }
  637. Widget _buildGuideWidget() {
  638. return Container(
  639. decoration: card(),
  640. margin: ui_margin_list,
  641. child: GestureDetector(
  642. behavior: HitTestBehavior.opaque,
  643. onTap: () => NavigatorUtil.goPage(context, (context) => GuidePage()),
  644. child: Stack(children: <Widget>[
  645. Padding(
  646. padding: EdgeInsets.all(ui_padding),
  647. child: Row(
  648. children: <Widget>[
  649. Image.asset("lib/assets/img/home_image_guidance.png"),
  650. Space(
  651. width: 12,
  652. ),
  653. Expanded(
  654. child: Column(
  655. crossAxisAlignment: CrossAxisAlignment.start,
  656. mainAxisAlignment: MainAxisAlignment.spaceAround,
  657. children: <Widget>[
  658. Text(
  659. "新手指引",
  660. style: Theme.of(context).textTheme.headline3.copyWith(fontSize: 14.0),
  661. ),
  662. Space(
  663. height: 4,
  664. ),
  665. Text("帮助您快速了解并使用相关功能", style: Theme.of(context).textTheme.bodyText1),
  666. ],
  667. ),
  668. ),
  669. ],
  670. ),
  671. ),
  672. Positioned(
  673. right: 0,
  674. child: GestureDetector(
  675. behavior: HitTestBehavior.opaque,
  676. onTap: () async {
  677. SharedPreferences preferences = await SharedPreferences.getInstance();
  678. preferences.setBool(GUIDE, false);
  679. _valueNotifierGuide.value = false;
  680. },
  681. child: Padding(
  682. padding: const EdgeInsets.all(12.0),
  683. child: Image.asset("lib/assets/img/btn_close_small.png"),
  684. ),
  685. ),
  686. )
  687. ])),
  688. );
  689. }
  690. Widget _buildSportHistoryWidget(game.GameInfoData item) {
  691. return GameItem(
  692. type: 1,
  693. imgUrl: item.cover,
  694. name: item.name,
  695. time: item.playTime,
  696. id: item.id,
  697. duration: item.durationTotal,
  698. data: item,
  699. bold: true,
  700. );
  701. }
  702. }
  703. class _HeaderBackground extends CustomPainter {
  704. final Paint _paint = Paint()
  705. ..color = Color(0xff241D19)
  706. ..isAntiAlias = true;
  707. @override
  708. void paint(Canvas canvas, Size size) {
  709. Path path = Path();
  710. path.lineTo(size.width, 0);
  711. path.lineTo(size.width, size.height);
  712. path.lineTo(0, size.height / 3 * 2);
  713. path.close();
  714. canvas.drawPath(path, _paint);
  715. }
  716. @override
  717. bool shouldRepaint(CustomPainter oldDelegate) {
  718. return false;
  719. }
  720. }
  721. class RankFlowDelegate extends FlowDelegate {
  722. @override
  723. void paintChildren(FlowPaintingContext context) {
  724. var screenW = context.size.width;
  725. var childW = screenW / context.childCount;
  726. double padding = 5; //间距
  727. double offsetX = padding; //x坐标
  728. double offsetY = padding; //y坐标
  729. for (int i = 0; i < context.childCount; i++) {
  730. // if(i == 0){
  731. context.paintChild(i, transform: Matrix4.translationValues(offsetX, offsetY, 0));
  732. // }
  733. }
  734. }
  735. @override
  736. bool shouldRepaint(FlowDelegate context) => true;
  737. }