field_control.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import 'dart:async';
  2. import 'package:app_business/service/abase.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_touch.dart';
  7. import 'package:common_pub/ui/map_view/view_map_trace_tail.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 'package:flutter/material.dart';
  12. import 'package:track_common/generated/assets.dart';
  13. import 'package:track_common/service/map_watch.dart';
  14. import 'package:track_common/widget/no_select_map.dart';
  15. class FieldControl extends StatelessWidget {
  16. const FieldControl({super.key});
  17. @override
  18. Widget build(BuildContext context) {
  19. return GetBuilder(
  20. init: FieldControlController(),
  21. builder: (c) {
  22. return Container(
  23. height: double.infinity,
  24. width: double.infinity,
  25. color: const Color(0xffc9c0c0),
  26. alignment: Alignment.center,
  27. child: Obx(() {
  28. final mapWatch = c.mapWatch;
  29. return mapWatch != null
  30. ? content(context, mapWatch, c)
  31. : const NoSelectMap();
  32. }));
  33. });
  34. }
  35. Widget content(BuildContext context, MapWatch map, FieldControlController c) {
  36. final children = <Widget>[
  37. ViewPlugLoading(map.plugMap),
  38. ViewMapImage(map.plugMap),
  39. ];
  40. final focusUser = c.focusUser;
  41. if (focusUser != null) {
  42. children.add(ViewMapCP(
  43. // key:UniqueKey(),
  44. map.plugMap,
  45. cpWantAndHistoryList: focusUser.cpList,
  46. isHideRouteBeforeStart: false,
  47. controller: c.viewCpController,
  48. ));
  49. }
  50. children.addAll([
  51. _ViewTrace(map: map, traceDuration: 30.seconds),
  52. ViewMapTouch(map.plugMap)
  53. ]);
  54. return Row(
  55. children: [
  56. Expanded(
  57. child: Column(
  58. children: [
  59. _MapView(),
  60. // Expanded(
  61. // child: ViewMapStack(plug: map.plugMap, children: children)),
  62. _MsgView(),
  63. ],
  64. )),
  65. _ActiveInfoView()
  66. ],
  67. );
  68. }
  69. }
  70. class _MapView extends GetView<FieldControlController> {
  71. @override
  72. Widget build(BuildContext context) {
  73. return Expanded(
  74. child: ViewMapStack(
  75. plug: controller.mapWatch!.plugMap,
  76. children: [
  77. Obx(() {
  78. final map = controller.mapWatch!;
  79. final children = <Widget>[
  80. ViewPlugLoading(map.plugMap),
  81. ViewMapImage(map.plugMap),
  82. ];
  83. final focusUser = controller.focusUser;
  84. if (focusUser != null) {
  85. children.add(ViewMapCP(
  86. // key:UniqueKey(),
  87. map.plugMap,
  88. cpWantAndHistoryList: focusUser.cpList,
  89. isHideRouteBeforeStart: false,
  90. controller: controller.viewCpController,
  91. ));
  92. }
  93. children.addAll([
  94. _ViewTrace(map: map, traceDuration: 30.seconds),
  95. ViewMapTouch(map.plugMap)
  96. ]);
  97. return SizedBox.expand(
  98. child: Stack(
  99. children: children,
  100. ),
  101. );
  102. })
  103. ],
  104. ));
  105. }
  106. }
  107. class _ViewTrace extends GetView<FieldControlController> {
  108. const _ViewTrace({required this.map, required this.traceDuration});
  109. final MapWatch map;
  110. final Duration traceDuration;
  111. @override
  112. Widget build(BuildContext context) {
  113. return Obx(() {
  114. final children = <Widget>[];
  115. for (final act in map.eventList) {
  116. for (final user in act.userList) {
  117. if (user.isHide.value) {
  118. continue;
  119. }
  120. final trace = user.trace.lastOrNull;
  121. final traceTailOnMap = <Offset>[];
  122. final st = user.startAt;
  123. for (final one in user.trace) {
  124. if (DateTime.now().difference(st.add(one.ts)) < traceDuration) {
  125. traceTailOnMap.add(one.onMap);
  126. }
  127. }
  128. if (trace != null) {
  129. children.add(ViewMapTraceTail(
  130. plug: map.plugMap,
  131. onMapTrace: traceTailOnMap,
  132. color: user.flag.value.color,
  133. ));
  134. children.add(ViewMapUserPoint(
  135. key: UniqueKey(),
  136. map.plugMap,
  137. trace,
  138. info: user.name,
  139. color: user.flag.value.color));
  140. }
  141. }
  142. }
  143. return Stack(alignment: Alignment.topLeft, children: children);
  144. });
  145. }
  146. }
  147. class _ActiveInfoView extends GetView<FieldControlController> {
  148. @override
  149. Widget build(BuildContext context) {
  150. return Obx(() => Container(
  151. width: 370,
  152. height: double.infinity,
  153. color: Colors.white,
  154. child: ListView(
  155. children: controller.eventList
  156. .map((element) => activeView(element))
  157. .toList(),
  158. ),
  159. ));
  160. }
  161. Widget activeView(EventOnMap info) {
  162. final children = <Widget>[
  163. Row(children: [
  164. Expanded(
  165. child: Text(
  166. '${info.name} (${info.userList.length}人)',
  167. maxLines: 1,
  168. )),
  169. const SizedBox(
  170. width: 8,
  171. ),
  172. IconButton(
  173. onPressed: () {
  174. info.isHide.value = !info.isHide.value;
  175. },
  176. icon: info.isHide.value
  177. ? const Icon(Icons.arrow_drop_down)
  178. : const Icon(Icons.arrow_drop_up))
  179. ]),
  180. ];
  181. if (!info.isHide.value) {
  182. children.addAll([
  183. Container(
  184. decoration: BoxDecoration(
  185. color: Colors.white, borderRadius: BorderRadius.circular(5)),
  186. padding: const EdgeInsets.fromLTRB(26, 11, 26, 11),
  187. child: Row(
  188. children: [
  189. const Text('广播'),
  190. const Spacer(),
  191. Image.asset(Assets.imagesIcCp, height: 20, width: 20),
  192. Text(' ${info.cpAllCount}'),
  193. const Spacer(),
  194. const Text('全部隐藏'),
  195. ],
  196. ),
  197. )
  198. ]);
  199. children
  200. .addAll(info.userList.map((e) => _UserInfoView(data: e)).toList());
  201. }
  202. return Container(
  203. decoration: BoxDecoration(
  204. color: const Color(0xffe0e0e0),
  205. borderRadius: BorderRadius.circular(5)),
  206. margin: const EdgeInsets.fromLTRB(9, 12, 9, 12),
  207. padding: const EdgeInsets.all(9),
  208. child: Column(
  209. children: children,
  210. ),
  211. );
  212. }
  213. }
  214. class _UserInfoView extends GetView<FieldControlController> {
  215. const _UserInfoView({required this.data});
  216. final UserOnMap data;
  217. @override
  218. Widget build(BuildContext context) {
  219. return Container(
  220. decoration: BoxDecoration(
  221. color: Colors.white, borderRadius: BorderRadius.circular(5)),
  222. padding: const EdgeInsets.fromLTRB(7, 11, 7, 11),
  223. margin: const EdgeInsets.only(top: 5),
  224. child: Column(
  225. crossAxisAlignment: CrossAxisAlignment.start,
  226. children: [
  227. Row(
  228. children: [
  229. Obx(() => Container(
  230. margin: const EdgeInsets.only(top: 2),
  231. decoration: BoxDecoration(
  232. color: data.flag.value.color,
  233. borderRadius: BorderRadius.circular(4)),
  234. width: 7,
  235. height: 16)),
  236. const SizedBox(
  237. width: 8,
  238. ),
  239. Expanded(
  240. child: Text.rich(TextSpan(
  241. text: data.name,
  242. children: [TextSpan(text: ' [${data.routeName}]')])),
  243. ),
  244. GestureDetector(
  245. onTap: () {
  246. final oldId = controller.focusUserId.value;
  247. if (oldId != null) {
  248. if (oldId == data.id) {
  249. controller.focusUserId.value = null;
  250. return;
  251. }
  252. }
  253. controller.focusUserId.value = data.id;
  254. },
  255. child: Obx(() => Icon(
  256. Icons.route,
  257. color: data.id != controller.focusUser?.id
  258. ? Colors.grey
  259. : const Color(0xffffbb77),
  260. )),
  261. ),
  262. const SizedBox(width: 8),
  263. GestureDetector(
  264. onTap: () {
  265. data.isHide.value = !data.isHide.value;
  266. },
  267. child: Obx(() => Icon(
  268. data.isHide.value
  269. ? Icons.visibility_off
  270. : Icons.visibility,
  271. color: data.isHide.value
  272. ? Colors.grey
  273. : const Color(0xffffbb77),
  274. )),
  275. )
  276. ],
  277. ),
  278. Container(
  279. margin: const EdgeInsets.only(left: 14),
  280. child: Row(
  281. children: [
  282. container(null, cpInfo, Colors.blue),
  283. container(
  284. const Icon(
  285. Icons.favorite,
  286. size: 13,
  287. color: Colors.white,
  288. ),
  289. ' ${hr == 0 ? '--' : hr}',
  290. data.heartRatePercent.toHRPColor()),
  291. container(null, paceInfo, data.pace.color)
  292. ],
  293. ),
  294. ),
  295. const SizedBox(height: 5),
  296. Container(
  297. margin: const EdgeInsets.only(left: 14),
  298. child: Row(
  299. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  300. children: [
  301. Text('距离 ${data.nextDistance.toString()}'),
  302. Text('时间 ${data.duration.toAppString()}'),
  303. Text('里程 ${data.distance.toString()}'),
  304. ],
  305. ))
  306. ],
  307. ),
  308. );
  309. }
  310. int get hr {
  311. return data.hrInfo.lastOrNull?.hr ?? 0;
  312. }
  313. String get cpInfo {
  314. final next = data.nextWant;
  315. return next != null ? '${data.nextCPSN}点(${next.areaId})' : '--';
  316. }
  317. String get paceInfo {
  318. Duration;
  319. return data.pace.toString();
  320. }
  321. Widget container(Widget? icon, String text, Color color) {
  322. final children = <Widget>[];
  323. if (icon != null) {
  324. children.add(icon);
  325. }
  326. children.add(
  327. Text(text, style: const TextStyle(color: Colors.white, fontSize: 14)));
  328. return Container(
  329. height: 20,
  330. padding: const EdgeInsets.fromLTRB(10, 0, 12, 0),
  331. margin: const EdgeInsets.only(right: 6),
  332. alignment: Alignment.center,
  333. decoration:
  334. BoxDecoration(color: color, borderRadius: BorderRadius.circular(9)),
  335. child: Row(
  336. children: children,
  337. ),
  338. );
  339. }
  340. }
  341. class _MsgView extends GetView<FieldControlController> {
  342. @override
  343. Widget build(BuildContext context) {
  344. return Container();
  345. }
  346. }
  347. class FieldControlController extends GetxController {
  348. @override
  349. void onInit() {
  350. super.onInit();
  351. final map = mapWatch;
  352. if (map != null) {
  353. eventList.bindStream(map.eventList.stream);
  354. }
  355. _subActive = eventList.listen((p0) {
  356. final user = focusUser;
  357. if (user != null) {
  358. viewCpController.setCPList(user.cpList);
  359. }
  360. });
  361. }
  362. @override
  363. void onClose() {
  364. super.onClose();
  365. _subActive?.cancel();
  366. }
  367. final viewCpController = ViewMapCPController();
  368. StreamSubscription<List<EventOnMap>>? _subActive;
  369. final _mapService = Get.find<MapWatchService>();
  370. MapWatch? get mapWatch => _mapService.instance;
  371. final eventList = <EventOnMap>[].obs;
  372. final Rx<int?> focusUserId = Rx(null);
  373. UserOnMap? findFocusUser(List<EventOnMap> list) {
  374. if (focusUserId.value == null) {
  375. return null;
  376. }
  377. for (final act in list) {
  378. for (final user in act.userList) {
  379. if (user.id == focusUserId.value) {
  380. return user;
  381. }
  382. }
  383. }
  384. return null;
  385. }
  386. UserOnMap? get focusUser {
  387. return findFocusUser(eventList);
  388. }
  389. }