personal_rank.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. import 'package:application/service/map_watch.dart';
  2. import 'package:common_pub/utils.dart';
  3. import 'package:get/get.dart';
  4. import 'package:flutter/material.dart';
  5. import '../../../widget/title_point.dart';
  6. class PersonalRankController extends GetxController {
  7. @override
  8. void onInit() {
  9. super.onInit();
  10. final map = MapWatchService.instance;
  11. if (map != null) {
  12. activeList.bindStream(map.activeList.stream);
  13. }
  14. }
  15. final activeList = <ActiveInfo>[].obs;
  16. final Rx<ActiveInfo?> selectActive = Rx(null);
  17. }
  18. class PersonalRankPage extends StatelessWidget {
  19. const PersonalRankPage({super.key});
  20. @override
  21. Widget build(BuildContext context) {
  22. return GetBuilder(
  23. init: PersonalRankController(),
  24. builder: (c) {
  25. return Container(
  26. width: double.infinity,
  27. height: double.infinity,
  28. margin: const EdgeInsets.all(20),
  29. padding: const EdgeInsets.fromLTRB(12, 17, 12, 17),
  30. decoration: BoxDecoration(
  31. color: Colors.transparent,
  32. borderRadius: BorderRadius.circular(16)),
  33. child: DefaultTextStyle(
  34. style: const TextStyle(color: Colors.white),
  35. child: Row(
  36. children: [
  37. SizedBox(
  38. width: 260,
  39. height: double.infinity,
  40. child: Obx(() => eActiveList(context, c))),
  41. const SizedBox(width: 20),
  42. Expanded(child: Obx(() => eUserList(context, c)))
  43. ],
  44. )),
  45. );
  46. });
  47. }
  48. Widget titlePoint() {
  49. return const TitlePoint(color: Color(0xff98d8ff));
  50. }
  51. Widget eActiveList(BuildContext context, PersonalRankController c) {
  52. return Column(
  53. children: [
  54. Row(
  55. children: [
  56. Padding(padding: const EdgeInsets.all(8), child: titlePoint()),
  57. Text('活动列表',
  58. style: context.textTheme.titleLarge
  59. ?.copyWith(color: Colors.white)),
  60. const Spacer(),
  61. Container(
  62. decoration: BoxDecoration(
  63. border: Border.all(color: const Color(0xffe3e3e3))),
  64. child: const Text('2023-06-26'),
  65. )
  66. ],
  67. ),
  68. const SizedBox(height: 20),
  69. Expanded(
  70. child: Container(
  71. padding: const EdgeInsets.all(12),
  72. decoration: BoxDecoration(
  73. color: const Color(0xff003656),
  74. borderRadius: BorderRadius.circular(9)),
  75. child: ListView(
  76. children: c.activeList
  77. .map((e) => wActiveCard(
  78. context, e, c.selectActive.value?.id == e.id, () {
  79. c.selectActive.value = e;
  80. }))
  81. .toList(),
  82. ))),
  83. ],
  84. );
  85. }
  86. Widget wActiveCard(BuildContext context, ActiveInfo active, bool selected,
  87. VoidCallback onTap) {
  88. return GestureDetector(
  89. onTap: onTap,
  90. child: Container(
  91. decoration: const BoxDecoration(color: Color(0xff00a0ff), boxShadow: [
  92. BoxShadow(color: Color(0x4d000000), blurRadius: 3.5)
  93. ]),
  94. height: _cardHeight,
  95. width: double.infinity,
  96. margin: const EdgeInsets.only(top: 7),
  97. padding: const EdgeInsets.only(right: 11),
  98. child: Row(
  99. children: [
  100. Container(
  101. margin: const EdgeInsets.only(right: 10),
  102. height: double.infinity,
  103. width: 6.4,
  104. color: selected ? const Color(0xffff870d) : Colors.transparent,
  105. ),
  106. Text(active.name),
  107. const Spacer(),
  108. Text(active.userList.length.toString())
  109. ],
  110. ),
  111. ));
  112. }
  113. Widget eUserList(BuildContext context, PersonalRankController c) {
  114. final active = c.selectActive.value;
  115. if (active == null) {
  116. return const SizedBox();
  117. }
  118. final userList = c.selectActive.value?.userList ?? <UserInfo>[];
  119. return Column(
  120. children: [
  121. Row(
  122. children: [
  123. const Padding(padding: EdgeInsets.all(8), child: TitlePoint()),
  124. Text('个人排名',
  125. style: context.textTheme.titleLarge
  126. ?.copyWith(color: Colors.white)),
  127. Text(' (${active.name})',
  128. style: context.textTheme.titleLarge
  129. ?.copyWith(color: const Color(0xffffcb00))),
  130. ],
  131. ),
  132. Expanded(
  133. child: Padding(
  134. padding: const EdgeInsets.all(18),
  135. child: Column(
  136. children: [
  137. eUserListTitle(context),
  138. Expanded(
  139. child: ListView(
  140. children: userList.indexed.map<Widget>((t) {
  141. return eUserCard(context, c, t.$1 + 1, t.$2);
  142. }).toList(),
  143. ))
  144. ],
  145. )))
  146. ],
  147. );
  148. }
  149. Widget eUserListTitle(BuildContext context) {
  150. return DefaultTextStyle(
  151. style: const TextStyle(
  152. color: Color(0xff98d8ff),
  153. fontSize: 20,
  154. fontWeight: FontWeight.w700),
  155. child: Row(
  156. children: [
  157. const SizedBox(
  158. width: _userIndexWidth,
  159. child: Text('排名', textAlign: TextAlign.center)),
  160. const SizedBox(width: 4),
  161. const SizedBox(
  162. width: _userNameWidth,
  163. child: Text('用户名', textAlign: TextAlign.center)),
  164. verticalDivider(show: false),
  165. const Expanded(child: Text('路线ID', textAlign: TextAlign.center)),
  166. verticalDivider(show: false),
  167. const SizedBox(
  168. width: _userTimeWidth,
  169. child: Text('总时间', textAlign: TextAlign.center)),
  170. verticalDivider(show: false),
  171. const Expanded(
  172. child: Text('总里程', textAlign: TextAlign.center)),
  173. verticalDivider(show: false),
  174. const Expanded(
  175. child: Text('配速', textAlign: TextAlign.center)),
  176. verticalDivider(show: false),
  177. const SizedBox(
  178. width: _userResultWidth,
  179. child: Text('分组', textAlign: TextAlign.center)),
  180. verticalDivider(show: false),
  181. ],
  182. ));
  183. }
  184. Widget eUserCard(BuildContext context, PersonalRankController c, int index,
  185. UserInfo data) {
  186. var startAt = '--';
  187. if (data.startAt != null) {
  188. startAt = data.startAt!.toIso8601String();
  189. }
  190. return DefaultTextStyle(
  191. style: context.textTheme.bodyMedium!.copyWith(color: Colors.white),
  192. child: Container(
  193. height: _cardHeight,
  194. width: double.infinity,
  195. margin: const EdgeInsets.only(top: 3),
  196. child: Row(children: [
  197. Container(
  198. width: _userIndexWidth,
  199. height:double.infinity,
  200. decoration: const BoxDecoration(
  201. color: Color(0xffff870d),
  202. borderRadius: BorderRadius.only(topLeft: Radius.circular(6), bottomLeft: Radius.circular(6))
  203. ),
  204. alignment: Alignment.center,
  205. child: Text(
  206. index.toString(),
  207. textAlign: TextAlign.center,
  208. style: const TextStyle(fontSize: 34, fontWeight: FontWeight.w700, fontStyle: FontStyle.italic),
  209. )),
  210. const SizedBox(width: 4),
  211. Expanded(child: Container(
  212. height: double.infinity,
  213. decoration: const BoxDecoration(
  214. color: Color(0xff003656),
  215. borderRadius: BorderRadius.only(topRight: Radius.circular(6), bottomRight: Radius.circular(6))
  216. ),
  217. child: Row(
  218. children: [
  219. SizedBox(
  220. width: _userNameWidth,
  221. child: Text(
  222. data.name,
  223. textAlign: TextAlign.center,
  224. )),
  225. verticalDivider(),
  226. Expanded(
  227. child: Text(
  228. data.routeName,
  229. textAlign: TextAlign.center,
  230. )),
  231. verticalDivider(),
  232. SizedBox(
  233. width: _userTimeWidth,
  234. child: Text(
  235. data.duration.toMinSecondString(),
  236. textAlign: TextAlign.center,
  237. )),
  238. verticalDivider(),
  239. Expanded(
  240. child: Text(
  241. data.distance.toString(),
  242. textAlign: TextAlign.center,
  243. )),
  244. verticalDivider(),
  245. Expanded(
  246. child: Container(
  247. margin: EdgeInsets.only(left: 8, right: 8),
  248. alignment: Alignment.center,
  249. height: 18,
  250. decoration: BoxDecoration(color: data.pace.color, borderRadius: BorderRadius.circular(9)),
  251. child: Text(
  252. data.pace.toString(),
  253. textAlign: TextAlign.center,
  254. )
  255. ) ),
  256. verticalDivider(),
  257. Container(
  258. alignment: Alignment.center,
  259. width: _userResultWidth,
  260. child: Icon(Icons.flag, color: data.flag.value.color)),
  261. ],
  262. ),
  263. ))
  264. ],
  265. )
  266. ));
  267. }
  268. Widget verticalDivider({bool show = true}){
  269. return VerticalDivider(
  270. width: 3,
  271. indent: 14,
  272. endIndent: 14,
  273. color: show? Colors.white: Colors.transparent,
  274. );
  275. }
  276. static const _userIndexWidth = 56.0;
  277. static const _userNameWidth = 84.0;
  278. static const _userResultWidth = 52.0;
  279. static const _userTimeWidth = 62.0;
  280. static const _cardHeight = 45.0;
  281. }