Преглед изворни кода

[new]完善首页当日行动轨迹功能

zk пре 5 месеци
родитељ
комит
db4b40e27e
37 измењених фајлова са 939 додато и 140 уклоњено
  1. BIN
      assets/images/bg_location_analyse.webp
  2. BIN
      assets/images/bg_location_analyse_ai.webp
  3. BIN
      assets/images/img_track_example.webp
  4. 3 0
      assets/string/base/string.xml
  5. BIN
      assets/video/location_analyse_robot.mp4
  6. 7 0
      lib/data/api/atmob_api.dart
  7. 38 0
      lib/data/api/atmob_api.g.dart
  8. 20 0
      lib/data/api/request/track_daily_action_request.dart
  9. 71 0
      lib/data/api/request/track_daily_action_request.g.dart
  10. 27 0
      lib/data/api/response/track_daily_action_response.dart
  11. 12 0
      lib/data/repositories/track_repository.dart
  12. 13 9
      lib/di/get_it.config.dart
  13. 35 0
      lib/module/analyse/location_analyse_controller.dart
  14. 117 0
      lib/module/analyse/location_analyse_page.dart
  15. 33 9
      lib/module/main/main_controller.dart
  16. 138 64
      lib/module/main/main_page.dart
  17. 119 0
      lib/module/main/today_track_helper.dart
  18. 22 16
      lib/module/main/view.dart
  19. 14 5
      lib/module/track/track_controller.dart
  20. 7 1
      lib/module/track/track_day_detail/track_daily_item.dart
  21. 21 5
      lib/resource/assets.gen.dart
  22. 6 0
      lib/resource/string.gen.dart
  23. 3 0
      lib/router/app_pages.dart
  24. 12 0
      lib/utils/date_util.dart
  25. 1 0
      plugins/map/lib/flutter_map.dart
  26. 3 1
      plugins/map/lib/src/consts/map_constants.dart
  27. 2 1
      plugins/map/lib/src/consts/polyline_type.dart
  28. 43 10
      plugins/map/lib/src/core/map_controller.dart
  29. 22 0
      plugins/map/lib/src/entity/polyline.dart
  30. 6 0
      plugins/map/lib/src/interface/map_fun_interface.dart
  31. 3 2
      plugins/map/lib/src/interface/map_polyline_interface.dart
  32. 79 5
      plugins/map/lib/src/widget/map_widget.dart
  33. 30 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/MapController.java
  34. 3 1
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java
  35. 23 10
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/polyline/PolylineController.java
  36. 2 1
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/polyline/PolylineType.java
  37. 4 0
      pubspec.yaml

BIN
assets/images/bg_location_analyse.webp


BIN
assets/images/bg_location_analyse_ai.webp


BIN
assets/images/img_track_example.webp


+ 3 - 0
assets/string/base/string.xml

@@ -345,4 +345,7 @@
     <string name="track_daily_summary_track">轨迹情况</string>
     <string name="track_daily_call_phone">联系TA</string>
     <string name="track_daily_skip_call_phone_fail">跳转拨号界面失败</string>
+
+    <string name="main_today_track_loading">正在加载中...</string>
+    <string name="main_today_track_normal_point">暂无异常</string>
 </resources>

BIN
assets/video/location_analyse_robot.mp4


+ 7 - 0
lib/data/api/atmob_api.dart

@@ -18,6 +18,7 @@ import 'package:location/data/api/request/send_code_request.dart';
 import 'package:location/data/api/request/submit_and_request_pay_request.dart';
 import 'package:location/data/api/request/subscription_check_request.dart';
 import 'package:location/data/api/request/subscription_resume_request.dart';
+import 'package:location/data/api/request/track_daily_action_request.dart';
 import 'package:location/data/api/request/upload_client_id_request.dart';
 import 'package:location/data/api/request/user_avatar_update_request.dart';
 import 'package:location/data/api/response/configs_response.dart';
@@ -36,6 +37,7 @@ import 'package:location/data/api/response/query_track_response.dart';
 import 'package:location/data/api/response/request_friend_list_response.dart';
 import 'package:location/data/api/response/request_pay_response.dart';
 import 'package:location/data/api/response/subscription_check_response.dart';
+import 'package:location/data/api/response/track_daily_action_response.dart';
 import 'package:location/data/api/response/track_daily_dialogs_response.dart';
 import 'package:location/data/api/response/track_daily_response.dart';
 import 'package:location/data/api/response/track_daily_summary_response.dart';
@@ -229,4 +231,9 @@ abstract class AtmobApi {
   @POST("/s/v1/location/track/daily/dialogs")
   Future<BaseResponse<TrackDailyDialogsResponse>> locationTrackDailyDialogs(
       @Body() AppBaseRequest request);
+
+  @POST("/s/v1/location/track/daily/action")
+  Future<BaseResponse<TrackDailyActionResponse>> trackDailyAction(
+      @Body() TrackDailyActionRequest request,
+      @DioOptions() RequestOptions options);
 }

+ 38 - 0
lib/data/api/atmob_api.g.dart

@@ -1740,6 +1740,44 @@ class _AtmobApi implements AtmobApi {
     return _value;
   }
 
+  @override
+  Future<BaseResponse<TrackDailyActionResponse>> trackDailyAction(
+    TrackDailyActionRequest request,
+    RequestOptions options,
+  ) async {
+    final _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _headers = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    _data.addAll(request.toJson());
+    final newOptions = newRequestOptions(options);
+    newOptions.extra.addAll(_extra);
+    newOptions.headers.addAll(_dio.options.headers);
+    newOptions.headers.addAll(_headers);
+    final _options = newOptions.copyWith(
+      method: 'POST',
+      baseUrl: _combineBaseUrls(
+        _dio.options.baseUrl,
+        baseUrl,
+      ),
+      queryParameters: queryParameters,
+      path: '/s/v1/location/track/daily/action',
+    )..data = _data;
+    final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+    late BaseResponse<TrackDailyActionResponse> _value;
+    try {
+      _value = BaseResponse<TrackDailyActionResponse>.fromJson(
+        _result.data!,
+        (json) =>
+            TrackDailyActionResponse.fromJson(json as Map<String, dynamic>),
+      );
+    } on Object catch (e, s) {
+      errorLogger?.logError(e, s, _options);
+      rethrow;
+    }
+    return _value;
+  }
+
   RequestOptions newRequestOptions(Object? options) {
     if (options is RequestOptions) {
       return options as RequestOptions;

+ 20 - 0
lib/data/api/request/track_daily_action_request.dart

@@ -0,0 +1,20 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:location/base/app_base_request.dart';
+
+part 'track_daily_action_request.g.dart';
+
+@JsonSerializable()
+class TrackDailyActionRequest extends AppBaseRequest {
+  @JsonKey(name: 'userId')
+  String? userId;
+
+  TrackDailyActionRequest(
+    this.userId,
+  );
+
+  factory TrackDailyActionRequest.fromJson(Map<String, dynamic> json) =>
+      _$TrackDailyActionRequestFromJson(json);
+
+  @override
+  Map<String, dynamic> toJson() => _$TrackDailyActionRequestToJson(this);
+}

+ 71 - 0
lib/data/api/request/track_daily_action_request.g.dart

@@ -0,0 +1,71 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'track_daily_action_request.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+TrackDailyActionRequest _$TrackDailyActionRequestFromJson(
+        Map<String, dynamic> json) =>
+    TrackDailyActionRequest(
+      json['userId'] as String?,
+    )
+      ..appPlatform = (json['appPlatform'] as num).toInt()
+      ..os = json['os'] as String
+      ..osVersion = json['osVersion'] as String
+      ..packageName = json['packageName'] as String?
+      ..appVersionName = json['appVersionName'] as String?
+      ..appVersionCode = (json['appVersionCode'] as num?)?.toInt()
+      ..channelName = json['channelName'] as String?
+      ..appId = (json['appId'] as num?)?.toInt()
+      ..tgPlatform = (json['tgPlatform'] as num?)?.toInt()
+      ..oaid = json['oaid'] as String?
+      ..aaid = json['aaid'] as String?
+      ..androidId = json['androidId'] as String?
+      ..imei = json['imei'] as String?
+      ..simImei0 = json['simImei0'] as String?
+      ..simImei1 = json['simImei1'] as String?
+      ..mac = json['mac'] as String?
+      ..idfa = json['idfa'] as String?
+      ..idfv = json['idfv'] as String?
+      ..machineId = json['machineId'] as String?
+      ..brand = json['brand'] as String?
+      ..model = json['model'] as String?
+      ..wifiName = json['wifiName'] as String?
+      ..region = json['region'] as String?
+      ..locLng = (json['locLng'] as num?)?.toDouble()
+      ..locLat = (json['locLat'] as num?)?.toDouble()
+      ..authToken = json['authToken'] as String?;
+
+Map<String, dynamic> _$TrackDailyActionRequestToJson(
+        TrackDailyActionRequest instance) =>
+    <String, dynamic>{
+      'appPlatform': instance.appPlatform,
+      'os': instance.os,
+      'osVersion': instance.osVersion,
+      'packageName': instance.packageName,
+      'appVersionName': instance.appVersionName,
+      'appVersionCode': instance.appVersionCode,
+      'channelName': instance.channelName,
+      'appId': instance.appId,
+      'tgPlatform': instance.tgPlatform,
+      'oaid': instance.oaid,
+      'aaid': instance.aaid,
+      'androidId': instance.androidId,
+      'imei': instance.imei,
+      'simImei0': instance.simImei0,
+      'simImei1': instance.simImei1,
+      'mac': instance.mac,
+      'idfa': instance.idfa,
+      'idfv': instance.idfv,
+      'machineId': instance.machineId,
+      'brand': instance.brand,
+      'model': instance.model,
+      'wifiName': instance.wifiName,
+      'region': instance.region,
+      'locLng': instance.locLng,
+      'locLat': instance.locLat,
+      'authToken': instance.authToken,
+      'userId': instance.userId,
+    };

+ 27 - 0
lib/data/api/response/track_daily_action_response.dart

@@ -0,0 +1,27 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/atmob_track_point.dart';
+import '../../bean/track_daily_bean.dart';
+
+part 'track_daily_action_response.g.dart';
+
+@JsonSerializable()
+class TrackDailyActionResponse {
+  @JsonKey(name: 'startPoint')
+  TrackDailyBean? startPoint;
+
+  @JsonKey(name: 'exceptionPoint')
+  TrackDailyBean? exceptionPoint;
+
+  @JsonKey(name: 'list')
+  List<AtmobTrackPoint>? trackPoints;
+
+  TrackDailyActionResponse({
+    this.startPoint,
+    this.exceptionPoint,
+    this.trackPoints,
+  });
+
+  factory TrackDailyActionResponse.fromJson(Map<String, dynamic> json) =>
+      _$TrackDailyActionResponseFromJson(json);
+}

+ 12 - 0
lib/data/repositories/track_repository.dart

@@ -9,8 +9,10 @@ import '../../di/get_it.dart';
 import '../../utils/http_handler.dart';
 import '../../utils/sse_parse_util.dart';
 import '../api/request/query_track_request.dart';
+import '../api/request/track_daily_action_request.dart';
 import '../api/response/location_track_days_response.dart';
 import '../api/response/query_track_response.dart';
+import '../api/response/track_daily_action_response.dart';
 import '../api/response/track_daily_dialogs_response.dart';
 import '../api/response/track_daily_summary_response.dart';
 import '../bean/track_daily_bean.dart';
@@ -116,6 +118,16 @@ class TrackRepository {
         .then(HttpHandler.handle(true));
   }
 
+  Future<TrackDailyActionResponse> trackDailyAction(String? userId) {
+    return atmobApi
+        .trackDailyAction(
+            TrackDailyActionRequest(userId),
+            RequestOptions(
+                receiveTimeout: Duration(seconds: 30),
+                connectTimeout: Duration(minutes: 2)))
+        .then(HttpHandler.handle(true));
+  }
+
   Future<Stream<Message>> streamDailySummary(
       {required int? startTime,
       required int? endTime,

+ 13 - 9
lib/di/get_it.config.dart

@@ -30,6 +30,7 @@ import '../module/friend/friend_controller.dart' as _i821;
 import '../module/friend/setting/friend_setting_controller.dart' as _i492;
 import '../module/login/login_controller.dart' as _i1008;
 import '../module/main/main_controller.dart' as _i731;
+import '../module/main/today_track_helper.dart' as _i683;
 import '../module/member/member_controller.dart' as _i269;
 import '../module/mine/mine_controller.dart' as _i732;
 import '../module/news/news_controller.dart' as _i489;
@@ -130,15 +131,6 @@ extension GetItInjectableX on _i174.GetIt {
           gh<_i825.ConfigRepository>(),
           gh<_i814.MemberRepository>(),
         ));
-    gh.factory<_i731.MainController>(() => _i731.MainController(
-          gh<_i1053.FriendsRepository>(),
-          gh<_i20.AccountRepository>(),
-          gh<_i791.MessageRepository>(),
-          gh<_i220.AtmobLocationClient>(),
-          gh<_i983.UrgentContactRepository>(),
-          gh<_i825.ConfigRepository>(),
-          gh<_i240.TrackRepository>(),
-        ));
     gh.lazySingleton<_i779.PaymentStatusManager>(
         () => _i779.PaymentStatusManager(gh<_i814.MemberRepository>()));
     gh.factory<_i518.TrackController>(() => _i518.TrackController(
@@ -146,11 +138,23 @@ extension GetItInjectableX on _i174.GetIt {
           gh<_i1053.FriendsRepository>(),
           gh<_i20.AccountRepository>(),
         ));
+    gh.lazySingleton<_i683.TodayTrackHelper>(
+        () => _i683.TodayTrackHelper(gh<_i240.TrackRepository>()));
     gh.factory<_i269.MemberController>(() => _i269.MemberController(
           gh<_i20.AccountRepository>(),
           gh<_i814.MemberRepository>(),
           gh<_i779.PaymentStatusManager>(),
         ));
+    gh.factory<_i731.MainController>(() => _i731.MainController(
+          gh<_i1053.FriendsRepository>(),
+          gh<_i20.AccountRepository>(),
+          gh<_i791.MessageRepository>(),
+          gh<_i683.TodayTrackHelper>(),
+          gh<_i220.AtmobLocationClient>(),
+          gh<_i983.UrgentContactRepository>(),
+          gh<_i825.ConfigRepository>(),
+          gh<_i240.TrackRepository>(),
+        ));
     return this;
   }
 }

+ 35 - 0
lib/module/analyse/location_analyse_controller.dart

@@ -0,0 +1,35 @@
+import 'package:flutter/cupertino.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+import 'package:location/base/base_controller.dart';
+import 'package:video_player/video_player.dart';
+
+import '../../resource/assets.gen.dart';
+
+class LocationAnalyseController extends BaseController {
+  late final VideoPlayerController bgController;
+
+  final RxBool _videoReady = RxBool(false);
+
+  bool get videoReady => _videoReady.value;
+
+  @override
+  void onInit() {
+    super.onInit();
+    bgController = VideoPlayerController.asset(
+      Assets.video.locationAnalyseRobot,
+    )
+      ..setLooping(true)
+      ..setVolume(0.0)
+      ..initialize().then((_) {
+        _videoReady.value = true;
+        bgController.play();
+      }).catchError((error) {
+        debugPrint('Error initializing video: $error');
+      });
+  }
+
+  void back() {
+    Get.back();
+  }
+}

+ 117 - 0
lib/module/analyse/location_analyse_page.dart

@@ -0,0 +1,117 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get.dart';
+import 'package:get/get_core/src/get_main.dart';
+import 'package:location/base/base_page.dart';
+import 'package:location/resource/colors.gen.dart';
+import 'package:location/router/app_pages.dart';
+import 'package:location/utils/common_expand.dart';
+import 'package:video_player/video_player.dart';
+import '../../resource/assets.gen.dart';
+import '../../widget/common_view.dart';
+import 'location_analyse_controller.dart';
+
+class LocationAnalysePage extends BasePage<LocationAnalyseController> {
+  LocationAnalysePage({super.key}) {
+    Get.lazyPut(() => LocationAnalyseController(), fenix: true);
+  }
+
+  static void start() {
+    Get.toNamed(RoutePath.locationAnalyse);
+  }
+
+  @override
+  bool immersive() {
+    return true;
+  }
+
+  @override
+  Widget buildBody(BuildContext context) {
+    return Container(
+      width: double.infinity,
+      height: double.infinity,
+      decoration: BoxDecoration(
+          gradient: LinearGradient(colors: [
+        ColorName.white,
+        '#D8D8FE'.color,
+      ], begin: Alignment.topCenter, end: Alignment.bottomCenter)),
+      child: Stack(
+        children: [buildBackBtnView(), buildAnalyseView()],
+      ),
+    );
+  }
+
+  Widget buildAnalyseView() {
+    return SizedBox(
+      width: double.infinity,
+      height: double.infinity,
+      child: SingleChildScrollView(
+          child: Column(
+        children: [
+          buildAIAnalyseView(),
+        ],
+      )),
+    );
+  }
+
+  Widget buildAIAnalyseView() {
+    return Stack(
+      alignment: Alignment.center,
+      children: [
+        Assets.images.bgLocationAnalyse.image(width: double.infinity),
+        buildRobotView(),
+      ],
+    );
+  }
+
+  Widget buildRobotView() {
+    return SizedBox(
+      width: 248.w,
+      height: 248.w,
+      child: Stack(
+        children: [
+          Assets.images.bgLocationAnalyseAi
+              .image(width: double.infinity, height: double.infinity),
+          Center(
+            child: ClipOval(
+              child: SizedBox(
+                width: 144.w,
+                height: 144.w,
+                child: Obx(() {
+                  if (!controller.videoReady) {
+                    return Center(child: Text('视频还没准备好'));
+                  }
+                  return Transform.scale(
+                    scale: 1.012, // 放大 3%,可以试试 1.01 ~ 1.05
+                    child: VideoPlayer(controller.bgController),
+                  );
+                }),
+              ),
+            ),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget buildBackBtnView() {
+    return SafeArea(
+      child: GestureDetector(
+        onTap: controller.back,
+        child: Container(
+          margin: EdgeInsets.only(top: 14.w, left: 12.w),
+          decoration: BoxDecoration(boxShadow: [
+            BoxShadow(
+              color: Colors.black.withOpacity(0.05),
+              blurRadius: 10.w,
+              offset: Offset(0, 2.w),
+            ),
+          ]),
+          child: CommonView.getBackBtnView(),
+        ),
+      ),
+    );
+  }
+}

+ 33 - 9
lib/module/main/main_controller.dart

@@ -17,6 +17,7 @@ import 'package:location/data/repositories/message_repository.dart';
 import 'package:location/handler/error_handler.dart';
 import 'package:location/module/friend/friend_page.dart';
 import 'package:location/module/login/login_page.dart';
+import 'package:location/module/main/today_track_helper.dart';
 import 'package:location/module/member/member_page.dart';
 import 'package:location/module/news/news_page.dart';
 import 'package:location/module/urgent_contact/urgent_contact_page.dart';
@@ -66,6 +67,7 @@ class MainController extends BaseController {
   UserInfo? get selectedFriend => _selectedFriend.value;
 
   final MapController mapController = MapController();
+  final MapController todayTrackSmallMapController = MapController();
 
   StreamSubscription? mineUserInfoSubscription;
   StreamSubscription? mineLocationSubscription;
@@ -81,6 +83,7 @@ class MainController extends BaseController {
   final MessageRepository messageRepository;
   final UrgentContactRepository urgentContactRepository;
   final TrackRepository trackRepository;
+  final TodayTrackHelper todayTrackHelper;
 
   bool get hasUnreadMessage => messageRepository.hasUnreadMessage.value;
 
@@ -90,15 +93,16 @@ class MainController extends BaseController {
   String? lastCheckFriendId;
   bool isExecuteAutoSelect = false;
 
-  final RxBool _todayTrackRequesting = RxBool(false);
-  double? _friendTrackVisibleFraction;
+  RxMap<String, TodayTrackReportBean> get todayTrackReportMap =>
+      todayTrackHelper.todayTrackReportMap;
 
-  bool get todayTrackRequesting => _todayTrackRequesting.value;
+  double? _friendTrackVisibleFraction;
 
   MainController(
       this.friendsRepository,
       this.accountRepository,
       this.messageRepository,
+      this.todayTrackHelper,
       AtmobLocationClient atmobLocationClient,
       this.urgentContactRepository,
       ConfigRepository configRepository,
@@ -164,11 +168,15 @@ class MainController extends BaseController {
   void _refreshTrackDailyDialogs() {
     trackRepository.locationTrackDailyDialogs().then((trackResponse) {
       if (trackResponse.show ?? false) {
-        TrackDailyReportDialog.show(trackDailyList: trackResponse.trackDailyList,confirmOnTap: () {
-          TrackChooseFriendDialog.show(trackDailyList: trackResponse.trackDailyList,onSelectItem: (selectInfo) {
-            onViewTraceClick(selectInfo);
-          });
-        });
+        TrackDailyReportDialog.show(
+            trackDailyList: trackResponse.trackDailyList,
+            confirmOnTap: () {
+              TrackChooseFriendDialog.show(
+                  trackDailyList: trackResponse.trackDailyList,
+                  onSelectItem: (selectInfo) {
+                    onViewTraceClick(selectInfo);
+                  });
+            });
       }
     }).catchError((error) {
       ErrorHandler.toastError(error);
@@ -453,13 +461,29 @@ class MainController extends BaseController {
   }
 
   void onFriendVisibleFraction(double visibleFraction) {
+    AtmobLog.d('zk', 'onFriendVisibleFraction: $visibleFraction');
     _friendTrackVisibleFraction = visibleFraction;
     if (visibleFraction > 0.2) {
       _requestSelectedFriendTrack();
     }
   }
 
-  void _requestSelectedFriendTrack() {}
+  void _requestSelectedFriendTrack() {
+    //查看当前选中的好友是否有记录数据
+    final selectedFriend = _selectedFriend.value;
+    if (selectedFriend == null) {
+      return;
+    }
+    todayTrackHelper.requestTodayTrackReport(selectedFriend.id);
+  }
+
+  void onTodayTraceClick() {
+    final selectedUserInfo = _selectedFriend.value;
+    if (selectedUserInfo == null) {
+      return;
+    }
+    onViewTraceClick(selectedUserInfo);
+  }
 
   @override
   void onClose() {

+ 138 - 64
lib/module/main/main_page.dart

@@ -8,11 +8,11 @@ import 'package:get/get.dart';
 import 'package:location/base/base_page.dart';
 import 'package:location/data/bean/user_info.dart';
 import 'package:location/module/main/main_controller.dart';
+import 'package:location/module/main/today_track_helper.dart';
 import 'package:location/module/main/view.dart';
 import 'package:location/resource/assets.gen.dart';
 import 'package:location/resource/colors.gen.dart';
 import 'package:location/resource/string.gen.dart';
-import 'package:location/utils/atmob_log.dart';
 import 'package:location/utils/common_expand.dart';
 import 'package:sliding_sheet2/sliding_sheet2.dart';
 import 'package:visibility_detector/visibility_detector.dart';
@@ -112,80 +112,154 @@ class MainPage extends BasePage<MainController> {
             color: Colors.white, borderRadius: BorderRadius.circular(20.r)),
         child: AspectRatio(
           aspectRatio: 336 / 134,
-          child: Column(
+          child: Obx(() {
+            final todayTrack = controller.selectedFriend?.id == null
+                ? null
+                : controller.todayTrackReportMap[controller.selectedFriend?.id];
+            if (todayTrack == null ||
+                todayTrack.isRequestSuccess.value == false) {
+              return buildTodayTrackLoadingView();
+            } else {
+              return buildTodayTrackDetailView(todayTrack);
+            }
+          }),
+        ),
+      ),
+    );
+  }
+
+  Widget buildTodayTrackLoadingView() {
+    return Column(
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        CupertinoActivityIndicator(
+          color: '#A3A3A5'.color,
+          radius: 16.w,
+        ),
+        SizedBox(height: 15.w),
+        Text(StringName.mainTodayTrackLoading,
+            style: TextStyle(
+                fontSize: 14.sp, color: '#666666'.color.withOpacity(0.87)))
+      ],
+    );
+  }
+
+  Widget buildTodayTrackDetailView(TodayTrackReportBean todayTrackReportBean) {
+    return GestureDetector(
+      behavior: HitTestBehavior.opaque,
+      onTap: controller.onTodayTraceClick,
+      child: Column(
+        children: [
+          Row(
             children: [
-              Row(
-                children: [
-                  Text(StringName.todaySimpleTrack,
-                      style: TextStyle(
-                          fontSize: 13.sp,
-                          color: '#333333'.color,
-                          fontWeight: FontWeight.bold)),
-                  Spacer(),
-                  Assets.images.iconMainTrackArrow
-                      .image(width: 10.w, height: 10.w),
-                ],
-              ),
-              SizedBox(height: 7.w),
-              buildSelectedFriendTodayTrackDetailView(),
+              Text(StringName.todaySimpleTrack,
+                  style: TextStyle(
+                      fontSize: 13.sp,
+                      color: '#333333'.color,
+                      fontWeight: FontWeight.bold)),
+              Spacer(),
+              Assets.images.iconMainTrackArrow.image(width: 10.w, height: 10.w),
             ],
           ),
-        ),
+          SizedBox(height: 7.w),
+          buildSelectedFriendTodayTrackDetailView(todayTrackReportBean),
+        ],
       ),
     );
   }
 
-  Widget buildSelectedFriendTodayTrackDetailView() {
-    return Expanded(child: Obx(() {
-      if (controller.todayTrackRequesting) {
-        return Center(child: Text('加载中...'));
-      } else {
-        return Row(
+  Widget buildSelectedFriendTodayTrackDetailView(
+      TodayTrackReportBean todayTrackReportBean) {
+    final startAddr = todayTrackReportBean.startPoint?.addr;
+
+    final errorAddr = todayTrackReportBean.exceptionPoint?.addr;
+
+    MainTrackType startPointType = startAddr?.isNotEmpty == true
+        ? MainTrackType.startPoint
+        : MainTrackType.normalPoint;
+
+    MainTrackType errorPointType = errorAddr?.isNotEmpty == true
+        ? MainTrackType.errorPoint
+        : MainTrackType.normalPoint;
+
+    Color startPointColor;
+    if (startPointType == MainTrackType.startPoint) {
+      startPointColor = '#15CBA1'.color;
+    } else if (startPointType == MainTrackType.errorPoint) {
+      startPointColor = '#E94949'.color;
+    } else {
+      startPointColor = '#D6D6D6'.color;
+    }
+
+    Color errorPointColor;
+    if (errorPointType == MainTrackType.errorPoint) {
+      errorPointColor = '#E94949'.color;
+    } else {
+      errorPointColor = '#D6D6D6'.color;
+    }
+
+    return Expanded(
+        child: Row(
+      children: [
+        Container(
+          margin: EdgeInsets.only(bottom: 3.w, top: 3.w),
+          child: AspectRatio(
+            aspectRatio: 1,
+            child: buildTodayMapView(todayTrackReportBean),
+          ),
+        ),
+        SizedBox(width: 10.w),
+        Column(
+          mainAxisAlignment: MainAxisAlignment.center,
           children: [
-            Container(
-              margin: EdgeInsets.only(bottom: 3.w, top: 3.w),
-              child: AspectRatio(
-                aspectRatio: 1,
-                child: Assets.images.imgTrackExample.image(),
-              ),
-            ),
-            SizedBox(width: 10.w),
-            Column(
-              mainAxisAlignment: MainAxisAlignment.center,
-              children: [
-                SizedBox(height: 6.w),
-                getMainTrackDot('#15CBA1'.color),
-                Expanded(
-                  child: Container(
-                    margin: EdgeInsets.symmetric(vertical: 4.w),
-                    width: 1.w,
-                    height: double.infinity,
-                    decoration: BoxDecoration(
-                      color: '#F0F0F0'.color,
-                      borderRadius: BorderRadius.circular(100.r),
-                    ),
-                  ),
-                ),
-                getMainTrackDot('#E94949'.color),
-                SizedBox(height: 6.w),
-              ],
-            ),
-            SizedBox(width: 8.w),
+            SizedBox(height: 6.w),
+            getMainTrackDot(startPointColor),
             Expanded(
-              child: Column(
-                children: [
-                  buildTrackPoint(MainTrackType.startPoint,
-                      '广东省广州市天河区高唐路235号靠近C栋2楼时代E-PARK'),
-                  Spacer(),
-                  buildTrackPoint(
-                      MainTrackType.errorPoint, '广东省广州市海珠区滨江东路驾驶花园C栋601'),
-                ],
+              child: Container(
+                margin: EdgeInsets.symmetric(vertical: 4.w),
+                width: 1.w,
+                height: double.infinity,
+                decoration: BoxDecoration(
+                  color: '#F0F0F0'.color,
+                  borderRadius: BorderRadius.circular(100.r),
+                ),
               ),
-            )
+            ),
+            getMainTrackDot(errorPointColor),
+            SizedBox(height: 6.w),
           ],
-        );
-      }
-    }));
+        ),
+        SizedBox(width: 8.w),
+        Expanded(
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: [
+              Visibility(
+                  visible: startPointType == MainTrackType.normalPoint,
+                  child: SizedBox(height: 2.w)),
+              buildTrackPoint(startPointType, startAddr ?? ''),
+              Spacer(),
+              buildTrackPoint(errorPointType, errorAddr ?? ''),
+              Visibility(
+                  visible: errorPointType == MainTrackType.normalPoint,
+                  child: SizedBox(height: 5.w))
+            ],
+          ),
+        )
+      ],
+    ));
+  }
+
+  Widget buildTodayMapView(TodayTrackReportBean todayTrackReportBean) {
+    return ClipRRect(
+        borderRadius: BorderRadius.circular(8.r),
+        child: MapWidget(
+          isShowLogo: false,
+          interactionIsEnabled: false,
+          mapPadding: todayTrackReportBean.mapPadding,
+          markers: todayTrackReportBean.markers,
+          polyline: todayTrackReportBean.polylines,
+        ));
   }
 
   Widget buildFriendListView() {

+ 119 - 0
lib/module/main/today_track_helper.dart

@@ -0,0 +1,119 @@
+import 'package:flutter_map/flutter_map.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
+import 'package:get/get_rx/src/rx_types/rx_types.dart';
+import 'package:injectable/injectable.dart';
+import 'package:location/data/repositories/track_repository.dart';
+import '../../data/bean/atmob_track_point.dart';
+import '../../data/bean/track_daily_bean.dart';
+
+@lazySingleton
+class TodayTrackHelper {
+  final RxMap<String, TodayTrackReportBean> todayTrackReportMap =
+      <String, TodayTrackReportBean>{}.obs;
+
+  final TrackRepository trackRepository;
+
+  TodayTrackHelper(this.trackRepository);
+
+  void requestTodayTrackReport(String userId) {
+    todayTrackReportMap.putIfAbsent(userId, () => TodayTrackReportBean(userId));
+    //确保一定有实例
+    final reportBean = todayTrackReportMap[userId]!;
+
+    if (reportBean.isRequesting) {
+      // 正在请求中,跳过
+      return;
+    }
+    if (reportBean.isRequestSuccess.value && !reportBean.isExpired) return;
+
+    //重置数据
+    reportBean.polylines = null;
+    reportBean.markers = null;
+    reportBean.trackPoints = null;
+    reportBean.startPoint = null;
+    reportBean.exceptionPoint = null;
+    reportBean.isRequestSuccess.value = false;
+
+    _requestTodayTrackReport(userId, reportBean);
+  }
+
+  void _requestTodayTrackReport(String userId, TodayTrackReportBean bean) {
+    bean.isRequesting = true;
+    trackRepository.trackDailyAction(userId).then((response) {
+      bean.startPoint = response.startPoint;
+      bean.exceptionPoint = response.exceptionPoint;
+      bean.trackPoints = response.trackPoints;
+
+      List<Marker> list = [];
+      final startPoint = response.startPoint;
+      if (startPoint != null && startPoint.addr?.isNotEmpty == true) {
+        list.add(Marker(
+            id: 'start',
+            markerName: '',
+            longitude: startPoint.lng,
+            latitude: startPoint.lat,
+            markerType: MarkerType.tracePassingPoint));
+      }
+      final exceptionPoint = response.exceptionPoint;
+      if (exceptionPoint != null && exceptionPoint.addr?.isNotEmpty == true) {
+        list.add(Marker(
+            id: 'end',
+            markerName: '',
+            longitude: exceptionPoint.lng,
+            latitude: exceptionPoint.lat,
+            markerType: MarkerType.tracePassingPoint));
+      }
+      bean.markers = list;
+
+      final points = response.trackPoints?.map((point) {
+        return LatLng(latitude: point.latitude, longitude: point.latitude);
+      }).toList();
+
+      bean.polylines = [
+        Polyline(
+            lineId: 'small_trace',
+            color: '#4476FF',
+            width: 6.5,
+            lineType: PolylineType.color,
+            points: points ?? [])
+      ];
+
+      bean.isRequestSuccess.value = true;
+      bean.lastRequestTime = DateTime.now();
+    }).whenComplete(() {
+      bean.isRequesting = false;
+    });
+  }
+}
+
+class TodayTrackReportBean {
+  final String userId;
+
+  bool isRequesting = false;
+
+  final RxBool isRequestSuccess = RxBool(false);
+
+  TrackDailyBean? startPoint;
+
+  TrackDailyBean? exceptionPoint;
+
+  List<AtmobTrackPoint>? trackPoints;
+
+  List<Polyline>? polylines;
+
+  List<Marker>? markers;
+
+  // 记录上一次请求成功的时间
+  DateTime? lastRequestTime;
+
+  TodayTrackReportBean(this.userId);
+
+  MapPadding mapPadding =
+      MapPadding(left: 10.w, right: 10.w, top: 10.w, bottom: 10.w);
+
+  bool get isExpired {
+    if (lastRequestTime == null) return true;
+    return DateTime.now().difference(lastRequestTime!) >
+        const Duration(minutes: 5);
+  }
+}

+ 22 - 16
lib/module/main/view.dart

@@ -23,25 +23,31 @@ Widget buildTrackPoint(MainTrackType type, String address) {
       children: [
         WidgetSpan(
           alignment: PlaceholderAlignment.middle,
-          child: Container(
-            margin: EdgeInsets.only(right: 4.w),
-            padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 2.5.w),
-            decoration: BoxDecoration(
-              color: type == MainTrackType.startPoint
-                  ? '#15CBA1'.color
-                  : '#E94949'.color,
-              borderRadius: BorderRadius.circular(3.r),
-            ),
-            child: Text(
-              type == MainTrackType.startPoint
-                  ? StringName.todaySimpleTrackStart
-                  : StringName.todaySimpleTrackError,
-              style: TextStyle(color: Colors.white, fontSize: 11.sp, height: 1),
+          child: Visibility(
+            visible: type != MainTrackType.normalPoint,
+            child: Container(
+              margin: EdgeInsets.only(right: 4.w),
+              padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 2.5.w),
+              decoration: BoxDecoration(
+                color: type == MainTrackType.startPoint
+                    ? '#15CBA1'.color
+                    : '#E94949'.color,
+                borderRadius: BorderRadius.circular(3.r),
+              ),
+              child: Text(
+                type == MainTrackType.startPoint
+                    ? StringName.todaySimpleTrackStart
+                    : StringName.todaySimpleTrackError,
+                style:
+                    TextStyle(color: Colors.white, fontSize: 11.sp, height: 1),
+              ),
             ),
           ),
         ),
         TextSpan(
-          text: address,
+          text: type == MainTrackType.normalPoint
+              ? StringName.mainTodayTrackNormalPoint
+              : address,
           style: TextStyle(fontSize: 12.sp, color: '#666666'.color),
         ),
       ],
@@ -49,4 +55,4 @@ Widget buildTrackPoint(MainTrackType type, String address) {
   );
 }
 
-enum MainTrackType { startPoint, errorPoint }
+enum MainTrackType { startPoint, errorPoint, normalPoint }

+ 14 - 5
lib/module/track/track_controller.dart

@@ -111,7 +111,8 @@ class TrackController extends BaseController
     mapController.clear();
     //画折线
     if (points.length > 1) {
-      mapController.addPolyline(Constants.traceNormalLineId, points,
+      mapController.addPolyline(
+          Polyline(lineId: Constants.traceNormalLineId, points: points),
           mapPadding: mapPadding);
     }
     //画标记点
@@ -132,15 +133,23 @@ class TrackController extends BaseController
 
   void showMovingTrack(List<LatLng> movingPoints) {
     _clearSelectMapMarker();
-    mapController.addPolyline(Constants.traceSelectLineId, movingPoints,
-        lineType: PolylineType.selected, mapPadding: mapPadding);
+    mapController.addPolyline(
+        Polyline(
+            lineId: Constants.traceSelectLineId,
+            points: movingPoints,
+            lineType: PolylineType.selected),
+        mapPadding: mapPadding);
   }
 
   void showTrackError(List<LatLng> errorPoints, Marker errorMarker) {
     _clearSelectMapMarker();
     mapController.updateOrAddMarker(errorMarker);
-    mapController.addPolyline(Constants.traceSelectLineId, errorPoints,
-        lineType: PolylineType.error, mapPadding: mapPadding);
+    mapController.addPolyline(
+        Polyline(
+            lineId: Constants.traceSelectLineId,
+            points: errorPoints,
+            lineType: PolylineType.error),
+        mapPadding: mapPadding);
   }
 
   void _clearSelectMapMarker() {

+ 7 - 1
lib/module/track/track_day_detail/track_daily_item.dart

@@ -5,6 +5,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:location/data/bean/track_daily_bean.dart';
 import 'package:location/data/consts/constants.dart';
 import 'package:location/dialog/track_error_tips_dialog.dart';
+import 'package:location/module/analyse/location_analyse_page.dart';
 import 'package:location/module/track/track_util.dart';
 import 'package:location/resource/assets.gen.dart';
 import 'package:location/resource/colors.gen.dart';
@@ -245,7 +246,12 @@ Widget buildErrorTrackDailyItem(TrackDailyBean bean,
                       ),
                     ),
                     Spacer(),
-                    Assets.images.imgTrackAiAnalyse.image(width: 73.w),
+                    GestureDetector(
+                        onTap: () {
+                          LocationAnalysePage.start();
+                        },
+                        child:
+                            Assets.images.imgTrackAiAnalyse.image(width: 73.w)),
                     SizedBox(width: 6.w),
                   ],
                 ),

+ 21 - 5
lib/resource/assets.gen.dart

@@ -32,6 +32,14 @@ class $AssetsImagesGen {
   AssetGenImage get bgFriendItem =>
       const AssetGenImage('assets/images/bg_friend_item.webp');
 
+  /// File path: assets/images/bg_location_analyse.webp
+  AssetGenImage get bgLocationAnalyse =>
+      const AssetGenImage('assets/images/bg_location_analyse.webp');
+
+  /// File path: assets/images/bg_location_analyse_ai.webp
+  AssetGenImage get bgLocationAnalyseAi =>
+      const AssetGenImage('assets/images/bg_location_analyse_ai.webp');
+
   /// File path: assets/images/bg_login_head_container.webp
   AssetGenImage get bgLoginHeadContainer =>
       const AssetGenImage('assets/images/bg_login_head_container.webp');
@@ -596,10 +604,6 @@ class $AssetsImagesGen {
   AssetGenImage get imgTrackAiAnalyse =>
       const AssetGenImage('assets/images/img_track_ai_analyse.webp');
 
-  /// File path: assets/images/img_track_example.webp
-  AssetGenImage get imgTrackExample =>
-      const AssetGenImage('assets/images/img_track_example.webp');
-
   /// File path: assets/images/img_track_no_data.webp
   AssetGenImage get imgTrackNoData =>
       const AssetGenImage('assets/images/img_track_no_data.webp');
@@ -611,6 +615,8 @@ class $AssetsImagesGen {
         bgDialogLocationPermissionIos,
         bgDialogTrackError,
         bgFriendItem,
+        bgLocationAnalyse,
+        bgLocationAnalyseAi,
         bgLoginHeadContainer,
         bgMemberHeader,
         bgMineMemberCard,
@@ -752,15 +758,25 @@ class $AssetsImagesGen {
         imgMemberRetainContainer,
         imgMemberUserCancelsContainer,
         imgTrackAiAnalyse,
-        imgTrackExample,
         imgTrackNoData
       ];
 }
 
+class $AssetsVideoGen {
+  const $AssetsVideoGen();
+
+  /// File path: assets/video/location_analyse_robot.mp4
+  String get locationAnalyseRobot => 'assets/video/location_analyse_robot.mp4';
+
+  /// List of all assets
+  List<String> get values => [locationAnalyseRobot];
+}
+
 class Assets {
   const Assets._();
 
   static const $AssetsImagesGen images = $AssetsImagesGen();
+  static const $AssetsVideoGen video = $AssetsVideoGen();
 }
 
 class AssetGenImage {

+ 6 - 0
lib/resource/string.gen.dart

@@ -289,6 +289,10 @@ class StringName {
   static String get trackDailySummarytrack => 'track_daily_summary_track'.tr; // 轨迹情况
   static String get trackDailyCallPhone => 'track_daily_call_phone'.tr; // 联系TA
   static String get trackDailySkipCallPhoneFail => 'track_daily_skip_call_phone_fail'.tr; // 跳转拨号界面失败
+  static String get mainTodayTrackLoading =>
+      'main_today_track_loading'.tr; // 正在加载中...
+  static String get mainTodayTrackNormalPoint =>
+      'main_today_track_normal_point'.tr; // 暂无异常
 }
 class StringMultiSource {
   StringMultiSource._();
@@ -581,6 +585,8 @@ class StringMultiSource {
       'track_daily_summary_track': '轨迹情况',
       'track_daily_call_phone': '联系TA',
       'track_daily_skip_call_phone_fail': '跳转拨号界面失败',
+      'main_today_track_loading': '正在加载中...',
+      'main_today_track_normal_point': '暂无异常',
     },
   };
 }

+ 3 - 0
lib/router/app_pages.dart

@@ -2,6 +2,7 @@ import 'package:get/get.dart';
 import 'package:location/di/get_it.dart';
 import 'package:location/module/about/about_controller.dart';
 import 'package:location/module/about/about_page.dart';
+import 'package:location/module/analyse/location_analyse_page.dart';
 import 'package:location/module/browser/browser_controller.dart';
 import 'package:location/module/browser/browser_view.dart';
 import 'package:location/module/feedback/feed_back_controller.dart';
@@ -56,6 +57,7 @@ abstract class RoutePath {
   static const feedback = '/feedback';
   static const about = '/about';
   static const permissionSetting = '/permissionSetting';
+  static const locationAnalyse = '/locationAnalyse';
 }
 
 class AppBinding extends Bindings {
@@ -103,4 +105,5 @@ final generalPages = [
   GetPage(name: RoutePath.trackDetail, page: () => TrackDetailPage()),
   GetPage(
       name: RoutePath.permissionSetting, page: () => PermissionSettingPage()),
+  GetPage(name: RoutePath.locationAnalyse, page: () => LocationAnalysePage()),
 ];

+ 12 - 0
lib/utils/date_util.dart

@@ -33,6 +33,18 @@ class DateUtil {
         ? diff.abs() > duration.inSeconds
         : diff > duration.inSeconds;
   }
+
+  /// 获取当天起始时间戳(毫秒)
+  static DateTime getStartOfDayTimestamp(DateTime date) {
+    return DateTime(date.year, date.month, date.day);
+  }
+
+  /// 获取当天结束时间戳(毫秒)
+  static DateTime getEndOfDayTimestamp(DateTime date) {
+    return DateTime(date.year, date.month, date.day)
+        .add(Duration(days: 1))
+        .subtract(Duration(milliseconds: 1));
+  }
 }
 
 extension DateTimeExt on DateTime {

+ 1 - 0
plugins/map/lib/flutter_map.dart

@@ -20,6 +20,7 @@ export 'package:flutter_map/src/entity/trace_location.dart';
 export 'package:flutter_map/src/entity/lat_lng.dart';
 export 'package:flutter_map/src/entity/map_padding.dart';
 export 'package:flutter_map/src/entity/popup.dart';
+export 'package:flutter_map/src/entity/polyline.dart';
 
 //接口
 export 'package:flutter_map/src/interface/map_sdk_interface.dart';

+ 3 - 1
plugins/map/lib/src/consts/map_constants.dart

@@ -15,7 +15,7 @@ class MapConstants {
   static const String mapWidgetViewType = "com.atmob.flutter.map";
 
   /// *********************************************************************************************************************
-  //基础方法名称
+  //SDK方法名称
   static const String init = 'init';
   static const String getPlatformMapName = 'getPlatformMapName';
   static const String startLocation = "startLocation";
@@ -26,6 +26,8 @@ class MapConstants {
   static const String methodMapClear = "map#clear";
   static const String methodMoveToSuitableLocation =
       "map#moveToSuitableLocation";
+  static const String methodInteractionEnabled = "map#interactionEnabled";
+  static const String methodMapLogoVisible = "map#logoVisible";
 
   //标记物相关方法名称
   static const String methodUpdateOrAddMarkers = "marker#updateOrAddMarkers";

+ 2 - 1
plugins/map/lib/src/consts/polyline_type.dart

@@ -1,7 +1,8 @@
 enum PolylineType {
   normal('normal'),
   error('error'),
-  selected('selected');
+  selected('selected'),
+  color('color');
 
   final String value;
 

+ 43 - 10
plugins/map/lib/src/core/map_controller.dart

@@ -9,11 +9,10 @@ import 'package:flutter_map/src/entity/camera_position.dart';
 import 'package:flutter_map/src/entity/codable.dart';
 import 'package:flutter_map/src/entity/lat_lng.dart';
 import 'package:flutter_map/src/entity/marker.dart';
+import 'package:flutter_map/src/entity/polyline.dart';
 import 'package:flutter_map/src/interface/map_fun_interface.dart';
 import 'package:flutter_map/src/interface/map_marker_interface.dart';
 import 'package:flutter_map/src/interface/map_polyline_interface.dart';
-
-import '../consts/polyline_type.dart';
 import '../entity/map_padding.dart';
 
 class MapController
@@ -120,16 +119,20 @@ class MapController
   }
 
   @override
-  void addPolyline(String lineId, List<LatLng> points,
-      {bool isAnimateCamera = true,
-      PolylineType lineType = PolylineType.normal,
-      MapPadding? mapPadding}) {
-    if (_isDisposed || points.isEmpty) return;
+  void addPolyline(Polyline polyline,
+      {bool isAnimateCamera = true, MapPadding? mapPadding}) {
+    if (_isDisposed || polyline.points.isEmpty) return;
     Map<String, dynamic> map = {};
-    map['lineId'] = lineId;
-    map['points'] = _encodeJson(points);
+    map['lineId'] = polyline.lineId;
+    map['points'] = _encodeJson(polyline.points);
     map['isAnimateCamera'] = isAnimateCamera;
-    map['lineType'] = lineType.value;
+    map['lineType'] = polyline.lineType.value;
+    if (polyline.color != null) {
+      map['color'] = polyline.color;
+    }
+    if (polyline.width != null) {
+      map['width'] = polyline.width;
+    }
     if (mapPadding != null) {
       map['mapPadding'] = _encodeJson(mapPadding);
     }
@@ -205,6 +208,36 @@ class MapController
     }
   }
 
+  @override
+  void setMapInteractionEnabled(bool enabled) {
+    if (_isDisposed) return;
+    final params = {
+      'method': MapConstants.methodInteractionEnabled,
+      'args': {'enabled': enabled}
+    };
+    debugPrint("setMapInteractionEnabled...params==>$params");
+    if (_channel != null) {
+      _executeMethod(params);
+    } else {
+      _pendingOperations.add(params);
+    }
+  }
+
+  @override
+  void setMapLogoVisible(bool visible) {
+    if (_isDisposed) return;
+    final params = {
+      'method': MapConstants.methodMapLogoVisible,
+      'args': {'visible': visible}
+    };
+    debugPrint("setMapLogoVisible...params==>$params");
+    if (_channel != null) {
+      _executeMethod(params);
+    } else {
+      _pendingOperations.add(params);
+    }
+  }
+
   /// iOS 需要一个Map 而不是json string
   dynamic _encodeJson(dynamic object) {
     // iOS平台直接返回对象或转换为JSON对象

+ 22 - 0
plugins/map/lib/src/entity/polyline.dart

@@ -0,0 +1,22 @@
+import '../../flutter_map.dart';
+
+class Polyline {
+  String lineId;
+
+  List<LatLng> points;
+
+  PolylineType lineType = PolylineType.normal;
+
+  //自定义线颜色
+  String? color;
+
+  //自定义线宽度 单位以及原生计算为准,Android为dp
+  double? width;
+
+  Polyline(
+      {required this.lineId,
+      required this.points,
+      this.color,
+      this.width,
+      this.lineType = PolylineType.normal});
+}

+ 6 - 0
plugins/map/lib/src/interface/map_fun_interface.dart

@@ -12,4 +12,10 @@ abstract class MapFunInterface {
 
   //移动至多个点的位置,并提供设置padding距离
   moveToSuitableLocation(List<LatLng> points, {MapPadding? mapPadding});
+
+  //禁用或启用地图交互
+  void setMapInteractionEnabled(bool enabled);
+
+  //显示或隐藏地图logo
+  void setMapLogoVisible(bool visible);
 }

+ 3 - 2
plugins/map/lib/src/interface/map_polyline_interface.dart

@@ -1,12 +1,13 @@
 import 'package:flutter_map/src/entity/map_padding.dart';
+import 'package:flutter_map/src/entity/polyline.dart';
 
 import '../../flutter_map.dart';
 import '../consts/polyline_type.dart';
 
 abstract class MapPolylineInterface {
   //画线   isAnimateCamera 是否动画移动到线的起点 ,mapPadding 地图边距 单位dp
-  void addPolyline(String lineId, List<LatLng> points,
-      {bool isAnimateCamera, PolylineType lineType, MapPadding? mapPadding});
+  void addPolyline(Polyline polyline,
+      {bool isAnimateCamera = true, MapPadding? mapPadding});
 
   //清除制定的线
   void removePolyline(String lineId);

+ 79 - 5
plugins/map/lib/src/widget/map_widget.dart

@@ -10,15 +10,32 @@ import 'package:flutter_map/src/consts/marker_type.dart';
 import 'package:flutter_map/src/core/map_controller.dart';
 import 'package:flutter_map/src/entity/marker.dart';
 
+import '../../flutter_map.dart';
+import '../entity/polyline.dart';
+
 class MapWidget extends StatefulWidget {
-  final MapController? controller;
+  late final MapController mapController;
   final MarkerTapCallback? onMarkerTap;
+  final List<Marker>? markers;
+  final List<Polyline>? polyline;
+  final MapPadding? mapPadding;
+  final bool isAnimateCamera;
+  final bool? isShowLogo;
+  final bool? interactionIsEnabled;
 
-  const MapWidget({
+  MapWidget({
     super.key,
-    this.controller,
+    MapController? controller,
     this.onMarkerTap,
-  });
+    this.mapPadding,
+    this.markers,
+    this.isAnimateCamera = true,
+    this.isShowLogo,
+    this.interactionIsEnabled,
+    this.polyline,
+  }) {
+    mapController = controller ?? MapController();
+  }
 
   @override
   State<MapWidget> createState() => _MapWidgetState();
@@ -27,6 +44,63 @@ class MapWidget extends StatefulWidget {
 class _MapWidgetState extends State<MapWidget> {
   final String mapViewType = MapConstants.mapWidgetViewType;
 
+  final List<LatLng> cameraPoints = [];
+
+  @override
+  void didUpdateWidget(covariant MapWidget oldWidget) {
+    super.didUpdateWidget(oldWidget);
+    cameraPoints.clear();
+    if (oldWidget.markers != widget.markers) {
+      showMarker();
+    }
+    if (oldWidget.polyline != widget.polyline) {
+      oldWidget.polyline?.forEach((line) {
+        widget.mapController.removePolyline(line.lineId);
+      });
+      showPolyline();
+    }
+    animateCamera();
+  }
+
+  void showMarker() {
+    final markerPoints = widget.markers
+        ?.map((marker) =>
+            LatLng(latitude: marker.latitude, longitude: marker.longitude))
+        .toList();
+    cameraPoints.addAll(markerPoints ?? []);
+    widget.mapController.replaceAllMarkers(widget.markers ?? []);
+  }
+
+  void showPolyline() {
+    widget.polyline?.where((line) => line.points.isNotEmpty).forEach((line) {
+      cameraPoints.addAll(line.points);
+      widget.mapController.addPolyline(line, isAnimateCamera: false);
+    });
+  }
+
+  void animateCamera() {
+    if (cameraPoints.isNotEmpty) {
+      widget.mapController
+          .moveToSuitableLocation(cameraPoints, mapPadding: widget.mapPadding);
+    }
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    if (widget.isShowLogo != null) {
+      widget.mapController.setMapLogoVisible(widget.isShowLogo!);
+    }
+    if (widget.interactionIsEnabled != null) {
+      widget.mapController
+          .setMapInteractionEnabled(widget.interactionIsEnabled!);
+    }
+
+    showMarker();
+    showPolyline();
+    animateCamera();
+  }
+
   @override
   Widget build(BuildContext context) {
     debugPrint(
@@ -70,7 +144,7 @@ class _MapWidgetState extends State<MapWidget> {
   Future<void> onPlatformViewCreated(int viewId) async {
     MethodChannel mapChannel =
         MethodChannel('${MapConstants.mapViewChannelName}$viewId');
-    widget.controller?.setChannel(mapChannel);
+    widget.mapController.setChannel(mapChannel);
     mapChannel.setMethodCallHandler(_mapMethodCallHandler);
   }
 

+ 30 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/MapController.java

@@ -5,6 +5,7 @@ import android.content.Context;
 import androidx.annotation.NonNull;
 
 import com.amap.api.maps.AMap;
+import com.amap.api.maps.AMapOptions;
 import com.amap.api.maps.CameraUpdate;
 import com.amap.api.maps.CameraUpdateFactory;
 import com.amap.api.maps.model.LatLng;
@@ -72,7 +73,36 @@ public class MapController implements MyMethodCallHandler {
             case Constants.METHOD_MAP_MOVE_TO_SUITABLE_LOCATION:
                 animateSuitableLocation(call, result);
                 break;
+            case Constants.METHOD_MAP_INTERACTION_ENABLED:
+                mapInteractionEnabled(call, result);
+                break;
+            case Constants.METHOD_MAP_LOGO_VISIBLE:
+                mapLogoVisible(call, result);
+                break;
+
+        }
+    }
+
+    private void mapInteractionEnabled(MethodCall call, MethodChannel.Result result) {
+        Map<String, Object> arguments = call.arguments();
+        boolean isEnabled = ParamUtil.getBoolean(arguments, "enabled", true);
+        if (map == null) {
+            result.error("map is null", null, null);
+            return;
         }
+        map.getUiSettings().setAllGesturesEnabled(isEnabled);
+        result.success(null);
+    }
+
+    private void mapLogoVisible(MethodCall call, MethodChannel.Result result) {
+        Map<String, Object> arguments = call.arguments();
+        boolean isVisible = ParamUtil.getBoolean(arguments, "visible", true);
+        if (map == null) {
+            result.error("map is null", null, null);
+            return;
+        }
+        map.getUiSettings().setLogoBottomMargin(isVisible ? 0 : -100);
+        result.success(null);
     }
 
     private void animateSuitableLocation(MethodCall call, MethodChannel.Result result) {

+ 3 - 1
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java

@@ -35,8 +35,10 @@ public class Constants {
     public static final String METHOD_ANIMATE_CAMERA = "map#animateCamera";
     public static final String METHOD_MAP_CLEAR = "map#clear";
     public static final String METHOD_MAP_MOVE_TO_SUITABLE_LOCATION = "map#moveToSuitableLocation";
+    public static final String METHOD_MAP_INTERACTION_ENABLED = "map#interactionEnabled";
+    public static final String METHOD_MAP_LOGO_VISIBLE = "map#logoVisible";
 
-    public static final String[] METHOD_ID_LIST_FOR_MAP = {METHOD_MOVE_CAMERA, METHOD_ANIMATE_CAMERA, METHOD_MAP_CLEAR, METHOD_MAP_MOVE_TO_SUITABLE_LOCATION};
+    public static final String[] METHOD_ID_LIST_FOR_MAP = {METHOD_MOVE_CAMERA, METHOD_ANIMATE_CAMERA, METHOD_MAP_CLEAR, METHOD_MAP_MOVE_TO_SUITABLE_LOCATION, METHOD_MAP_INTERACTION_ENABLED, METHOD_MAP_LOGO_VISIBLE};
 
     /**
      * markers

+ 23 - 10
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/polyline/PolylineController.java

@@ -1,6 +1,8 @@
 package com.atmob.map_amap_android.overlays.polyline;
 
 import android.content.Context;
+import android.graphics.Color;
+import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
 
@@ -109,7 +111,13 @@ public class PolylineController implements MyMethodCallHandler {
                 return;
             }
             String lineType = ParamUtil.getString(arguments, "lineType");
-            drawTrackByPoints(lineId, latLngList, lineType);
+
+            //获取颜色
+            String color = ParamUtil.getString(arguments, "color");
+            //获取宽度
+            double width = ParamUtil.getDouble(arguments, "width", 8);
+
+            drawTrackByPoints(lineId, latLngList, lineType, color, width);
             if (isAnimateCamera) {
                 String paddingStr = (String) arguments.get("mapPadding");
 
@@ -127,22 +135,27 @@ public class PolylineController implements MyMethodCallHandler {
         }
     }
 
-    private void drawTrackByPoints(String lineId, List<LatLng> points, @PolylineType String lineType) {
+    private void drawTrackByPoints(String lineId, List<LatLng> points, @PolylineType String lineType, String color, double width) {
         if (map == null || points == null || points.isEmpty()) {
             return;
         }
-        int lineDrawableId;
+
+        PolylineOptions polylineOptions = new PolylineOptions()
+                .setUseTexture(true)
+                .width(SizeUtil.dp2px(context, (float) width));
+
         if (Objects.equals(PolylineType.ERROR, lineType)) {
-            lineDrawableId = R.drawable.bg_map_error_line;
+            polylineOptions.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.bg_map_error_line));
         } else if (Objects.equals(PolylineType.SELECTED, lineType)) {
-            lineDrawableId = R.drawable.bg_map_selected_line;
+            polylineOptions.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.bg_map_selected_line));
+        } else if (Objects.equals(PolylineType.COLOR, lineType)) {
+            if (!TextUtils.isEmpty(color)) {
+                polylineOptions.color(Color.parseColor(color));
+            }
         } else {
-            lineDrawableId = R.drawable.bg_map_line;
+            polylineOptions.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.bg_map_line));
         }
-        PolylineOptions polylineOptions = new PolylineOptions()
-                .setCustomTexture(BitmapDescriptorFactory.fromResource(lineDrawableId))
-                .setUseTexture(true)
-                .width(SizeUtil.dp2px(context, 8));
+
 
         LatLng lastPoint = null;
         for (int i = 0; i < points.size(); i++) {

+ 2 - 1
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/polyline/PolylineType.java

@@ -6,10 +6,11 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 @Retention(RetentionPolicy.SOURCE)
-@StringDef({PolylineType.NORMAL, PolylineType.ERROR, PolylineType.SELECTED})
+@StringDef({PolylineType.NORMAL, PolylineType.ERROR, PolylineType.SELECTED, PolylineType.COLOR})
 public @interface PolylineType {
 
     String NORMAL = "normal";
     String ERROR = "error";
     String SELECTED = "selected";
+    String COLOR = "color";
 }

+ 4 - 0
pubspec.yaml

@@ -145,6 +145,9 @@ dependencies:
   #指示器
   smooth_page_indicator: ^1.2.1
 
+  #视频播放器
+  video_player: ^2.10.0
+
   ######################地图########################
   flutter_map:
     path: plugins/map
@@ -246,6 +249,7 @@ flutter:
 
   assets:
     - assets/images/
+    - assets/video/
 
   fonts:
     - family: OppoSans