field_control.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import 'package:application/service/api.dart' as pb;
  2. import 'package:application/widget.dart';
  3. import 'package:common_pub/ui/map_view/map_view.dart';
  4. import 'package:common_pub/ui/map_view/view_map_cp.dart';
  5. import 'package:common_pub/ui/map_view/view_map_image.dart';
  6. import 'package:common_pub/ui/map_view/view_map_trace_tail.dart';
  7. import 'package:common_pub/ui/map_view/view_map_touch.dart';
  8. import 'package:common_pub/ui/map_view/view_map_user_point.dart';
  9. import 'package:common_pub/ui/map_view/view_plug_loading.dart';
  10. import 'package:common_pub/utils.dart';
  11. import 'field_control_controller.dart';
  12. class FieldControlPage extends StatelessWidget {
  13. const FieldControlPage({super.key});
  14. @override
  15. Widget build(BuildContext context) {
  16. return GetBuilder(
  17. init: FieldControlController(),
  18. builder: (c) {
  19. return Container(
  20. height: double.infinity,
  21. width: double.infinity,
  22. color: const Color(0xffc9c0c0),
  23. alignment: Alignment.center,
  24. child: Obx(() {
  25. final mapWatch = c.mapWatch;
  26. return mapWatch != null
  27. ? content(context, mapWatch, c)
  28. : noData();
  29. }));
  30. });
  31. }
  32. Widget noData() {
  33. return Center(
  34. child: Column(
  35. mainAxisSize: MainAxisSize.min,
  36. children: [
  37. Image.asset(Assets.imagesIcNoData, height: 64),
  38. const SizedBox(height: 25),
  39. const Text('没有数据, 请选择地图',
  40. style: TextStyle(color: Color(0xff707070), fontSize: 18.5)),
  41. ],
  42. ),
  43. );
  44. }
  45. Widget content(
  46. BuildContext context, MapWatchService map, FieldControlController c) {
  47. final children = <Widget>[
  48. ViewPlugLoading(map.plugMap),
  49. ViewMapImage(map.plugMap),
  50. ];
  51. final focusUser = c.focusUser;
  52. if (focusUser != null) {
  53. children.add(ViewMapCP(
  54. key:UniqueKey(),
  55. map.plugMap,
  56. cpWantAndHistoryList: focusUser.cpList,
  57. isHideRouteBeforeStart: false,
  58. ));
  59. }
  60. children.addAll([
  61. _ViewTrace(map: map, traceDuration: 30.seconds),
  62. ViewMapTouch(map.plugMap)
  63. ]);
  64. return Row(
  65. children: [
  66. Expanded(
  67. child: Column(
  68. children: [
  69. Expanded(
  70. child: ViewMapStack(plug: map.plugMap, children: children)),
  71. _MsgView(),
  72. ],
  73. )),
  74. _ActiveInfoView()
  75. ],
  76. );
  77. }
  78. }
  79. class _ViewTrace extends GetView<FieldControlController> {
  80. const _ViewTrace({required this.map, required this.traceDuration});
  81. final MapWatchService map;
  82. final Duration traceDuration;
  83. @override
  84. Widget build(BuildContext context) {
  85. return Obx(() {
  86. final children = <Widget>[];
  87. for (final act in map.activeList) {
  88. for (final user in act.userList) {
  89. if (user.isHide.value) {
  90. continue;
  91. }
  92. final trace = user.trace.lastOrNull;
  93. final traceTailOnMap = <Offset>[];
  94. final st = user.gameInfo.gameSaveInfo.startAt.toModel();
  95. for (final one in user.trace) {
  96. if (DateTime.now().difference(st.add(one.ts)) < traceDuration) {
  97. traceTailOnMap.add(one.onMap);
  98. }
  99. }
  100. if (trace != null) {
  101. children.add(ViewMapTraceTail(
  102. plug: map.plugMap,
  103. onMapTrace: traceTailOnMap,
  104. color: user.flag.value.color,
  105. ));
  106. children.add(ViewMapUserPoint(
  107. key: UniqueKey(),
  108. map.plugMap, trace,
  109. info: user.name, color: user.flag.value.color));
  110. }
  111. }
  112. }
  113. return Stack(alignment: Alignment.topLeft, children: children);
  114. });
  115. }
  116. }
  117. class _ActiveInfoView extends GetView<FieldControlController> {
  118. @override
  119. Widget build(BuildContext context) {
  120. return Obx(() => Container(
  121. width: 370,
  122. height: double.infinity,
  123. color: Colors.white,
  124. child: ListView(
  125. children: controller.activeList
  126. .map((element) => activeView(element))
  127. .toList(),
  128. ),
  129. ));
  130. }
  131. Widget activeView(ActiveInfo info) {
  132. final children = <Widget>[
  133. Row(children: [
  134. Text('${info.name} (${info.userList.length}人)'),
  135. const Spacer(),
  136. IconButton(
  137. onPressed: () {
  138. info.isHide.value = !info.isHide.value;
  139. },
  140. icon: info.isHide.value
  141. ? const Icon(Icons.arrow_drop_down)
  142. : const Icon(Icons.arrow_drop_up))
  143. ]),
  144. ];
  145. if (!info.isHide.value) {
  146. children.addAll([
  147. Container(
  148. decoration: BoxDecoration(
  149. color: Colors.white, borderRadius: BorderRadius.circular(5)),
  150. padding: const EdgeInsets.fromLTRB(26, 11, 26, 11),
  151. child: Row(
  152. children: [
  153. const Text('广播'),
  154. const Spacer(),
  155. Image.asset(Assets.imagesIcCp, height: 20, width: 20),
  156. Text(' ${info.cpAllCount}'),
  157. const Spacer(),
  158. const Text('全部隐藏'),
  159. ],
  160. ),
  161. )
  162. ]);
  163. children
  164. .addAll(info.userList.map((e) => _UserInfoView(data: e)).toList());
  165. }
  166. return Container(
  167. decoration: BoxDecoration(
  168. color: const Color(0xffe0e0e0),
  169. borderRadius: BorderRadius.circular(5)),
  170. margin: const EdgeInsets.fromLTRB(9, 12, 9, 12),
  171. padding: const EdgeInsets.all(9),
  172. child: Column(
  173. children: children,
  174. ),
  175. );
  176. }
  177. }
  178. class _UserInfoView extends GetView<FieldControlController> {
  179. const _UserInfoView({required this.data});
  180. final UserInfo data;
  181. @override
  182. Widget build(BuildContext context) {
  183. return Container(
  184. decoration: BoxDecoration(
  185. color: Colors.white, borderRadius: BorderRadius.circular(5)),
  186. padding: const EdgeInsets.fromLTRB(7, 11, 7, 11),
  187. margin: const EdgeInsets.only(top: 5),
  188. child: Column(
  189. crossAxisAlignment: CrossAxisAlignment.start,
  190. children: [
  191. Row(
  192. children: [
  193. Obx(() => Container(
  194. margin: const EdgeInsets.only(top: 2),
  195. decoration: BoxDecoration(
  196. color: data.flag.value.color,
  197. borderRadius: BorderRadius.circular(4)),
  198. width: 7,
  199. height: 16)),
  200. const SizedBox(
  201. width: 8,
  202. ),
  203. Expanded(
  204. child: Text.rich(TextSpan(
  205. text: data.name,
  206. children: [TextSpan(text: ' [${data.routeName}]')])),
  207. ),
  208. GestureDetector(
  209. onTap: () {
  210. final oldId = controller.focusUserId.value;
  211. if(oldId != null){
  212. if(oldId == data.id){
  213. controller.focusUserId.value = null;
  214. return;
  215. }
  216. }
  217. controller.focusUserId.value = data.id;
  218. },
  219. child: Obx(() => Icon(
  220. Icons.route,
  221. color: data.gameInfo.userId !=
  222. controller.focusUser?.gameInfo.userId
  223. ? Colors.grey
  224. : const Color(0xffffbb77),
  225. )),
  226. ),
  227. const SizedBox(width: 8),
  228. GestureDetector(
  229. onTap: () {
  230. data.isHide.value = !data.isHide.value;
  231. },
  232. child: Obx(() => Icon(
  233. data.isHide.value
  234. ? Icons.visibility_off
  235. : Icons.visibility,
  236. color: data.isHide.value
  237. ? Colors.grey
  238. : const Color(0xffffbb77),
  239. )),
  240. )
  241. ],
  242. ),
  243. Container(
  244. margin: const EdgeInsets.only(left: 14),
  245. child: Row(
  246. children: [
  247. container(null, cpInfo, Colors.blue),
  248. container(
  249. const Icon(
  250. Icons.favorite,
  251. size: 13,
  252. color: Colors.white,
  253. ),
  254. ' ${hr == 0 ? '--' : hr}',
  255. (data.gameInfo.otherInfo.hasHeartRatePercent()
  256. ? data.gameInfo.otherInfo.heartRatePercent
  257. : 0)
  258. .toHRPColor()),
  259. container(null, paceInfo, data.pace.color)
  260. ],
  261. ),
  262. ),
  263. const SizedBox(height: 5),
  264. Container(
  265. margin: const EdgeInsets.only(left: 14),
  266. child: Row(
  267. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  268. children: [
  269. Text('距离 ${data.nextDistance.toString()}'),
  270. Text('时间 ${data.duration.toAppString()}'),
  271. Text('里程 ${data.distance.toString()}'),
  272. ],
  273. ))
  274. ],
  275. ),
  276. );
  277. }
  278. int get hr {
  279. return data.gameInfo.hrInfo.hrInfo.lastOrNull?.hr ?? 0;
  280. }
  281. String get cpInfo {
  282. final next = data.nextWant;
  283. return next != null ? '${data.nextCPSN}点(${next.areaId})' : '--';
  284. }
  285. String get paceInfo {
  286. Duration;
  287. return data.pace.toString();
  288. }
  289. Widget container(Widget? icon, String text, Color color) {
  290. final children = <Widget>[];
  291. if (icon != null) {
  292. children.add(icon);
  293. }
  294. children.add(
  295. Text(text, style: const TextStyle(color: Colors.white, fontSize: 14)));
  296. return Container(
  297. height: 20,
  298. padding: const EdgeInsets.fromLTRB(10, 0, 12, 0),
  299. margin: const EdgeInsets.only(right: 6),
  300. alignment: Alignment.center,
  301. decoration:
  302. BoxDecoration(color: color, borderRadius: BorderRadius.circular(9)),
  303. child: Row(
  304. children: children,
  305. ),
  306. );
  307. }
  308. }
  309. class _MsgView extends GetView<FieldControlController> {
  310. @override
  311. Widget build(BuildContext context) {
  312. return Container();
  313. }
  314. }