rank_detail.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. import 'dart:convert';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:provider/provider.dart';
  5. import 'package:sport/bean/rank_game_info.dart';
  6. import 'package:sport/bean/user.dart';
  7. import 'package:sport/provider/user_model.dart';
  8. import 'package:sport/router/navigator_util.dart';
  9. import 'package:sport/router/routes.dart';
  10. import 'package:sport/services/api/inject_api.dart';
  11. import 'package:sport/widgets/area.dart';
  12. import 'package:sport/widgets/decoration.dart';
  13. import 'package:sport/widgets/image.dart';
  14. import 'package:sport/widgets/loading.dart';
  15. import 'package:sport/widgets/space.dart';
  16. class RankDetailPage extends StatefulWidget {
  17. final String id;
  18. final String type;
  19. RankDetailPage(this.id, this.type);
  20. @override
  21. State<StatefulWidget> createState() {
  22. return _RankDetailPageState();
  23. }
  24. }
  25. class _RankDetailPageState extends State<RankDetailPage> with InjectApi {
  26. bool _isLoading;
  27. RankGameInfoData _data;
  28. String _id;
  29. String _rank;
  30. @override
  31. void initState() {
  32. super.initState();
  33. // 简单的缓存...
  34. if (_data == null || _id != widget.id) {
  35. _isLoading = true;
  36. initData();
  37. _id = widget.id;
  38. } else {
  39. _isLoading = false;
  40. }
  41. }
  42. // 缓存数据...
  43. initData() async {
  44. if (widget.type == "1") {
  45. api.getRankGameInfo(widget.id).then((data) {
  46. setState(() {
  47. _data = data.data;
  48. _isLoading = false;
  49. });
  50. });
  51. } else {
  52. api.getRankSportInfo(widget.id).then((data) {
  53. setState(() {
  54. _data = data.data;
  55. _isLoading = false;
  56. });
  57. });
  58. }
  59. }
  60. area(bool all, bool province, int provinceId, int cityId) {
  61. setState(() {
  62. _isLoading = true;
  63. });
  64. if (widget.type == "1") {
  65. api.getRankGameInfo(widget.id, provinceId: all ? null : provinceId, cityId: all ? null : province ? null : cityId).then((data) {
  66. setState(() {
  67. _data = data.data;
  68. _isLoading = false;
  69. });
  70. });
  71. } else {
  72. api.getRankSportInfo(widget.id, provinceId: all ? null : provinceId, cityId: all ? null : province ? null : cityId).then((data) {
  73. setState(() {
  74. _data = data.data;
  75. _isLoading = false;
  76. });
  77. });
  78. }
  79. }
  80. initUser(int rank) {
  81. return User(position: rank, userName: '虚位以待', score: 0, id: 0);
  82. }
  83. Widget _buildRankWidget() {
  84. dynamic user1 = initUser(1), user2 = initUser(2), user3 = initUser(3), user4;
  85. if (_data != null && _data.records != null) {
  86. for (var i = 0; i < _data.records.length; i++) {
  87. var item = _data.records[i];
  88. if (i == 0) {
  89. user1 = item;
  90. } else if (i == 1) {
  91. user2 = item;
  92. } else if (i == 2) {
  93. user3 = item;
  94. }
  95. }
  96. }
  97. return Column(
  98. children: <Widget>[
  99. Center(
  100. child: Row(
  101. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  102. crossAxisAlignment: CrossAxisAlignment.end,
  103. children: <Widget>[
  104. Container(width: 94, child: user2 != null ? _buildRankHeaderWidget(user2) : _buildRankHeaderWidget(initUser(2))),
  105. Container(width: 120, child: user1 != null ? _buildRankHeaderWidget(user1) : _buildRankHeaderWidget(initUser(1))),
  106. Container(width: 94, child: user3 != null ? _buildRankHeaderWidget(user3) : _buildRankHeaderWidget(initUser(3))),
  107. ],
  108. ),
  109. ),
  110. Space(
  111. height: 10,
  112. ),
  113. Divider(
  114. height: 1,
  115. indent: 12,
  116. endIndent: 12,
  117. )
  118. ],
  119. );
  120. }
  121. Widget _buildRankHeaderWidget(User user) {
  122. int rank = user.position;
  123. return InkWell(
  124. child: Column(
  125. children: <Widget>[
  126. rank == 1
  127. ? Stack(
  128. alignment: Alignment.center,
  129. children: <Widget>[
  130. CircleAvatar(backgroundImage: userAvatarProvider(user.userAvatar == null ? "" : user.userAvatar), radius: 46.0),
  131. Image.asset(
  132. "lib/assets/img/rank_image_no$rank.png",
  133. )
  134. ],
  135. )
  136. : Stack(
  137. alignment: Alignment.topCenter,
  138. children: <Widget>[
  139. Positioned(top: 3, child: CircleAvatar(backgroundImage: userAvatarProvider(user.userAvatar == null ? "" : user.userAvatar), radius: 35.0)),
  140. Image.asset(
  141. "lib/assets/img/rank_image_no$rank.png",
  142. )
  143. ],
  144. ),
  145. Space(
  146. height: 6,
  147. ),
  148. Text(
  149. "${user.userName}",
  150. style: Theme.of(context).textTheme.subtitle1,
  151. maxLines: 1,
  152. ),
  153. Space(
  154. height: 4,
  155. ),
  156. _dataText(_data.rank, user)
  157. ],
  158. ),
  159. onTap: () {
  160. if (user.id != 0) NavigatorUtil.goRankPeopleDetails(context, details: user);
  161. },
  162. );
  163. }
  164. Widget _buildRankItemWidget(int number, User data) {
  165. return Column(
  166. children: <Widget>[
  167. InkWell(
  168. onTap: () {
  169. if (data is User) {
  170. if (data.id != 0) NavigatorUtil.goRankPeopleDetails(context, details: data);
  171. }
  172. },
  173. child: Container(
  174. padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
  175. child: Row(
  176. children: <Widget>[
  177. Container(
  178. width: 20,
  179. child: Text(
  180. "$number",
  181. style: Theme.of(context).textTheme.bodyText2,
  182. ),
  183. ),
  184. Padding(
  185. padding: const EdgeInsets.fromLTRB(0, 6, 10.0, 6),
  186. child: CircleAvatar(backgroundColor:Colors.transparent, backgroundImage: userAvatarProvider(data.userAvatar == null ? "" : data.userAvatar), radius: 22),
  187. ),
  188. Expanded(
  189. child: Text(
  190. data.userName,
  191. style: Theme.of(context).textTheme.subtitle1,
  192. maxLines: 1,
  193. ),
  194. ),
  195. _dataText(_data.rank, data),
  196. ],
  197. ),
  198. ),
  199. ),
  200. Divider(
  201. height: 1,
  202. indent: 12,
  203. endIndent: 12,
  204. ),
  205. ],
  206. );
  207. }
  208. List<Widget> getRankItems() {
  209. List<Widget> rankItems = [];
  210. if (_data != null && _data.records != null) {
  211. for (var i = 3; i < _data.records.length; i++) {
  212. var item = _data.records[i];
  213. rankItems.add(_buildRankItemWidget(i + 1, item));
  214. }
  215. }
  216. if (rankItems.length < 7) {
  217. int start = rankItems.length;
  218. for (var i = start; i < 7; i++) {
  219. var item = initUser(i + 4);
  220. rankItems.add(_buildRankItemWidget(i + 4, item));
  221. }
  222. }
  223. return rankItems;
  224. }
  225. Widget getUserRank() {
  226. return Positioned(
  227. bottom: -36.0,
  228. left: 12.0,
  229. right: 12.0,
  230. child: Container(
  231. width: MediaQuery.of(context).size.width * 0.95,
  232. height: 72.0,
  233. decoration: card(),
  234. child: Padding(
  235. padding: EdgeInsets.symmetric(horizontal: 12.0),
  236. child: Row(
  237. children: <Widget>[
  238. _data.user != null
  239. ? Container(
  240. padding: EdgeInsets.only(right: 8),
  241. child: Text(
  242. "${_data.user.position}",
  243. style: Theme.of(context).textTheme.bodyText2,
  244. ),
  245. )
  246. : Container(),
  247. _data.user != null
  248. ? _data.user.up != 0 && _data.user.upNew != 0
  249. ? Padding(
  250. padding: EdgeInsets.only(right: 8),
  251. child: Image.asset("lib/assets/img/rand_icon_${_data.user.up >= 0 ? 'top' : 'down'}.png"),
  252. )
  253. : Container()
  254. : Container(),
  255. Expanded(
  256. child: Container(
  257. child: Consumer<UserModel>(
  258. builder: (_, model, __) {
  259. return Row(
  260. mainAxisSize: MainAxisSize.max,
  261. children: <Widget>[
  262. CircleAvatar(backgroundImage: userAvatarProvider(model?.user?.avatar != null ? model.user.avatar : ""), radius: 22),
  263. SizedBox(
  264. width: 10,
  265. ),
  266. Expanded(
  267. child: Text(
  268. model.user.name,
  269. overflow: TextOverflow.ellipsis,
  270. style: Theme.of(context).textTheme.subtitle1,
  271. ),
  272. ),
  273. SizedBox(
  274. width: 10,
  275. ),
  276. ],
  277. );
  278. },
  279. ),
  280. ),
  281. ),
  282. _data.user == null ? Text("暂无记录", style: Theme.of(context).textTheme.bodyText1,) : _data.rank.userCountMax < _data.user.position ? Text("暂未上榜", style: Theme.of(context).textTheme.bodyText1,) : _dataText(_data.rank, _data.user),
  283. Container(
  284. width: 12.0,
  285. height: 14.0,
  286. margin: EdgeInsets.only(left: 18.0),
  287. child: Image.asset(
  288. "lib/assets/img/rank_icon_location.png",
  289. fit: BoxFit.cover,
  290. ),
  291. ),
  292. ],
  293. ),
  294. ),
  295. ),
  296. );
  297. }
  298. Widget _dataText(Rank rank, User user) {
  299. var unit = _dataUnit(rank);
  300. if (rank != null) {
  301. if (rank.field == null || rank.field == "") {
  302. return Text(user.score == null || user.score == 0 ? "" : "${user.score.toStringAsFixed(1)}$unit",
  303. style: TextStyle(color: Color.fromRGBO(255, 196, 0, 1)));
  304. } else {
  305. if (rank.field == 'consume') {
  306. return Text(user.consume == null ? "" : "${user.consume.round()}$unit", style: TextStyle(color: Color.fromRGBO(255, 196, 0, 1)));
  307. } else if (rank.field == 'duration') {
  308. return Text(user.duration == null ? "" : "${user.duration}$unit", style: TextStyle(color: Color.fromRGBO(255, 196, 0, 1)));
  309. }
  310. }
  311. }
  312. return Container();
  313. }
  314. String _dataUnit(Rank rank) {
  315. if (rank != null) {
  316. if (rank.field == null || rank.field == "") {
  317. return "分";
  318. } else {
  319. if (rank.field == 'consume') {
  320. return "kal";
  321. } else if (rank.field == 'duration') {
  322. return "分钟";
  323. }
  324. }
  325. }
  326. return "";
  327. }
  328. @override
  329. Widget build(BuildContext context) {
  330. return Scaffold(
  331. backgroundColor: Colors.white,
  332. body: SingleChildScrollView(
  333. child: Column(
  334. children: <Widget>[
  335. //header
  336. Stack(
  337. overflow: Overflow.visible,
  338. children: <Widget>[
  339. Container(
  340. width: MediaQuery.of(context).size.width,
  341. height: MediaQuery.of(context).size.width * 611 / 1125,
  342. decoration: BoxDecoration(image: DecorationImage(image: AssetImage("lib/assets/img/rank_bg.png"), fit: BoxFit.fill)),
  343. child: SafeArea(
  344. child: Align(
  345. alignment: Alignment.centerLeft,
  346. child: Padding(
  347. padding: const EdgeInsets.symmetric(horizontal: 12),
  348. child: _data?.rank == null
  349. ? Container()
  350. : Column(
  351. crossAxisAlignment: CrossAxisAlignment.start,
  352. mainAxisSize: MainAxisSize.min,
  353. children: <Widget>[
  354. Padding(
  355. padding: EdgeInsets.symmetric(vertical: 6.0),
  356. child: Text(
  357. "${_data.rank.name}",
  358. style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.w600),
  359. ),
  360. ),
  361. Padding(
  362. padding: EdgeInsets.symmetric(vertical: 2.0),
  363. child: Text(
  364. "${_data.rank.introduce}",
  365. style: Theme.of(context).textTheme.bodyText1,
  366. ),
  367. ),
  368. Padding(
  369. padding: EdgeInsets.symmetric(vertical: 2.0),
  370. child: Text(
  371. "${_data.rank.rateBegin} ~ ${_data.rank.rateEnd}",
  372. style: Theme.of(context).textTheme.bodyText1,
  373. ),
  374. ),
  375. SizedBox(
  376. height: 10,
  377. )
  378. ],
  379. ),
  380. ),
  381. ),
  382. )),
  383. if (_data != null) getUserRank(),
  384. Positioned(
  385. left: 0.0,
  386. top: 0.0,
  387. child: SafeArea(
  388. child: IconButton(
  389. icon: Image.asset("lib/assets/img/topbar_return_white.png"),
  390. onPressed: () {
  391. Navigator.pop(context);
  392. },
  393. ),
  394. )),
  395. Positioned(
  396. top: 0,
  397. right: 0,
  398. child: SafeArea(
  399. child: IconButton(
  400. icon: Image.asset("lib/assets/img/topbar_problom.png"),
  401. onPressed: () {
  402. if(_data == null)
  403. return;
  404. NavigatorUtil.go(context, "${Routes.rankIntroduce}?data=${Uri.encodeComponent(json.encode(_data))}");
  405. },
  406. ),
  407. ))
  408. ],
  409. ),
  410. if (!_isLoading)
  411. Padding(
  412. padding: const EdgeInsets.only(top: 50.0),
  413. child: Row(
  414. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  415. children: <Widget>[
  416. Consumer<UserModel>(
  417. builder: (_, model, __) => model.user.city == null || _rank == "全国榜" || _rank == null
  418. ? Container()
  419. : Container(
  420. padding: EdgeInsets.symmetric(horizontal: 12.0),
  421. child: Row(
  422. children: <Widget>[
  423. Image.asset("lib/assets/img/rank_icon_position.png"),
  424. SizedBox(
  425. width: 6,
  426. ),
  427. Text(
  428. "${_rank == '全省榜' ? model.user?.province : model.user?.city}",
  429. style: Theme.of(context).textTheme.bodyText2,
  430. )
  431. ],
  432. ),
  433. ),
  434. ),
  435. NotificationListener<AreaNotification>(
  436. onNotification: (n) {
  437. _rank = n.rank;
  438. area(n.all, n.province, n.provinceId, n.cityId);
  439. return true;
  440. },
  441. child: AreaPage(
  442. area: _rank,
  443. ),
  444. )
  445. ],
  446. ),
  447. ),
  448. if (_isLoading)
  449. Container(child: RequestLoadingWidget()),
  450. if (!_isLoading)
  451. Padding(
  452. padding: EdgeInsets.only(top: 12.0),
  453. child: _buildRankWidget(),
  454. ),
  455. if (!_isLoading)
  456. Column(
  457. children: getRankItems(),
  458. )
  459. ],
  460. ),
  461. ));
  462. }
  463. }