|
|
@@ -0,0 +1,425 @@
|
|
|
+import 'dart:async';
|
|
|
+
|
|
|
+import 'package:app_business/service/abase.dart';
|
|
|
+import 'package:common_pub/ui/map_view/map_view.dart';
|
|
|
+import 'package:common_pub/ui/map_view/view_map_cp.dart';
|
|
|
+import 'package:common_pub/ui/map_view/view_map_image.dart';
|
|
|
+import 'package:common_pub/ui/map_view/view_map_touch.dart';
|
|
|
+import 'package:common_pub/ui/map_view/view_map_trace_tail.dart';
|
|
|
+import 'package:common_pub/ui/map_view/view_map_user_point.dart';
|
|
|
+import 'package:common_pub/ui/map_view/view_plug_loading.dart';
|
|
|
+import 'package:common_pub/utils.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:track_common/generated/assets.dart';
|
|
|
+import 'package:track_common/service/map_watch.dart';
|
|
|
+import 'package:track_common/widget/no_select_map.dart';
|
|
|
+
|
|
|
+class FieldControl extends StatelessWidget {
|
|
|
+ const FieldControl({super.key});
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return GetBuilder(
|
|
|
+ init: FieldControlController(),
|
|
|
+ builder: (c) {
|
|
|
+ return Container(
|
|
|
+ height: double.infinity,
|
|
|
+ width: double.infinity,
|
|
|
+ color: const Color(0xffc9c0c0),
|
|
|
+ alignment: Alignment.center,
|
|
|
+ child: Obx(() {
|
|
|
+ final mapWatch = c.mapWatch;
|
|
|
+ return mapWatch != null
|
|
|
+ ? content(context, mapWatch, c)
|
|
|
+ : const NoSelectMap();
|
|
|
+ }));
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget content(BuildContext context, MapWatch map, FieldControlController c) {
|
|
|
+ final children = <Widget>[
|
|
|
+ ViewPlugLoading(map.plugMap),
|
|
|
+ ViewMapImage(map.plugMap),
|
|
|
+ ];
|
|
|
+
|
|
|
+ final focusUser = c.focusUser;
|
|
|
+ if (focusUser != null) {
|
|
|
+ children.add(ViewMapCP(
|
|
|
+ // key:UniqueKey(),
|
|
|
+ map.plugMap,
|
|
|
+ cpWantAndHistoryList: focusUser.cpList,
|
|
|
+ isHideRouteBeforeStart: false,
|
|
|
+ controller: c.viewCpController,
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ children.addAll([
|
|
|
+ _ViewTrace(map: map, traceDuration: 30.seconds),
|
|
|
+ ViewMapTouch(map.plugMap)
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return Row(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ _MapView(),
|
|
|
+ // Expanded(
|
|
|
+ // child: ViewMapStack(plug: map.plugMap, children: children)),
|
|
|
+ _MsgView(),
|
|
|
+ ],
|
|
|
+ )),
|
|
|
+ _ActiveInfoView()
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _MapView extends GetView<FieldControlController> {
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Expanded(
|
|
|
+ child: ViewMapStack(
|
|
|
+ plug: controller.mapWatch!.plugMap,
|
|
|
+ children: [
|
|
|
+ Obx(() {
|
|
|
+ final map = controller.mapWatch!;
|
|
|
+ final children = <Widget>[
|
|
|
+ ViewPlugLoading(map.plugMap),
|
|
|
+ ViewMapImage(map.plugMap),
|
|
|
+ ];
|
|
|
+
|
|
|
+ final focusUser = controller.focusUser;
|
|
|
+ if (focusUser != null) {
|
|
|
+ children.add(ViewMapCP(
|
|
|
+ // key:UniqueKey(),
|
|
|
+ map.plugMap,
|
|
|
+ cpWantAndHistoryList: focusUser.cpList,
|
|
|
+ isHideRouteBeforeStart: false,
|
|
|
+ controller: controller.viewCpController,
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ children.addAll([
|
|
|
+ _ViewTrace(map: map, traceDuration: 30.seconds),
|
|
|
+ ViewMapTouch(map.plugMap)
|
|
|
+ ]);
|
|
|
+ return SizedBox.expand(
|
|
|
+ child: Stack(
|
|
|
+ children: children,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ })
|
|
|
+ ],
|
|
|
+ ));
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _ViewTrace extends GetView<FieldControlController> {
|
|
|
+ const _ViewTrace({required this.map, required this.traceDuration});
|
|
|
+
|
|
|
+ final MapWatch map;
|
|
|
+ final Duration traceDuration;
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Obx(() {
|
|
|
+ final children = <Widget>[];
|
|
|
+
|
|
|
+ for (final act in map.eventList) {
|
|
|
+ for (final user in act.userList) {
|
|
|
+ if (user.isHide.value) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ final trace = user.trace.lastOrNull;
|
|
|
+ final traceTailOnMap = <Offset>[];
|
|
|
+ final st = user.startAt;
|
|
|
+ for (final one in user.trace) {
|
|
|
+ if (DateTime.now().difference(st.add(one.ts)) < traceDuration) {
|
|
|
+ traceTailOnMap.add(one.onMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (trace != null) {
|
|
|
+ children.add(ViewMapTraceTail(
|
|
|
+ plug: map.plugMap,
|
|
|
+ onMapTrace: traceTailOnMap,
|
|
|
+ color: user.flag.value.color,
|
|
|
+ ));
|
|
|
+ children.add(ViewMapUserPoint(
|
|
|
+ key: UniqueKey(),
|
|
|
+ map.plugMap,
|
|
|
+ trace,
|
|
|
+ info: user.name,
|
|
|
+ color: user.flag.value.color));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Stack(alignment: Alignment.topLeft, children: children);
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _ActiveInfoView extends GetView<FieldControlController> {
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Obx(() => Container(
|
|
|
+ width: 370,
|
|
|
+ height: double.infinity,
|
|
|
+ color: Colors.white,
|
|
|
+ child: ListView(
|
|
|
+ children: controller.eventList
|
|
|
+ .map((element) => activeView(element))
|
|
|
+ .toList(),
|
|
|
+ ),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget activeView(EventOnMap info) {
|
|
|
+ final children = <Widget>[
|
|
|
+ Row(children: [
|
|
|
+ Expanded(
|
|
|
+ child: Text(
|
|
|
+ '${info.name} (${info.userList.length}人)',
|
|
|
+ maxLines: 1,
|
|
|
+ )),
|
|
|
+ const SizedBox(
|
|
|
+ width: 8,
|
|
|
+ ),
|
|
|
+ IconButton(
|
|
|
+ onPressed: () {
|
|
|
+ info.isHide.value = !info.isHide.value;
|
|
|
+ },
|
|
|
+ icon: info.isHide.value
|
|
|
+ ? const Icon(Icons.arrow_drop_down)
|
|
|
+ : const Icon(Icons.arrow_drop_up))
|
|
|
+ ]),
|
|
|
+ ];
|
|
|
+
|
|
|
+ if (!info.isHide.value) {
|
|
|
+ children.addAll([
|
|
|
+ Container(
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Colors.white, borderRadius: BorderRadius.circular(5)),
|
|
|
+ padding: const EdgeInsets.fromLTRB(26, 11, 26, 11),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ const Text('广播'),
|
|
|
+ const Spacer(),
|
|
|
+ Image.asset(Assets.imagesIcCp, height: 20, width: 20),
|
|
|
+ Text(' ${info.cpAllCount}'),
|
|
|
+ const Spacer(),
|
|
|
+ const Text('全部隐藏'),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ ]);
|
|
|
+ children
|
|
|
+ .addAll(info.userList.map((e) => _UserInfoView(data: e)).toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ return Container(
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: const Color(0xffe0e0e0),
|
|
|
+ borderRadius: BorderRadius.circular(5)),
|
|
|
+ margin: const EdgeInsets.fromLTRB(9, 12, 9, 12),
|
|
|
+ padding: const EdgeInsets.all(9),
|
|
|
+ child: Column(
|
|
|
+ children: children,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _UserInfoView extends GetView<FieldControlController> {
|
|
|
+ const _UserInfoView({required this.data});
|
|
|
+
|
|
|
+ final UserOnMap data;
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Container(
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: Colors.white, borderRadius: BorderRadius.circular(5)),
|
|
|
+ padding: const EdgeInsets.fromLTRB(7, 11, 7, 11),
|
|
|
+ margin: const EdgeInsets.only(top: 5),
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Row(
|
|
|
+ children: [
|
|
|
+ Obx(() => Container(
|
|
|
+ margin: const EdgeInsets.only(top: 2),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: data.flag.value.color,
|
|
|
+ borderRadius: BorderRadius.circular(4)),
|
|
|
+ width: 7,
|
|
|
+ height: 16)),
|
|
|
+ const SizedBox(
|
|
|
+ width: 8,
|
|
|
+ ),
|
|
|
+ Expanded(
|
|
|
+ child: Text.rich(TextSpan(
|
|
|
+ text: data.name,
|
|
|
+ children: [TextSpan(text: ' [${data.routeName}]')])),
|
|
|
+ ),
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ final oldId = controller.focusUserId.value;
|
|
|
+ if (oldId != null) {
|
|
|
+ if (oldId == data.id) {
|
|
|
+ controller.focusUserId.value = null;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ controller.focusUserId.value = data.id;
|
|
|
+ },
|
|
|
+ child: Obx(() => Icon(
|
|
|
+ Icons.route,
|
|
|
+ color: data.id != controller.focusUser?.id
|
|
|
+ ? Colors.grey
|
|
|
+ : const Color(0xffffbb77),
|
|
|
+ )),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 8),
|
|
|
+ GestureDetector(
|
|
|
+ onTap: () {
|
|
|
+ data.isHide.value = !data.isHide.value;
|
|
|
+ },
|
|
|
+ child: Obx(() => Icon(
|
|
|
+ data.isHide.value
|
|
|
+ ? Icons.visibility_off
|
|
|
+ : Icons.visibility,
|
|
|
+ color: data.isHide.value
|
|
|
+ ? Colors.grey
|
|
|
+ : const Color(0xffffbb77),
|
|
|
+ )),
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ Container(
|
|
|
+ margin: const EdgeInsets.only(left: 14),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ container(null, cpInfo, Colors.blue),
|
|
|
+ container(
|
|
|
+ const Icon(
|
|
|
+ Icons.favorite,
|
|
|
+ size: 13,
|
|
|
+ color: Colors.white,
|
|
|
+ ),
|
|
|
+ ' ${hr == 0 ? '--' : hr}',
|
|
|
+ data.heartRatePercent.toHRPColor()),
|
|
|
+ container(null, paceInfo, data.pace.color)
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 5),
|
|
|
+ Container(
|
|
|
+ margin: const EdgeInsets.only(left: 14),
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
+ children: [
|
|
|
+ Text('距离 ${data.nextDistance.toString()}'),
|
|
|
+ Text('时间 ${data.duration.toAppString()}'),
|
|
|
+ Text('里程 ${data.distance.toString()}'),
|
|
|
+ ],
|
|
|
+ ))
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ int get hr {
|
|
|
+ return data.hrInfo.lastOrNull?.hr ?? 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ String get cpInfo {
|
|
|
+ final next = data.nextWant;
|
|
|
+ return next != null ? '${data.nextCPSN}点(${next.areaId})' : '--';
|
|
|
+ }
|
|
|
+
|
|
|
+ String get paceInfo {
|
|
|
+ Duration;
|
|
|
+ return data.pace.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget container(Widget? icon, String text, Color color) {
|
|
|
+ final children = <Widget>[];
|
|
|
+ if (icon != null) {
|
|
|
+ children.add(icon);
|
|
|
+ }
|
|
|
+ children.add(
|
|
|
+ Text(text, style: const TextStyle(color: Colors.white, fontSize: 14)));
|
|
|
+
|
|
|
+ return Container(
|
|
|
+ height: 20,
|
|
|
+ padding: const EdgeInsets.fromLTRB(10, 0, 12, 0),
|
|
|
+ margin: const EdgeInsets.only(right: 6),
|
|
|
+ alignment: Alignment.center,
|
|
|
+ decoration:
|
|
|
+ BoxDecoration(color: color, borderRadius: BorderRadius.circular(9)),
|
|
|
+ child: Row(
|
|
|
+ children: children,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _MsgView extends GetView<FieldControlController> {
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Container();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class FieldControlController extends GetxController {
|
|
|
+ @override
|
|
|
+ void onInit() {
|
|
|
+ super.onInit();
|
|
|
+ final map = mapWatch;
|
|
|
+ if (map != null) {
|
|
|
+ eventList.bindStream(map.eventList.stream);
|
|
|
+ }
|
|
|
+
|
|
|
+ _subActive = eventList.listen((p0) {
|
|
|
+ final user = focusUser;
|
|
|
+ if (user != null) {
|
|
|
+ viewCpController.setCPList(user.cpList);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onClose() {
|
|
|
+ super.onClose();
|
|
|
+ _subActive?.cancel();
|
|
|
+ }
|
|
|
+
|
|
|
+ final viewCpController = ViewMapCPController();
|
|
|
+ StreamSubscription<List<EventOnMap>>? _subActive;
|
|
|
+ final _mapService = Get.find<MapWatchService>();
|
|
|
+ MapWatch? get mapWatch => _mapService.instance;
|
|
|
+ final eventList = <EventOnMap>[].obs;
|
|
|
+ final Rx<int?> focusUserId = Rx(null);
|
|
|
+
|
|
|
+ UserOnMap? findFocusUser(List<EventOnMap> list) {
|
|
|
+ if (focusUserId.value == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (final act in list) {
|
|
|
+ for (final user in act.userList) {
|
|
|
+ if (user.id == focusUserId.value) {
|
|
|
+ return user;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ UserOnMap? get focusUser {
|
|
|
+ return findFocusUser(eventList);
|
|
|
+ }
|
|
|
+}
|