import 'package:common_pub/logger.dart'; import 'package:common_pub/model/distance.dart'; import 'package:common_pub/model/pace.dart'; import 'package:common_pub/utils.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../widget/title_point.dart'; import '../../service/map_watch.dart'; abstract class PersonalRankController extends GetxController { @override void onInit() { super.onInit(); final now = DateTime.now(); filterStartAt.value = DateTime(now.year, now.month, now.day); workFlushData(); } Future workFlushData() async { while (!isClosed) { final map = mapWatch; if (map == null) { activeList.clear(); await 3.seconds.delay(); continue; } try { final out = await getRankList(map.id, filterStartAt.value); for (final one in out) { final oldEvent = map.getEventById(one.id); if (oldEvent != null) { for (final user in one.userList) { final oldUser = oldEvent.getUserById(user.id); if (oldUser != null) { user.flag = oldUser.flag.value; } } } } activeList.value = out; } catch (_) {} await 3.seconds.delay(); } } final filterStartAt = DateTime.now().obs; final activeList = [].obs; final Rx selectActive = Rx(null); MapWatch? get mapWatch => Get.find().instance; Future> getRankList(int mapId, DateTime startAt); } class PersonalRankPage extends GetView { const PersonalRankPage({super.key}); @override Widget build(BuildContext context) { return Container( width: double.infinity, height: double.infinity, margin: const EdgeInsets.all(20), padding: const EdgeInsets.fromLTRB(12, 17, 12, 17), decoration: BoxDecoration( color: Colors.transparent, borderRadius: BorderRadius.circular(16)), child: DefaultTextStyle( style: const TextStyle(color: Colors.white), child: Row( children: [ SizedBox( width: 260, height: double.infinity, child: Obx(() => eActiveList(context, controller))), const SizedBox(width: 20), Expanded(child: Obx(() => eUserList(context, controller))) ], )), ); } Future _pickDate(BuildContext context, PersonalRankController c) async { final now = c.filterStartAt.value; final time = await showTimePicker( context: context, initialTime: TimeOfDay.fromDateTime(now)); if (time != null) { c.filterStartAt.value = DateTime(now.year, now.month, now.day, time.hour, time.minute); info('time: ${c.filterStartAt.value}'); c.selectActive.value = null; c.activeList.clear(); } // final date = await showDatePicker( // context: context, // initialDate: c.filterStartAt.value, // firstDate: DateTime(now.year - 1), // lastDate: DateTime(now.year, now.month, now.day + 1), // ); // // if (date != null) { // c.filterStartAt.value = DateTime(date.year, date.month, date.day); // } } Widget titlePoint() { return const TitlePoint(color: Color(0xff98d8ff)); } Widget eActiveList(BuildContext context, PersonalRankController c) { return Column( children: [ Row( children: [ Padding(padding: const EdgeInsets.all(8), child: titlePoint()), Text('活动列表', style: context.textTheme.titleLarge ?.copyWith(color: Colors.white, fontSize: 14.22)), const Spacer(), GestureDetector( onTap: () => _pickDate(context, c), child: Container( decoration: BoxDecoration( border: Border.all(color: const Color(0xffe3e3e3))), height: 22.04, alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 8), child: Text(TimeOfDay.fromDateTime(c.filterStartAt.value) .format(context)))) ], ), const SizedBox(height: 20), Expanded( child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xff003656), borderRadius: BorderRadius.circular(9)), child: ListView( children: c.activeList .map((e) => wActiveCard( context, e, c.selectActive.value?.id == e.id, () { c.selectActive.value = e; })) .toList(), ))), ], ); } Widget wActiveCard(BuildContext context, RankActiveInfo active, bool selected, VoidCallback onTap) { return GestureDetector( onTap: onTap, child: Container( decoration: const BoxDecoration(color: Color(0xff00a0ff), boxShadow: [ BoxShadow(color: Color(0x4d000000), blurRadius: 3.5) ]), height: _cardHeight, width: double.infinity, margin: const EdgeInsets.only(top: 7), padding: const EdgeInsets.only(right: 11), child: Row( children: [ Container( margin: const EdgeInsets.only(right: 10), height: double.infinity, width: 6.4, color: selected ? const Color(0xffff870d) : Colors.transparent, ), Expanded(child: Text(active.name)), const SizedBox(width: 8), Text(active.userList.length.toString()) ], ), )); } Widget eUserList(BuildContext context, PersonalRankController c) { final active = c.selectActive.value; if (active == null) { return const SizedBox(); } final userList = c.selectActive.value?.userList ?? []; return Column( children: [ Row( children: [ Padding(padding: const EdgeInsets.all(8), child: titlePoint()), Text('个人排名', style: context.textTheme.titleLarge ?.copyWith(color: Colors.white, fontSize: 14.22)), Text(' (${active.name})', style: context.textTheme.titleLarge?.copyWith( color: const Color(0xffffcb00), fontSize: 14.22)), ], ), Expanded( child: Padding( padding: const EdgeInsets.all(18), child: Column( children: [ eUserListTitle(context), Expanded( child: ListView( children: userList.indexed.map((t) { return eUserCard(context, c, t.$1 + 1, t.$2); }).toList(), )) ], ))) ], ); } Widget eUserListTitle(BuildContext context) { return DefaultTextStyle( style: const TextStyle( color: Color(0xff98d8ff), fontSize: 14.22, fontWeight: FontWeight.w700), child: Row( children: [ const SizedBox( width: _userIndexWidth, child: Text('排名', textAlign: TextAlign.center)), const SizedBox(width: 4), const SizedBox( width: _userNameWidth, child: Text('用户名', textAlign: TextAlign.center)), verticalDivider(show: false), const SizedBox( width: _userPhoneWidth, child: Text( '手机号', textAlign: TextAlign.center, ), ), verticalDivider(show: false), const Expanded( flex: 5, child: Text('路线ID', textAlign: TextAlign.center)), verticalDivider(show: false), const SizedBox( width: _userTimeWidth, child: Text('总时间', textAlign: TextAlign.center)), verticalDivider(show: false), const Expanded( flex: 3, child: Text('总里程', textAlign: TextAlign.center)), verticalDivider(show: false), const Expanded( flex: 4, child: Text('配速', textAlign: TextAlign.center)), verticalDivider(show: false), const SizedBox( width: _userResultWidth, child: Text('状态', textAlign: TextAlign.center)), // verticalDivider(show: false), // const SizedBox( // width: _userFlagWidth, // child: Text('分组', textAlign: TextAlign.center)), // verticalDivider(show: false), ], )); } Widget eUserCard(BuildContext context, PersonalRankController c, int index, RankUserInfo data) { return DefaultTextStyle( style: context.textTheme.bodyMedium!.copyWith(color: Colors.white), child: Container( height: _cardHeight, width: double.infinity, margin: const EdgeInsets.only(top: 3), child: Row( children: [ Container( width: _userIndexWidth, height: double.infinity, decoration: const BoxDecoration( color: Color(0xffff870d), borderRadius: BorderRadius.only( topLeft: Radius.circular(6), bottomLeft: Radius.circular(6))), alignment: Alignment.center, child: Text( index.toString(), textAlign: TextAlign.center, style: const TextStyle( fontSize: 34, fontWeight: FontWeight.w700, fontStyle: FontStyle.italic), )), const SizedBox(width: 4), Expanded( child: Container( height: double.infinity, decoration: const BoxDecoration( color: Color(0xff003656), borderRadius: BorderRadius.only( topRight: Radius.circular(6), bottomRight: Radius.circular(6))), child: Row( children: [ SizedBox( width: _userNameWidth, child: Text( data.name, maxLines: 1, textAlign: TextAlign.center, )), verticalDivider(), SizedBox( width: _userPhoneWidth, child: Text( data.phone, textAlign: TextAlign.center, )), verticalDivider(), Expanded( flex: 5, child: Text( data.routeName, textAlign: TextAlign.center, )), verticalDivider(), SizedBox( width: _userTimeWidth, child: Text( data.duration.toMinSecondString(), textAlign: TextAlign.center, )), verticalDivider(), Expanded( flex: 3, child: Text( data.distance.toString(), textAlign: TextAlign.center, )), verticalDivider(), Expanded( flex: 4, child: Container( margin: const EdgeInsets.only(left: 8, right: 8), alignment: Alignment.center, height: 18, decoration: BoxDecoration( color: data.pace.color, borderRadius: BorderRadius.circular(9)), child: Text( data.pace.toString(), textAlign: TextAlign.center, ))), // verticalDivider(), verticalDivider(), SizedBox( width: _userResultWidth, child: Text( switch (data.state) { GameState.processing => '进行中', GameState.finish => '完赛', GameState.unFinish => '退赛' }, textAlign: TextAlign.center, )), // Container( // alignment: Alignment.center, // width: _userFlagWidth, // child: Icon(Icons.flag, color: data.flag.color)), ], ), )) ], ))); } Widget verticalDivider({bool show = true}) { return VerticalDivider( width: 3, indent: 14, endIndent: 14, color: show ? Colors.white : Colors.transparent, ); } static const _userIndexWidth = 56.0; static const _userNameWidth = 70.0; static const _userPhoneWidth = 94.0; static const _userResultWidth = 62.0; static const _userTimeWidth = 62.0; static const _cardHeight = 45.0; // static const _userFlagWidth = 52.0; } enum GameState { processing, finish, unFinish, } class RankUserInfo { var id = 0; var name = ''; var routeName = ''; var state = GameState.processing; var duration = 0.seconds; var distance = 0.meter; var startAt = DateTime(2000); var phone = ''; Pace get pace => Pace(distance, duration); var flag = Flag.red; } class RankActiveInfo { var id = 0; var name = ''; var userList = []; }