소스 검색

[new]增加好友marker点显示

zk 1 년 전
부모
커밋
f35cddbce4
100개의 변경된 파일3411개의 추가작업 그리고 20개의 파일을 삭제
  1. BIN
      android/app/keystore/location.jks
  2. BIN
      android/app/keystore/location_new.jks
  3. 9 2
      android/app/src/main/AndroidManifest.xml
  4. 2 2
      lib/main.dart
  5. 64 1
      lib/module/main/main_controller.dart
  6. 7 2
      lib/module/main/main_page.dart
  7. 7 0
      lib/sdk/map/map_helper.dart
  8. 0 13
      lib/widget/map_view.dart
  9. 29 0
      plugins/map/.gitignore
  10. 33 0
      plugins/map/.metadata
  11. 3 0
      plugins/map/CHANGELOG.md
  12. 1 0
      plugins/map/LICENSE
  13. 15 0
      plugins/map/README.md
  14. 4 0
      plugins/map/analysis_options.yaml
  15. 10 0
      plugins/map/lib/flutter_map.dart
  16. 87 0
      plugins/map/lib/src/core/map_controller.dart
  17. 21 0
      plugins/map/lib/src/core/map_initializer.dart
  18. 25 0
      plugins/map/lib/src/core/map_platform.dart
  19. 62 0
      plugins/map/lib/src/widget/map_widget.dart
  20. 29 0
      plugins/map/pubspec.yaml
  21. 29 0
      plugins/map_amap_android/.gitignore
  22. 30 0
      plugins/map_amap_android/.metadata
  23. 3 0
      plugins/map_amap_android/CHANGELOG.md
  24. 1 0
      plugins/map_amap_android/LICENSE
  25. 15 0
      plugins/map_amap_android/README.md
  26. 4 0
      plugins/map_amap_android/analysis_options.yaml
  27. 9 0
      plugins/map_amap_android/android/.gitignore
  28. 89 0
      plugins/map_amap_android/android/build.gradle
  29. 20 0
      plugins/map_amap_android/android/gradle.properties
  30. BIN
      plugins/map_amap_android/android/gradle/wrapper/gradle-wrapper.jar
  31. 7 0
      plugins/map_amap_android/android/gradle/wrapper/gradle-wrapper.properties
  32. 249 0
      plugins/map_amap_android/android/gradlew
  33. 92 0
      plugins/map_amap_android/android/gradlew.bat
  34. 1 0
      plugins/map_amap_android/android/settings.gradle
  35. 36 0
      plugins/map_amap_android/android/src/main/AndroidManifest.xml
  36. 19 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/FlutterViewPlugin.java
  37. 88 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/MapAmapAndroidPlugin.java
  38. 243 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/AmapView.java
  39. 88 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/MapController.java
  40. 39 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/MapViewFactory.java
  41. 105 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/MakerInfo.java
  42. 34 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java
  43. 27 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/lifecycle/FlutterLifecycleAdapter.java
  44. 24 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/MyMethodCallHandler.java
  45. 180 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/marker/MarkersController.java
  46. 81 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/AMapHelper.java
  47. 16 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/GsonUtil.java
  48. 88 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/LogUtil.java
  49. 50 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/ParamUtil.java
  50. 55 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/ProcessUtil.java
  51. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/bg_location_marker_bubble.9.png
  52. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/bg_marker_normal.webp
  53. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_default_friend_avatar.webp
  54. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_default_mine_avatar.webp
  55. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_location_marker_normal.webp
  56. BIN
      plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_location_marker_selected.png
  57. 9 0
      plugins/map_amap_android/android/src/main/res/drawable/icon_marker_normal_bottom_circle.xml
  58. 9 0
      plugins/map_amap_android/android/src/main/res/drawable/icon_marker_selected_bottom_circle.xml
  59. 63 0
      plugins/map_amap_android/android/src/main/res/layout/item_location_marker.xml
  60. 29 0
      plugins/map_amap_android/android/src/test/java/com/atmob/map_amap_android/MapAmapAndroidPluginTest.java
  61. 73 0
      plugins/map_amap_android/pubspec.yaml
  62. 29 0
      plugins/map_google_android/.gitignore
  63. 30 0
      plugins/map_google_android/.metadata
  64. 3 0
      plugins/map_google_android/CHANGELOG.md
  65. 1 0
      plugins/map_google_android/LICENSE
  66. 15 0
      plugins/map_google_android/README.md
  67. 4 0
      plugins/map_google_android/analysis_options.yaml
  68. 9 0
      plugins/map_google_android/android/.gitignore
  69. 69 0
      plugins/map_google_android/android/build.gradle
  70. 3 0
      plugins/map_google_android/android/gradle.properties
  71. BIN
      plugins/map_google_android/android/gradle/wrapper/gradle-wrapper.jar
  72. 7 0
      plugins/map_google_android/android/gradle/wrapper/gradle-wrapper.properties
  73. 249 0
      plugins/map_google_android/android/gradlew
  74. 92 0
      plugins/map_google_android/android/gradlew.bat
  75. 1 0
      plugins/map_google_android/android/settings.gradle
  76. 2 0
      plugins/map_google_android/android/src/main/AndroidManifest.xml
  77. 15 0
      plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/FlutterViewPlugin.java
  78. 76 0
      plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/MapGoogleAndroidPlugin.java
  79. 9 0
      plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/conts/FlutterMapNameConfig.java
  80. 70 0
      plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/googlemap/GoogleMapView.java
  81. 36 0
      plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/googlemap/MapViewFactory.java
  82. 29 0
      plugins/map_google_android/android/src/test/java/com/atmob/map_google_android/MapGoogleAndroidPluginTest.java
  83. 3 0
      plugins/map_google_android/lib/map_google_android.dart
  84. 11 0
      plugins/map_google_android/lib/map_google_android_method_channel.dart
  85. 28 0
      plugins/map_google_android/lib/map_google_android_platform_interface.dart
  86. 74 0
      plugins/map_google_android/pubspec.yaml
  87. 29 0
      plugins/map_platform_interface/.gitignore
  88. 10 0
      plugins/map_platform_interface/.metadata
  89. 3 0
      plugins/map_platform_interface/CHANGELOG.md
  90. 1 0
      plugins/map_platform_interface/LICENSE
  91. 39 0
      plugins/map_platform_interface/README.md
  92. 4 0
      plugins/map_platform_interface/analysis_options.yaml
  93. 14 0
      plugins/map_platform_interface/lib/map_interface.dart
  94. 19 0
      plugins/map_platform_interface/lib/src/consts/constants.dart
  95. 5 0
      plugins/map_platform_interface/lib/src/consts/flutter_map_name_config.dart
  96. 22 0
      plugins/map_platform_interface/lib/src/consts/marker_type.dart
  97. 29 0
      plugins/map_platform_interface/lib/src/entity/camera_position.dart
  98. 46 0
      plugins/map_platform_interface/lib/src/entity/marker.dart
  99. 12 0
      plugins/map_platform_interface/lib/src/interface/map_overlays_interface.dart
  100. 0 0
      plugins/map_platform_interface/lib/src/interface/map_platform_interface.dart

BIN
android/app/keystore/location.jks


BIN
android/app/keystore/location_new.jks


+ 9 - 2
android/app/src/main/AndroidManifest.xml

@@ -1,9 +1,12 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
 
     <application
+        android:allowBackup="false"
         android:label="location"
         android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher">
+
+
         <activity
             android:name=".MainActivity"
             android:exported="true"
@@ -25,8 +28,12 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <!-- Don't delete the meta-data below.
-             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+
+
+        <meta-data
+            android:name="com.amap.api.v2.apikey"
+            android:value="d10659dc789ae488935bd9e221d81cab" />
+
         <meta-data
             android:name="flutterEmbedding"
             android:value="2" />

+ 2 - 2
lib/main.dart

@@ -10,12 +10,12 @@ import 'package:location/resource/colors.gen.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/resource/string_source.dart';
 import 'package:location/router/app_pages.dart';
+import 'package:location/sdk/map/map_helper.dart';
 import 'package:location/utils/app_info_util.dart';
 import 'package:location/utils/mmkv_util.dart';
 import 'package:location/utils/privacy_compliance.dart';
 import 'package:location/utils/toast_util.dart';
 import 'package:pull_to_refresh/pull_to_refresh.dart';
-
 import 'data/consts/channel_util.dart';
 import 'di/get_it.dart';
 import 'data/consts/build_config.dart';
@@ -55,7 +55,6 @@ void initCommon() {
   ChannelUtil.initChannel();
 }
 
-
 /// 隐私相关初始化
 class AppInitTask implements EnsurePolicyGrant {
   @override
@@ -64,6 +63,7 @@ class AppInitTask implements EnsurePolicyGrant {
     await appInfoUtil.init();
     await deviceInfoUtil.init();
     //初始化其他sdk
+    await MapHelper.init();
   }
 }
 

+ 64 - 1
lib/module/main/main_controller.dart

@@ -1,10 +1,14 @@
+import 'package:get/get.dart';
 import 'package:get/get_rx/src/rx_types/rx_types.dart';
 import 'package:injectable/injectable.dart';
 import 'package:location/base/base_controller.dart';
+import 'package:location/data/bean/location_info.dart';
 import 'package:location/data/bean/user_info.dart';
 import 'package:location/data/repositories/account_repository.dart';
 import 'package:location/data/repositories/friends_repository.dart';
 import 'package:location/utils/atmob_log.dart';
+import 'package:map/flutter_map.dart';
+import 'package:map_platform_interface/map_interface.dart';
 
 import '../add_friend/add_friend_view.dart';
 import '../mine/mine_page.dart';
@@ -24,6 +28,16 @@ class MainController extends BaseController {
 
   UserInfo? get selectedFriend => _selectedFriend.value;
 
+  final MapController mapController = MapController();
+
+  @override
+  void onReady() {
+    super.onReady();
+    friendsList.listen((list) {
+      mapController.addMarkers(_convertToMarker(list));
+    });
+  }
+
   void onAddFriendClick() {
     AddFriendView.show();
   }
@@ -33,7 +47,56 @@ class MainController extends BaseController {
   }
 
   void onSelectClick(UserInfo mineLocation) {
-    AtmobLog.d("zkzk", "onSelectClick: ${mineLocation.phoneNumber}");
+    UserInfo? oldInfo = _selectedFriend.value;
     _selectedFriend.value = mineLocation;
+    //修改地图选中
+    _updateMapSelected(oldInfo, mineLocation);
+    //移动到选中的位置
+    animateCamera(mineLocation);
+  }
+
+  void _updateMapSelected(UserInfo? oldInfo, UserInfo newInfo) {
+    List<UserInfo> markers = [];
+    if (oldInfo != null) {
+      markers.add(oldInfo);
+    }
+    markers.add(newInfo);
+    mapController.updateMarkers(_convertToMarker(markers));
+  }
+
+  void animateCamera(UserInfo userInfo) {
+    LocationInfo? locationInfo = userInfo.lastLocation;
+    if (locationInfo == null ||
+        locationInfo.longitude == 0 ||
+        locationInfo.latitude == 0) {
+      return;
+    }
+    mapController.animateCamera(CameraPosition(
+        longitude: locationInfo.longitude,
+        latitude: locationInfo.latitude,
+        zoom: 18));
+  }
+
+  List<Marker> _convertToMarker(List<UserInfo> list) {
+    return list
+        .map((e) => Marker(
+              id: e.id,
+              markerName: e.remark ?? e.phoneNumber,
+              longitude: e.lastLocation?.longitude,
+              latitude: e.lastLocation?.latitude,
+              markerType: MarkerType.friend,
+              isSelected: e == _selectedFriend.value,
+            ))
+        .toList();
+  }
+
+  void onMarkerTap(Marker marker) {
+    String id = marker.id;
+    UserInfo? userInfo =
+        friendsList.firstWhereOrNull((element) => element.id == id);
+    if (userInfo == null) {
+      return;
+    }
+    onSelectClick(userInfo);
   }
 }

+ 7 - 2
lib/module/main/main_page.dart

@@ -11,7 +11,7 @@ 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/common_expand.dart';
-import 'package:location/widget/map_view.dart';
+import 'package:map/flutter_map.dart';
 import '../../router/app_pages.dart';
 import 'main_friend_item.dart';
 
@@ -37,7 +37,12 @@ class MainPage extends BasePage<MainController> {
       children: [
         Padding(
           padding: EdgeInsets.only(bottom: 50.h),
-          child: SizedBox(width: double.infinity, child: MapView()),
+          child: SizedBox(
+              width: double.infinity,
+              child: MapWidget(
+                controller: controller.mapController,
+                onMarkerTap: controller.onMarkerTap,
+              )),
         ),
         Align(
           alignment: Alignment.bottomCenter,

+ 7 - 0
lib/sdk/map/map_helper.dart

@@ -0,0 +1,7 @@
+import 'package:map/flutter_map.dart';
+
+class MapHelper {
+  static Future<void> init() async {
+    await MapInitializer.init();
+  }
+}

+ 0 - 13
lib/widget/map_view.dart

@@ -1,13 +0,0 @@
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart';
-import 'package:location/utils/common_expand.dart';
-
-class MapView extends StatelessWidget {
-  const MapView({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-        color: "#DAF2E2".color, child: Center(child: Text('这是地图页面,暂时没有开发')));
-  }
-}

+ 29 - 0
plugins/map/.gitignore

@@ -0,0 +1,29 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+build/

+ 33 - 0
plugins/map/.metadata

@@ -0,0 +1,33 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819"
+  channel: "stable"
+
+project_type: plugin
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+    - platform: android
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+    - platform: ios
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'

+ 3 - 0
plugins/map/CHANGELOG.md

@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.

+ 1 - 0
plugins/map/LICENSE

@@ -0,0 +1 @@
+TODO: Add your license here.

+ 15 - 0
plugins/map/README.md

@@ -0,0 +1,15 @@
+# map
+
+定位地图调用总入口
+
+## Getting Started
+
+This project is a starting point for a Flutter
+[plug-in package](https://flutter.dev/to/develop-plugins),
+a specialized package that includes platform-specific implementation code for
+Android and/or iOS.
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
+

+ 4 - 0
plugins/map/analysis_options.yaml

@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

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

@@ -0,0 +1,10 @@
+library map;
+
+//
+
+//地图组件
+export 'package:map/src/widget/map_widget.dart';
+export 'package:map/src/core/map_controller.dart';
+
+//初始化
+export 'package:map/src/core/map_initializer.dart';

+ 87 - 0
plugins/map/lib/src/core/map_controller.dart

@@ -0,0 +1,87 @@
+import 'dart:convert';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/services.dart';
+import 'package:map_platform_interface/map_interface.dart';
+
+class MapController extends MapOverlaysInterface {
+  final _pendingOperations = <Map<String, dynamic>>[];
+  MethodChannel? _channel;
+  bool _isDisposed = false;
+
+  @override
+  void addMarkers(List<Marker> markers) {
+    updateMarkers(markers);
+  }
+
+  void setChannel(MethodChannel channel) {
+    if (_isDisposed) return;
+
+    _channel = channel;
+    for (final op in _pendingOperations) {
+      _executeMethod(op);
+    }
+    _pendingOperations.clear();
+  }
+
+  void _executeMethod(Map<String, dynamic> op) {
+    debugPrint("_executeMethod...");
+    _channel?.invokeMethod(op['method'], op['args']).catchError((e) {
+      debugPrint('Method ${op['method']} failed: $e');
+    });
+  }
+
+  void dispose() {
+    _isDisposed = true;
+    _pendingOperations.clear();
+    _channel = null;
+  }
+
+  @override
+  void updateMarkers(List<Marker> markers) {
+    if (_isDisposed || markers.isEmpty) return;
+    final serialized = jsonEncode(markers);
+    final params = {
+      'method': Constants.methodUpdateMarkers,
+      'args': serialized
+    };
+    debugPrint("updateMarkers...params==>$params");
+    if (_channel != null) {
+      _executeMethod(params);
+    } else {
+      _pendingOperations.add(params);
+    }
+  }
+
+  @override
+  void moveCamera(CameraPosition cameraPosition) {
+    if (_isDisposed) return;
+    final serialized = cameraPosition.toJson();
+    final params = {
+      'method': Constants.methodMapMoveCamera,
+      'args': serialized
+    };
+    debugPrint("moveCamera...params==>$params");
+    if (_channel != null) {
+      _executeMethod(params);
+    } else {
+      _pendingOperations.add(params);
+    }
+  }
+
+  @override
+  void animateCamera(CameraPosition cameraPosition) {
+    if (_isDisposed) return;
+    final serialized = cameraPosition.toJson();
+    final params = {
+      'method': Constants.methodMapAnimateCamera,
+      'args': serialized
+    };
+    debugPrint("animateCamera...params==>$params");
+    if (_channel != null) {
+      _executeMethod(params);
+    } else {
+      _pendingOperations.add(params);
+    }
+  }
+}

+ 21 - 0
plugins/map/lib/src/core/map_initializer.dart

@@ -0,0 +1,21 @@
+import 'package:flutter/services.dart';
+import 'package:map_platform_interface/map_interface.dart';
+import 'map_platform.dart';
+
+class MapInitializer {
+  static MapPlatform? _mapPlatform;
+
+  static Future<void> init() async {
+    if (_mapPlatform != null) {
+      return;
+    }
+    MethodChannel channel =
+        const MethodChannel(FlutterMapNameConfig.mapMethodChannel);
+
+    MapPlatform mapPlatform = MapPlatform(channel);
+    if (!await mapPlatform.initClient()) {
+      throw Exception("地图初始化失败,未找到适配的地图,请检查是否添加的地图库");
+    }
+    _mapPlatform = mapPlatform;
+  }
+}

+ 25 - 0
plugins/map/lib/src/core/map_platform.dart

@@ -0,0 +1,25 @@
+import 'package:flutter/services.dart';
+import 'package:map_platform_interface/map_interface.dart';
+
+class MapPlatform extends MapInterface {
+  final MethodChannel _channel;
+
+  MapPlatform(this._channel);
+
+  @override
+  Future<String?> getPlatformName() {
+    return _channel.invokeMethod<String>(Constants.getPlatformMapName);
+  }
+
+  @override
+  Future<bool> initClient() async {
+    bool? isSupport;
+    try {
+      isSupport = await _channel.invokeMethod<bool>(Constants.initClient);
+      if (isSupport == true) {
+        return true;
+      }
+    } catch (e) {}
+    return false;
+  }
+}

+ 62 - 0
plugins/map/lib/src/widget/map_widget.dart

@@ -0,0 +1,62 @@
+import 'dart:convert';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:map/src/core/map_controller.dart';
+import 'package:map_platform_interface/map_interface.dart';
+
+class MapWidget extends StatefulWidget {
+  final MapController? controller;
+  final MarkerTapCallback? onMarkerTap;
+
+  const MapWidget({
+    super.key,
+    this.controller,
+    this.onMarkerTap,
+  });
+
+  @override
+  State<MapWidget> createState() => _MapWidgetState();
+}
+
+class _MapWidgetState extends State<MapWidget> {
+  final String mapViewType = 'com.atmob.flutter.map';
+
+  @override
+  Widget build(BuildContext context) {
+    if (defaultTargetPlatform == TargetPlatform.android) {
+      return AndroidView(
+        viewType: mapViewType,
+        onPlatformViewCreated: onPlatformViewCreated,
+        creationParamsCodec: const StandardMessageCodec(),
+      );
+    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
+      return UiKitView(
+        viewType: mapViewType,
+        onPlatformViewCreated: onPlatformViewCreated,
+        creationParamsCodec: const StandardMessageCodec(),
+      );
+    }
+    return Text('当前平台:$defaultTargetPlatform, 不支持使用地图插件');
+  }
+
+  Future<void> onPlatformViewCreated(int viewId) async {
+    MethodChannel mapChannel =
+        MethodChannel('${Constants.mapViewChannelName}$viewId');
+    widget.controller?.setChannel(mapChannel);
+    mapChannel.setMethodCallHandler(_mapMethodCallHandler);
+  }
+
+  Future<dynamic> _mapMethodCallHandler(MethodCall call) async {
+    debugPrint(
+        '_mapMethodCallHandler...call.method==>${call.method},call.arguments==>${call.arguments}');
+    switch (call.method) {
+      case Constants.methodMarkerOnTap:
+        Map<String, dynamic> jsonMap = jsonDecode(call.arguments);
+        Marker marker = Marker.fromJson(jsonMap);
+        widget.onMarkerTap?.call(marker);
+        break;
+    }
+  }
+}

+ 29 - 0
plugins/map/pubspec.yaml

@@ -0,0 +1,29 @@
+name: map
+description: "定位地图调用总入口"
+version: 0.0.1
+homepage:
+
+environment:
+  sdk: ^3.5.0
+  flutter: '>=3.3.0'
+
+dependencies:
+  flutter:
+    sdk: flutter
+  plugin_platform_interface: ^2.0.2
+
+  map_platform_interface:
+    path: ../map_platform_interface
+
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+  flutter_lints: ^4.0.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+

+ 29 - 0
plugins/map_amap_android/.gitignore

@@ -0,0 +1,29 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+build/

+ 30 - 0
plugins/map_amap_android/.metadata

@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819"
+  channel: "stable"
+
+project_type: plugin
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+    - platform: android
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'

+ 3 - 0
plugins/map_amap_android/CHANGELOG.md

@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.

+ 1 - 0
plugins/map_amap_android/LICENSE

@@ -0,0 +1 @@
+TODO: Add your license here.

+ 15 - 0
plugins/map_amap_android/README.md

@@ -0,0 +1,15 @@
+# map_amap_android
+
+定位Android端高德地图实现
+
+## Getting Started
+
+This project is a starting point for a Flutter
+[plug-in package](https://flutter.dev/to/develop-plugins),
+a specialized package that includes platform-specific implementation code for
+Android and/or iOS.
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
+

+ 4 - 0
plugins/map_amap_android/analysis_options.yaml

@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

+ 9 - 0
plugins/map_amap_android/android/.gitignore

@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx

+ 89 - 0
plugins/map_amap_android/android/build.gradle

@@ -0,0 +1,89 @@
+group = "com.atmob.map_amap_android"
+version = "1.0"
+
+buildscript {
+    repositories {
+        google()
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath("com.android.tools.build:gradle:7.3.0")
+    }
+}
+
+rootProject.allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+
+// 加载 local.properties 文件
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withInputStream { stream ->
+        localProperties.load(stream)
+    }
+}
+
+// 读取变量
+def flutterSdk = localProperties.getProperty('flutter.sdk')
+
+apply plugin: "com.android.library"
+
+android {
+    if (project.android.hasProperty("namespace")) {
+        namespace = "com.atmob.map_amap_android"
+    }
+
+    compileSdk = 34
+
+    buildFeatures {
+        viewBinding true
+        dataBinding true
+    }
+
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+    }
+
+    defaultConfig {
+        minSdk = 21
+    }
+
+
+    dependencies {
+        //flutter
+        compileOnly files("$flutterSdk/bin/cache/artifacts/engine/android-arm/flutter.jar")
+
+        //AndroidX
+        compileOnly "androidx.annotation:annotation:1.1.0"
+
+
+        //constraintlayout
+        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+
+        //高德地图
+        implementation "com.amap.api:3dmap:9.7.0"
+        implementation 'com.amap.api:search:9.7.0'
+
+        //gson
+        implementation "com.google.code.gson:gson:2.10"
+
+    }
+
+    testOptions {
+        unitTests.all {
+            testLogging {
+                events "passed", "skipped", "failed", "standardOut", "standardError"
+                outputs.upToDateWhen { false }
+                showStandardStreams = true
+            }
+        }
+    }
+}

+ 20 - 0
plugins/map_amap_android/android/gradle.properties

@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library

BIN
plugins/map_amap_android/android/gradle/wrapper/gradle-wrapper.jar


+ 7 - 0
plugins/map_amap_android/android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 249 - 0
plugins/map_amap_android/android/gradlew

@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"

+ 92 - 0
plugins/map_amap_android/android/gradlew.bat

@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 1 - 0
plugins/map_amap_android/android/settings.gradle

@@ -0,0 +1 @@
+rootProject.name = 'map_amap_android'

+ 36 - 0
plugins/map_amap_android/android/src/main/AndroidManifest.xml

@@ -0,0 +1,36 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.atmob.map_amap_android">
+
+    <!-- 允许访问网络,必选权限 -->
+    <uses-permission android:name="android.permission.INTERNET" /> <!-- 允许获取精确位置,精准定位必选 -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 允许获取粗略位置,粗略定位必选 -->
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 允许获取设备和运营商信息,用于问题排查和网络定位(无gps情况下的定位),若需网络定位功能则必选 -->
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 允许获取网络状态,用于网络定位(无gps情况下的定位),若需网络定位功能则必选 -->
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 允许获取wifi网络信息,用于网络定位(无gps情况下的定位),若需网络定位功能则必选 -->
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 允许获取wifi状态改变,用于网络定位(无gps情况下的定位),若需网络定位功能则必选 -->
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 后台获取位置信息,若需后台定位则必选 -->
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- 用于申请调用A-GPS模块,卫星定位加速 -->
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 允许写设备缓存,用于问题排查 -->
+    <uses-permission
+        android:name="android.permission.WRITE_SETTINGS"
+        tools:ignore="ProtectedPermissions" /> <!-- 允许写入扩展存储,用于写入缓存定位数据 -->
+    <uses-permission
+        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+        tools:ignore="ScopedStorage" /> <!-- 允许读设备等信息,用于问题排查 -->
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 用于申请获取蓝牙信息进行室内定位 -->
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /><!-- 电池优化白名单 -->
+
+    <application>
+
+        <service
+            android:name="com.amap.api.location.APSService"
+            android:foregroundServiceType="location" />
+    </application>
+
+</manifest>

+ 19 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/FlutterViewPlugin.java

@@ -0,0 +1,19 @@
+package com.atmob.map_amap_android;
+
+
+import android.app.Activity;
+
+import androidx.lifecycle.LifecycleOwner;
+
+import com.atmob.map_amap_android.amap.MapViewFactory;
+import com.google.gson.Gson;
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+
+public class FlutterViewPlugin {
+
+
+    public static void registerWith(FlutterPlugin.FlutterPluginBinding flutterPluginBinding, Activity mActivity, LifecycleOwner lifecycleProvider) {
+        flutterPluginBinding.getPlatformViewRegistry().registerViewFactory("com.atmob.flutter.map", new MapViewFactory(mActivity, flutterPluginBinding.getBinaryMessenger(), lifecycleProvider));
+    }
+}

+ 88 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/MapAmapAndroidPlugin.java

@@ -0,0 +1,88 @@
+package com.atmob.map_amap_android;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+
+import com.atmob.map_amap_android.contants.Constants;
+import com.atmob.map_amap_android.lifecycle.FlutterLifecycleAdapter;
+import com.atmob.map_amap_android.util.AMapHelper;
+import com.atmob.map_amap_android.util.LogUtil;
+import com.google.gson.Gson;
+
+import io.flutter.BuildConfig;
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.embedding.engine.plugins.activity.ActivityAware;
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
+import io.flutter.plugin.common.MethodChannel.Result;
+
+/**
+ * MapAmapAndroidPlugin
+ */
+public class MapAmapAndroidPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
+    /// The MethodChannel that will the communication between Flutter and native Android
+    ///
+    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
+    /// when the Flutter Engine is detached from the Activity
+    private MethodChannel channel;
+    private Activity mActivity;
+    private Context applicationContext;
+    private FlutterPluginBinding flutterPluginBinding;
+    private Lifecycle lifecycle;
+
+    @Override
+    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+        LogUtil.setLogEnabled(BuildConfig.DEBUG);
+        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "atmob_map_method_channel");
+        applicationContext = flutterPluginBinding.getApplicationContext();
+        channel.setMethodCallHandler(this);
+        this.flutterPluginBinding = flutterPluginBinding;
+
+    }
+
+    @Override
+    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
+        if (call.method.equals(Constants.METHOD_INIT_CLIENT)) {
+            AMapHelper.init(applicationContext);
+            result.success(true);
+        } else if (call.method.equals(Constants.METHOD_GET_PLATFORM_MAP_NAME)) {
+            result.success(Constants.MAP_NAME);
+        } else {
+            result.notImplemented();
+        }
+    }
+
+
+    @Override
+    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+        channel.setMethodCallHandler(null);
+    }
+
+    @Override
+    public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
+        mActivity = activityPluginBinding.getActivity();
+        lifecycle = FlutterLifecycleAdapter.getActivityLifecycle(activityPluginBinding);
+        FlutterViewPlugin.registerWith(flutterPluginBinding, mActivity, () -> lifecycle);
+    }
+
+    @Override
+    public void onDetachedFromActivityForConfigChanges() {
+        this.onDetachedFromActivity();
+    }
+
+    @Override
+    public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding activityPluginBinding) {
+        onAttachedToActivity(activityPluginBinding);
+    }
+
+    @Override
+    public void onDetachedFromActivity() {
+        mActivity = null;
+        lifecycle = null;
+    }
+}

+ 243 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/AmapView.java

@@ -0,0 +1,243 @@
+package com.atmob.map_amap_android.amap;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.TextureMapView;
+import com.atmob.map_amap_android.contants.Constants;
+import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
+import com.atmob.map_amap_android.overlays.marker.MarkersController;
+import com.atmob.map_amap_android.util.LogUtil;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Objects;
+
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.platform.PlatformView;
+
+public class AmapView implements PlatformView, DefaultLifecycleObserver, MethodChannel.MethodCallHandler, ActivityPluginBinding.OnSaveInstanceStateListener {
+
+    private final String TAG = "AmapView";
+
+    private static final int DEFAULT_MAP_ZOOM_SCALE = 18;
+
+
+    private final MethodChannel channel;
+
+    private TextureMapView mapView = null;
+
+    private boolean disposed = false;
+
+    private final Map<String, MyMethodCallHandler> myMethodCallHandlerMap;
+
+    private MapController mapController;
+
+    private MarkersController markersController;
+
+
+    public AmapView(Context context, Activity activity, BinaryMessenger messenger, int viewId, Map<String, Object> args, LifecycleOwner lifecycleProvider) {
+        channel = new MethodChannel(messenger, Constants.MAP_VIEW_CHANNEL_NAME + viewId);
+        channel.setMethodCallHandler(this);
+        myMethodCallHandlerMap = new HashMap<>(8);
+
+        try {
+            mapView = new TextureMapView(context);
+            AMap amap = mapView.getMap();
+            initMapDefaultSetting(amap);
+
+            markersController = new MarkersController(context, channel, amap);
+            mapController = new MapController(context, channel, amap);
+
+            initMyMethodCallHandlerMap();
+
+            lifecycleProvider.getLifecycle().addObserver(this);
+        } catch (Exception e) {
+            LogUtil.e(TAG, "<init>", e);
+        }
+    }
+
+
+    private void initMyMethodCallHandlerMap() {
+        String[] methodIdArray = markersController.getRegisterMethodIdArray();
+        if (null != methodIdArray) {
+            for (String methodId : methodIdArray) {
+                myMethodCallHandlerMap.put(methodId, markersController);
+            }
+        }
+
+        methodIdArray = mapController.getRegisterMethodIdArray();
+        if (null != methodIdArray) {
+            for (String methodId : methodIdArray) {
+                myMethodCallHandlerMap.put(methodId, mapController);
+            }
+        }
+    }
+
+
+    private void initMapDefaultSetting(AMap map) {
+        if (map == null) {
+            return;
+        }
+        //目前直接设置
+        map.getUiSettings().setZoomControlsEnabled(false);
+        map.moveCamera(CameraUpdateFactory.zoomTo(DEFAULT_MAP_ZOOM_SCALE));
+    }
+
+
+    @Nullable
+    @Override
+    public View getView() {
+        return mapView;
+    }
+
+
+    @Override
+    public void dispose() {
+        LogUtil.i(TAG, "dispose==>");
+        try {
+            if (disposed) {
+                return;
+            }
+            channel.setMethodCallHandler(null);
+            destroyMapViewIfNecessary();
+            disposed = true;
+        } catch (Throwable e) {
+            LogUtil.e(TAG, "dispose", e);
+        }
+    }
+
+
+    @Override
+    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
+        LogUtil.i(TAG, "onMethodCall==>" + call.method + ", arguments==> " + call.arguments);
+        String methodId = call.method;
+        if (myMethodCallHandlerMap.containsKey(methodId)) {
+            try {
+                Objects.requireNonNull(myMethodCallHandlerMap.get(methodId)).doMethodCall(call, result);
+            } catch (Exception e) {
+                LogUtil.e(TAG, "onMethodCall", e);
+                result.error("Exception", e.getMessage(), e.getStackTrace());
+            }
+        } else {
+            LogUtil.w(TAG, "onMethodCall, the methodId: " + call.method + ", not implemented");
+            result.notImplemented();
+        }
+
+    }
+
+
+    @Override
+    public void onCreate(@NonNull LifecycleOwner owner) {
+        LogUtil.i(TAG, "onCreate==>");
+        try {
+            if (disposed) {
+                return;
+            }
+            if (null != mapView) {
+                mapView.onCreate(null);
+            }
+        } catch (Throwable e) {
+            LogUtil.e(TAG, "onCreate", e);
+        }
+    }
+
+    @Override
+    public void onStart(@NonNull LifecycleOwner owner) {
+        LogUtil.i(TAG, "onStart==>");
+    }
+
+
+    @Override
+    public void onResume(@NonNull LifecycleOwner owner) {
+        LogUtil.i(TAG, "onResume==>");
+        try {
+            if (disposed) {
+                return;
+            }
+            if (null != mapView) {
+                mapView.onResume();
+            }
+        } catch (Throwable e) {
+            LogUtil.e(TAG, "onResume", e);
+        }
+    }
+
+    @Override
+    public void onPause(@NonNull LifecycleOwner owner) {
+        LogUtil.i(TAG, "onPause==>");
+        try {
+            if (disposed) {
+                return;
+            }
+            mapView.onPause();
+        } catch (Throwable e) {
+            LogUtil.e(TAG, "onPause", e);
+        }
+    }
+
+    @Override
+    public void onStop(@NonNull LifecycleOwner owner) {
+        LogUtil.i(TAG, "onStop==>");
+    }
+
+    @Override
+    public void onDestroy(@NonNull LifecycleOwner owner) {
+        LogUtil.i(TAG, "onDestroy==>");
+        try {
+            if (disposed) {
+                return;
+            }
+            destroyMapViewIfNecessary();
+        } catch (Throwable e) {
+            LogUtil.e(TAG, "onDestroy", e);
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(@NonNull Bundle bundle) {
+        LogUtil.i(TAG, "onDestroy==>");
+        try {
+            if (disposed) {
+                return;
+            }
+            mapView.onSaveInstanceState(bundle);
+        } catch (Throwable e) {
+            LogUtil.e(TAG, "onSaveInstanceState", e);
+        }
+    }
+
+    @Override
+    public void onRestoreInstanceState(@Nullable Bundle bundle) {
+        LogUtil.i(TAG, "onDestroy==>");
+        try {
+            if (disposed) {
+                return;
+            }
+            mapView.onCreate(bundle);
+        } catch (Throwable e) {
+            LogUtil.e(TAG, "onRestoreInstanceState", e);
+        }
+    }
+
+    private void destroyMapViewIfNecessary() {
+        if (mapView == null) {
+            return;
+        }
+        mapView.onDestroy();
+    }
+}

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

@@ -0,0 +1,88 @@
+package com.atmob.map_amap_android.amap;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.atmob.map_amap_android.contants.Constants;
+import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
+import com.atmob.map_amap_android.util.LogUtil;
+import com.atmob.map_amap_android.util.ParamUtil;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+public class MapController implements MyMethodCallHandler {
+
+    private final String TAG = "MapController";
+
+    private final AMap map;
+
+    private final Context context;
+
+    private final MethodChannel methodChannel;
+
+    public MapController(Context context, MethodChannel methodChannel, AMap map) {
+        this.context = context;
+        this.methodChannel = methodChannel;
+        this.map = map;
+    }
+
+
+    @Override
+    public void doMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
+        switch (call.method) {
+            case Constants.METHOD_MOVE_CAMERA:
+                moveToCamera(call, result);
+                break;
+            case Constants.METHOD_ANIMATE_CAMERA:
+                animateCamera(call, result);
+                break;
+        }
+    }
+
+    private void animateCamera(MethodCall call, MethodChannel.Result result) {
+        LogUtil.i(TAG, "animateCamera===>" + call.arguments());
+        Map<String, Object> arguments = call.arguments();
+        Double longitude = ParamUtil.getDouble(arguments, "longitude");
+        Double latitude = ParamUtil.getDouble(arguments, "latitude");
+        Integer zoom = ParamUtil.getInt(arguments, "latitude");
+        if (longitude == null || latitude == null) {
+            result.error("error", "longitude or latitude is null", null);
+            return;
+        }
+        if (zoom == null) {
+            zoom = 18;
+        }
+        map.animateCamera(CameraUpdateFactory.newLatLngZoom(new com.amap.api.maps.model.LatLng(latitude, longitude), zoom));
+        LogUtil.i(TAG, "animateCamera===>success");
+        result.success(null);
+    }
+
+    private void moveToCamera(MethodCall call, MethodChannel.Result result) {
+        LogUtil.i(TAG, "moveToCamera===>" + call.arguments());
+        Map<String, Object> arguments = call.arguments();
+        Double longitude = ParamUtil.getDouble(arguments, "longitude");
+        Double latitude = ParamUtil.getDouble(arguments, "latitude");
+        Integer zoom = ParamUtil.getInt(arguments, "latitude");
+        if (longitude == null || latitude == null) {
+            result.error("error", "longitude or latitude is null", null);
+            return;
+        }
+        if (zoom == null) {
+            zoom = 18;
+        }
+        map.moveCamera(CameraUpdateFactory.newLatLngZoom(new com.amap.api.maps.model.LatLng(latitude, longitude), zoom));
+        LogUtil.i(TAG, "moveToCamera===>success");
+        result.success(null);
+    }
+
+    @Override
+    public String[] getRegisterMethodIdArray() {
+        return Constants.METHOD_ID_LIST_FOR_MAP;
+    }
+}

+ 39 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/amap/MapViewFactory.java

@@ -0,0 +1,39 @@
+package com.atmob.map_amap_android.amap;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LifecycleOwner;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.StandardMessageCodec;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewFactory;
+
+public class MapViewFactory extends PlatformViewFactory {
+
+    private final Activity activity;
+    private final BinaryMessenger messenger;
+    private final LifecycleOwner lifecycleProvider;
+
+    public MapViewFactory(Activity activity, BinaryMessenger messenger, LifecycleOwner lifecycleProvider) {
+        super(StandardMessageCodec.INSTANCE);
+        this.activity = activity;
+        this.messenger = messenger;
+        this.lifecycleProvider = lifecycleProvider;
+    }
+
+    @NonNull
+    @Override
+    public PlatformView create(Context context, int viewId, @Nullable Object args) {
+        Map<String, Object> params = null;
+        if (args instanceof Map) {
+            params = (Map<String, Object>) args;
+        }
+        return new AmapView(context, activity, messenger, viewId, params,lifecycleProvider);
+    }
+}

+ 105 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/bean/MakerInfo.java

@@ -0,0 +1,105 @@
+package com.atmob.map_amap_android.bean;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+public class MakerInfo {
+
+
+    @SerializedName("id")
+    private String id;
+
+    @SerializedName("markerName")
+    private String markerName;
+
+    @SerializedName("longitude")
+    private double longitude;
+
+    @SerializedName("latitude")
+    private double latitude;
+
+    @MarkerType
+    @SerializedName("markerType")
+    private int markerType;
+
+
+    @SerializedName("isSelected")
+    private boolean isSelected;
+
+    public boolean isSelected() {
+        return isSelected;
+    }
+
+    public void setSelected(boolean selected) {
+        isSelected = selected;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(double latitude) {
+        this.latitude = latitude;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(double longitude) {
+        this.longitude = longitude;
+    }
+
+    public String getMarkerName() {
+        return markerName;
+    }
+
+    public void setMarkerName(String markerName) {
+        this.markerName = markerName;
+    }
+
+    public int getMarkerType() {
+        return markerType;
+    }
+
+    public void setMarkerType(int markerType) {
+        this.markerType = markerType;
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({MarkerType.MINE, MarkerType.FRIEND, MarkerType.SIGNAL_STARTING_POINT, MarkerType.SIGNAL_END_POINT, MarkerType.TRAJECTORY_TARGET_POINT})
+    public @interface MarkerType {
+        int MINE = 1;
+        int FRIEND = 2;
+        int SIGNAL_STARTING_POINT = 3;
+        int SIGNAL_END_POINT = 4;
+        int TRAJECTORY_TARGET_POINT = 5;
+    }
+
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "MakerInfo{" +
+                "id='" + id + '\'' +
+                ", markerName='" + markerName + '\'' +
+                ", longitude=" + longitude +
+                ", latitude=" + latitude +
+                ", markerType=" + markerType +
+                ", isSelected=" + isSelected +
+                '}';
+    }
+}

+ 34 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java

@@ -0,0 +1,34 @@
+package com.atmob.map_amap_android.contants;
+
+public class Constants {
+
+
+    /*******渠道名称********/
+    public static final String MAP_VIEW_CHANNEL_NAME = "com.atmob.flutter_map/map_view_";
+
+
+    /*******方法名称********/
+    /*******基础功能********/
+    public static final String METHOD_INIT_CLIENT = "initClient";
+    public static final String METHOD_GET_PLATFORM_MAP_NAME = "getPlatformMapName";
+    public static final String MAP_NAME = "amap map";
+
+
+    /*******地图操作相关********/
+    public static final String METHOD_MOVE_CAMERA = "map#moveCamera";
+    public static final String METHOD_ANIMATE_CAMERA = "map#animateCamera";
+
+    public static final String[] METHOD_ID_LIST_FOR_MAP = {METHOD_MOVE_CAMERA, METHOD_ANIMATE_CAMERA};
+
+    /**
+     * markers
+     */
+    public static final String METHOD_UPDATE_MARKERS = "marker#updateMarkers";
+    public static final String METHOD_DELETE_MARKERS = "marker#deleteMarkers";
+    public static final String METHOD_MARKER_ON_TAP = "marker#onTap";
+
+
+    public static final String[] METHOD_ID_LIST_FOR_MARKER = {METHOD_UPDATE_MARKERS, METHOD_DELETE_MARKERS, METHOD_MARKER_ON_TAP};
+
+
+}

+ 27 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/lifecycle/FlutterLifecycleAdapter.java

@@ -0,0 +1,27 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package com.atmob.map_amap_android.lifecycle;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
+import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference;
+
+/** Provides a static method for extracting lifecycle objects from Flutter plugin bindings. */
+public class FlutterLifecycleAdapter {
+  /**
+   * Returns the lifecycle object for the activity a plugin is bound to.
+   *
+   * <p>Returns null if the Flutter engine version does not include the lifecycle extraction code.
+   * (this probably means the Flutter engine version is too old).
+   */
+  @NonNull
+  public static Lifecycle getActivityLifecycle(
+      @NonNull ActivityPluginBinding activityPluginBinding) {
+    HiddenLifecycleReference reference =
+        (HiddenLifecycleReference) activityPluginBinding.getLifecycle();
+    return reference.getLifecycle();
+  }
+}

+ 24 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/MyMethodCallHandler.java

@@ -0,0 +1,24 @@
+package com.atmob.map_amap_android.overlays;
+
+import androidx.annotation.NonNull;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * @author whm
+ * @date 2020/11/10 9:47 PM
+ * @mail hongming.whm@alibaba-inc.com
+ * @since
+ */
+public interface MyMethodCallHandler {
+
+    void doMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result);
+
+    /**
+     * 获取注册的{@link MethodCall#method}
+     *
+     * @return
+     */
+    String[] getRegisterMethodIdArray();
+}

+ 180 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/overlays/marker/MarkersController.java

@@ -0,0 +1,180 @@
+package com.atmob.map_amap_android.overlays.marker;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.CameraUpdateFactory;
+import com.amap.api.maps.model.BitmapDescriptor;
+import com.amap.api.maps.model.BitmapDescriptorFactory;
+import com.amap.api.maps.model.LatLng;
+import com.amap.api.maps.model.Marker;
+import com.amap.api.maps.model.MarkerOptions;
+import com.atmob.map_amap_android.R;
+import com.atmob.map_amap_android.bean.MakerInfo;
+import com.atmob.map_amap_android.contants.Constants;
+import com.atmob.map_amap_android.databinding.ItemLocationMarkerBinding;
+import com.atmob.map_amap_android.overlays.MyMethodCallHandler;
+import com.atmob.map_amap_android.util.GsonUtil;
+import com.atmob.map_amap_android.util.LogUtil;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+public class MarkersController implements MyMethodCallHandler, AMap.OnMarkerClickListener {
+
+    private final String TAG = "MarkersController";
+
+    private final AMap map;
+
+    private final Context context;
+
+    private final MethodChannel methodChannel;
+
+
+    private final HashMap<String, Marker> currentMarkers = new HashMap<>(10);
+
+    private ItemLocationMarkerBinding markerBinding;
+
+    private Gson gson;
+
+
+    public MarkersController(Context context, MethodChannel methodChannel, AMap map) {
+        this.context = context;
+        this.methodChannel = methodChannel;
+        this.map = map;
+        gson = GsonUtil.getInstance();
+        map.setOnMarkerClickListener(this);
+    }
+
+    @Override
+    public void doMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
+        LogUtil.i(TAG, "doMethodCall===>" + call.method);
+        switch (call.method) {
+            case Constants.METHOD_UPDATE_MARKERS:
+                updateMarkers(call, result);
+                break;
+            case Constants.METHOD_DELETE_MARKERS:
+                deleteMarkers(call, result);
+                break;
+        }
+    }
+
+    private void deleteMarkers(MethodCall call, MethodChannel.Result result) {
+
+    }
+
+    private void updateMarkers(MethodCall call, MethodChannel.Result result) {
+        LogUtil.i(TAG, "updateMarkers===>" + call.arguments());
+        String arguments = call.arguments();
+        if (arguments == null || TextUtils.isEmpty(arguments)) {
+            result.error("-1", "updateMarkers.arguments is empty", null);
+            return;
+        }
+        List<MakerInfo> list = gson.fromJson(arguments, new TypeToken<List<MakerInfo>>() {
+        });
+        if (list == null || list.isEmpty()) {
+            result.error("-1", "updateFriendMarkers.list is empty", null);
+            return;
+        }
+        LogUtil.i(TAG, "updateMarkers=list==>" + list.size());
+        for (MakerInfo makerInfo : list) {
+            updateMarker(makerInfo);
+        }
+        result.success(null);
+    }
+
+
+    private void updateMarker(MakerInfo makerInfo) {
+        LatLng latLng = new LatLng(makerInfo.getLatitude(), makerInfo.getLongitude());
+        Marker marker = currentMarkers.get(makerInfo.getId());
+        LogUtil.d(TAG, "updateMarkers==>" + makerInfo);
+        if (marker != null) {
+            marker.setPosition(latLng);
+            Object object = marker.getObject();
+            if (object instanceof MakerInfo) {
+                MakerInfo cacheInfo = (MakerInfo) object;
+                if (!Objects.equals(cacheInfo.getMarkerName(), makerInfo.getMarkerName())
+                        || cacheInfo.isSelected() != makerInfo.isSelected()) {
+                    BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo.getMarkerName(), makerInfo.isSelected(), makerInfo.getMarkerType() == MakerInfo.MarkerType.MINE);
+                    marker.setIcon(markerBitmap);
+                    marker.setObject(makerInfo);
+                }
+            }
+            LogUtil.i(TAG, "updateMarkers=marker..updateSuccess,id=" + makerInfo.getId());
+        } else {
+            BitmapDescriptor markerBitmap = getMarkerBitmap(makerInfo.getMarkerName(), makerInfo.isSelected(), makerInfo.getMarkerType() == MakerInfo.MarkerType.MINE);
+            MarkerOptions markerOption = new MarkerOptions()
+                    .position(latLng)
+                    .icon(markerBitmap)
+                    .anchor(0.5f, 1);
+            marker = map.addMarker(markerOption);
+            marker.setObject(makerInfo);
+            currentMarkers.put(makerInfo.getId(), marker);
+            LogUtil.i(TAG, "updateMarkers=marker..addSuccess,id=" + makerInfo.getId());
+        }
+    }
+
+
+    @Override
+    public String[] getRegisterMethodIdArray() {
+        return Constants.METHOD_ID_LIST_FOR_MARKER;
+    }
+
+
+    public BitmapDescriptor getMarkerBitmap(String name, boolean isSelected, boolean isSelf) {
+        if (markerBinding == null) {
+            markerBinding = ItemLocationMarkerBinding.inflate(LayoutInflater.from(context));
+        }
+        ConstraintLayout view = markerBinding.getRoot();
+
+        markerBinding.locationMarkerTvName.setText(name);
+        markerBinding.ivMarkerAvatar.setImageResource(isSelf ? R.drawable.icon_default_mine_avatar : R.drawable.icon_default_friend_avatar);
+        markerBinding.vBottom.setBackgroundResource(isSelected ? R.drawable.icon_marker_selected_bottom_circle : R.drawable.icon_marker_normal_bottom_circle);
+        markerBinding.locationMarkerIvBg.setImageResource(isSelected ? R.drawable.icon_location_marker_selected : R.drawable.icon_location_marker_normal);
+        view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+        Bitmap markerBitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
+        Canvas markerCanvas = new Canvas(markerBitmap);
+        view.draw(markerCanvas);
+        BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(markerBitmap);
+        markerCanvas.setBitmap(null);
+        markerBitmap.recycle();
+        return bitmapDescriptor;
+    }
+
+    @Override
+    public boolean onMarkerClick(Marker marker) {
+        Object object = marker.getObject();
+        if (object == null) {
+            return false;
+        }
+        if (object instanceof MakerInfo) {
+            MakerInfo markerInfo = (MakerInfo) object;
+            markerInfo.setLatitude(marker.getPosition().latitude);
+            markerInfo.setLongitude(marker.getPosition().longitude);
+            LogUtil.d(TAG, "onMarkerClick==>" + markerInfo);
+            methodChannel.invokeMethod(Constants.METHOD_MARKER_ON_TAP, gson.toJson(markerInfo));
+            return true;
+        }
+        return false;
+    }
+}

+ 81 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/AMapHelper.java

@@ -0,0 +1,81 @@
+package com.atmob.map_amap_android.util;
+
+import android.annotation.SuppressLint;
+import android.app.Application;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import com.amap.api.location.AMapLocation;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class AMapHelper {
+    private static final String TAG = "AMapHelper";
+    public static final String KEY_LAST_TRACK_ID = "key_amap_last_track_id";
+    private static final int AMAP_NOTIFICATION_ID = 340;
+    private static final String AMAP_NOTIFICATION_CHANNEL_ID = "amap_notif_channel_id";
+    private static final String AMAP_NOTIFICATION_CHANNEL_NAME = "Location Tracking";
+    private static final AtomicBoolean isLocationInit = new AtomicBoolean(false);
+    private static final ArrayList<OnLocationListener> locationListeners = new ArrayList<>();
+    private static AMapLocation lastLocation;
+    @SuppressLint("StaticFieldLeak")
+    private static AMapLocationClient aMapLocationClient;
+
+    public static void init(Context context) {
+        if (ProcessUtil.isMainProcess(context)) {
+            initLocation(context);
+        }
+    }
+
+    private static void initLocation(Context context) {
+        if (isLocationInit.compareAndSet(false, true)) {
+            try {
+                Log.d(TAG, "amap ... initLocation...");
+                AMapLocationClient.updatePrivacyShow(context, true, true);
+                AMapLocationClient.updatePrivacyAgree(context, true);
+                aMapLocationClient = new AMapLocationClient(context);
+                aMapLocationClient.setLocationOption(new AMapLocationClientOption().setInterval(5000));
+                aMapLocationClient.setLocationListener(aMapLocation -> {
+                    if (aMapLocation != null) {
+                        if (aMapLocation.getErrorCode() == 0) {
+                            lastLocation = aMapLocation;
+                            Log.d(TAG, "onLocationChanged: " + aMapLocation);
+                            for (OnLocationListener locationListener : locationListeners) {
+                                locationListener.onLocationChanged(aMapLocation);
+                            }
+                        }
+                    }
+                });
+//                if (PermissionUtil.hasPermission(LocationUtil.getLocationPermissions())) {
+//                    aMapLocationClient.startLocation();
+//                    aMapLocationClient.enableBackgroundLocation(AMAP_NOTIFICATION_ID, createLocationNotification(application));
+//                }
+            } catch (Exception e) {
+                isLocationInit.set(false);
+                Log.e(TAG, "initLocation failed.", e);
+            }
+        }
+    }
+
+
+
+    public static AMapLocation getLastLocation() {
+        return lastLocation;
+    }
+
+    public static void addOnLocationListener(OnLocationListener locationListener) {
+        locationListeners.add(locationListener);
+    }
+
+    public static void removeOnLocationListener(OnLocationListener locationListener) {
+        locationListeners.remove(locationListener);
+    }
+
+    public interface OnLocationListener {
+        void onLocationChanged(@NonNull AMapLocation aMapLocation);
+    }
+
+}

+ 16 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/GsonUtil.java

@@ -0,0 +1,16 @@
+package com.atmob.map_amap_android.util;
+
+import com.google.gson.Gson;
+
+public class GsonUtil {
+    private GsonUtil() {
+    }
+
+    private static class GsonUtilHolder {
+        private static final Gson instance = new Gson();
+    }
+
+    public static Gson getInstance() {
+        return GsonUtilHolder.instance;
+    }
+}

+ 88 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/LogUtil.java

@@ -0,0 +1,88 @@
+package com.atmob.map_amap_android.util;
+
+import android.util.Log;
+
+public class LogUtil {
+
+    // 日志开关(默认开启)
+    private static boolean isLogEnabled = false;
+
+    private static final String TAG = "AMapLogUtil_";
+
+    /**
+     * 设置日志开关
+     *
+     * @param enabled true 开启日志,false 关闭日志
+     */
+    public static void setLogEnabled(boolean enabled) {
+        isLogEnabled = enabled;
+    }
+
+    /**
+     * 打印 VERBOSE 级别日志
+     *
+     * @param msg 日志内容
+     */
+    public static void v(String tag, String msg) {
+        if (isLogEnabled) {
+            Log.v(TAG + tag, msg);
+        }
+    }
+
+    /**
+     * 打印 DEBUG 级别日志
+     *
+     * @param msg 日志内容
+     */
+    public static void d(String tag, String msg) {
+        if (isLogEnabled) {
+            Log.d(TAG + tag, msg);
+        }
+    }
+
+    /**
+     * 打印 INFO 级别日志
+     *
+     * @param msg 日志内容
+     */
+    public static void i(String tag, String msg) {
+        if (isLogEnabled) {
+            Log.i(TAG + tag, msg);
+        }
+    }
+
+    /**
+     * 打印 WARN 级别日志
+     *
+     * @param msg 日志内容
+     */
+    public static void w(String tag, String msg) {
+        if (isLogEnabled) {
+            Log.w(TAG + tag, msg);
+        }
+    }
+
+    /**
+     * 打印 ERROR 级别日志
+     *
+     * @param msg 日志内容
+     */
+    public static void e(String tag, String msg) {
+        if (isLogEnabled) {
+            Log.e(TAG + tag, msg);
+        }
+    }
+
+    /**
+     * 打印 ERROR 级别日志(带异常信息)
+     *
+     * @param msg 日志内容
+     * @param tr  异常信息
+     */
+    public static void e(String tag, String msg, Throwable tr) {
+        if (isLogEnabled) {
+            Log.e(TAG + tag, msg, tr);
+        }
+    }
+
+}

+ 50 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/ParamUtil.java

@@ -0,0 +1,50 @@
+package com.atmob.map_amap_android.util;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.MethodCall;
+
+public class ParamUtil {
+
+
+    public static String getString(Map<String, Object> map, String key) {
+        if (map == null) {
+            return null;
+        }
+        if (!map.containsKey(key)) {
+            return null;
+        }
+        if (map.get(key) instanceof String) {
+            return (String) map.get(key);
+        }
+        return null;
+    }
+
+    public static Integer getInt(Map<String, Object> map, String key) {
+        if (map == null) {
+            return null;
+        }
+        if (!map.containsKey(key)) {
+            return null;
+        }
+        if (map.get(key) instanceof Integer) {
+            return (Integer) map.get(key);
+        }
+        return null;
+    }
+
+    public static Double getDouble(Map<String, Object> map, String key) {
+        if (map == null) {
+            return null;
+        }
+        if (!map.containsKey(key)) {
+            return null;
+        }
+        if (map.get(key) instanceof Double) {
+            return (Double) map.get(key);
+        }
+        return null;
+    }
+
+
+}

+ 55 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/ProcessUtil.java

@@ -0,0 +1,55 @@
+package com.atmob.map_amap_android.util;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.Process;
+import android.text.TextUtils;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+public class ProcessUtil {
+    private ProcessUtil() {
+
+    }
+
+    /**
+     * 获取进程号对应的进程名
+     *
+     * @param pid 进程号
+     * @return 进程名
+     */
+    private static String getProcessName(int pid) {
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline"));
+            String processName = reader.readLine();
+            if (!TextUtils.isEmpty(processName)) {
+                processName = processName.trim();
+            }
+            return processName;
+        } catch (Throwable throwable) {
+            throwable.printStackTrace();
+        } finally {
+            try {
+                if (reader != null) {
+                    reader.close();
+                }
+            } catch (IOException exception) {
+                exception.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    public static boolean isMainProcess(Context context) {
+        String packageName = context.getPackageName();
+        int pid = Process.myPid();
+        String processName = getProcessName(pid);
+        return processName == null || processName.equals(packageName);
+    }
+
+    public static boolean isMainThread() {
+        return Looper.getMainLooper() == Looper.myLooper();
+    }
+}

BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/bg_location_marker_bubble.9.png


BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/bg_marker_normal.webp


BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_default_friend_avatar.webp


BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_default_mine_avatar.webp


BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_location_marker_normal.webp


BIN
plugins/map_amap_android/android/src/main/res/drawable-xxhdpi/icon_location_marker_selected.png


+ 9 - 0
plugins/map_amap_android/android/src/main/res/drawable/icon_marker_normal_bottom_circle.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <corners android:radius="100dp" />
+    <solid android:color="#7B7DFF" />
+    <stroke
+        android:width="2dp"
+        android:color="#fff" />
+</shape>

+ 9 - 0
plugins/map_amap_android/android/src/main/res/drawable/icon_marker_selected_bottom_circle.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <corners android:radius="100dp" />
+    <solid android:color="#fff" />
+    <stroke
+        android:width="2dp"
+        android:color="#7B7DFF" />
+</shape>

+ 63 - 0
plugins/map_amap_android/android/src/main/res/layout/item_location_marker.xml

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/location_marker_tv_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/bg_location_marker_bubble"
+        android:gravity="center"
+        android:paddingHorizontal="12dp"
+        android:textColor="#FF000000"
+        android:textSize="10sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="12345678910" />
+
+    <Space
+        app:layout_constraintEnd_toEndOf="@+id/location_marker_iv_bg"
+        app:layout_constraintStart_toStartOf="@+id/location_marker_iv_bg"
+        app:layout_constraintTop_toTopOf="@+id/location_marker_iv_bg"
+        android:id="@+id/space_bottom"
+        android:layout_width="wrap_content"
+        android:layout_height="44dp" />
+
+
+    <View
+        android:id="@+id/v_bottom"
+        app:layout_constraintTop_toBottomOf="@+id/space_bottom"
+        tools:background="@drawable/icon_marker_selected_bottom_circle"
+        app:layout_constraintEnd_toEndOf="@+id/location_marker_iv_bg"
+        app:layout_constraintStart_toStartOf="@+id/location_marker_iv_bg"
+        android:layout_width="11dp"
+        android:layout_height="11dp" />
+
+    <ImageView
+        android:id="@+id/location_marker_iv_bg"
+        android:layout_width="44dp"
+        app:layout_constraintDimensionRatio="132:150"
+        android:layout_height="0dp"
+        app:layout_constraintEnd_toEndOf="@+id/location_marker_tv_name"
+        app:layout_constraintStart_toStartOf="@+id/location_marker_tv_name"
+        app:layout_constraintTop_toBottomOf="@+id/location_marker_tv_name"
+        tools:src="@drawable/icon_location_marker_selected" />
+
+
+    <ImageView
+        android:id="@+id/iv_marker_avatar"
+        app:layout_constraintVertical_bias="0.25"
+        app:layout_constraintBottom_toBottomOf="@+id/location_marker_iv_bg"
+        app:layout_constraintTop_toTopOf="@+id/location_marker_iv_bg"
+        app:layout_constraintEnd_toEndOf="@+id/location_marker_iv_bg"
+        app:layout_constraintStart_toStartOf="@+id/location_marker_iv_bg"
+        tools:src="@drawable/icon_default_friend_avatar"
+        android:layout_width="38dp"
+        android:layout_height="38dp" />
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 29 - 0
plugins/map_amap_android/android/src/test/java/com/atmob/map_amap_android/MapAmapAndroidPluginTest.java

@@ -0,0 +1,29 @@
+package com.atmob.map_amap_android;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import org.junit.Test;
+
+/**
+ * This demonstrates a simple unit test of the Java portion of this plugin's implementation.
+ *
+ * Once you have built the plugin's example app, you can run these tests from the command
+ * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
+ * you can run them directly from IDEs that support JUnit such as Android Studio.
+ */
+
+public class MapAmapAndroidPluginTest {
+  @Test
+  public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
+    MapAmapAndroidPlugin plugin = new MapAmapAndroidPlugin();
+
+    final MethodCall call = new MethodCall("getPlatformVersion", null);
+    MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
+    plugin.onMethodCall(call, mockResult);
+
+    verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
+  }
+}

+ 73 - 0
plugins/map_amap_android/pubspec.yaml

@@ -0,0 +1,73 @@
+name: map_amap_android
+description: "定位Android端高德地图实现"
+version: 0.0.1
+homepage:
+
+environment:
+  sdk: ^3.5.0
+  flutter: '>=3.3.0'
+
+dependencies:
+  flutter:
+    sdk: flutter
+  plugin_platform_interface: ^2.0.2
+
+  map_platform_interface:
+    path: ../map_platform_interface
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+  flutter_lints: ^4.0.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+  # This section identifies this Flutter project as a plugin project.
+  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
+  # which should be registered in the plugin registry. This is required for
+  # using method channels.
+  # The Android 'package' specifies package in which the registered class is.
+  # This is required for using method channels on Android.
+  # The 'ffiPlugin' specifies that native code should be built and bundled.
+  # This is required for using `dart:ffi`.
+  # All these are used by the tooling to maintain consistency when
+  # adding or updating assets for this project.
+  plugin:
+    platforms:
+      android:
+        package: com.atmob.map_amap_android
+        pluginClass: MapAmapAndroidPlugin
+
+  # To add assets to your plugin package, add an assets section, like this:
+  # assets:
+  #   - images/a_dot_burr.jpeg
+  #   - images/a_dot_ham.jpeg
+  #
+  # For details regarding assets in packages, see
+  # https://flutter.dev/to/asset-from-package
+  #
+  # An image asset can refer to one or more resolution-specific "variants", see
+  # https://flutter.dev/to/resolution-aware-images
+
+  # To add custom fonts to your plugin package, add a fonts section here,
+  # in this "flutter" section. Each entry in this list should have a
+  # "family" key with the font family name, and a "fonts" key with a
+  # list giving the asset and other descriptors for the font. For
+  # example:
+  # fonts:
+  #   - family: Schyler
+  #     fonts:
+  #       - asset: fonts/Schyler-Regular.ttf
+  #       - asset: fonts/Schyler-Italic.ttf
+  #         style: italic
+  #   - family: Trajan Pro
+  #     fonts:
+  #       - asset: fonts/TrajanPro.ttf
+  #       - asset: fonts/TrajanPro_Bold.ttf
+  #         weight: 700
+  #
+  # For details regarding fonts in packages, see
+  # https://flutter.dev/to/font-from-package

+ 29 - 0
plugins/map_google_android/.gitignore

@@ -0,0 +1,29 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+build/

+ 30 - 0
plugins/map_google_android/.metadata

@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819"
+  channel: "stable"
+
+project_type: plugin
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+    - platform: android
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'

+ 3 - 0
plugins/map_google_android/CHANGELOG.md

@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.

+ 1 - 0
plugins/map_google_android/LICENSE

@@ -0,0 +1 @@
+TODO: Add your license here.

+ 15 - 0
plugins/map_google_android/README.md

@@ -0,0 +1,15 @@
+# map_google_android
+
+定位谷歌原生Android端实现插件
+
+## Getting Started
+
+This project is a starting point for a Flutter
+[plug-in package](https://flutter.dev/to/develop-plugins),
+a specialized package that includes platform-specific implementation code for
+Android and/or iOS.
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
+

+ 4 - 0
plugins/map_google_android/analysis_options.yaml

@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

+ 9 - 0
plugins/map_google_android/android/.gitignore

@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx

+ 69 - 0
plugins/map_google_android/android/build.gradle

@@ -0,0 +1,69 @@
+group = "com.atmob.map_google_android"
+version = "1.0"
+
+buildscript {
+    repositories {
+        google()
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath("com.android.tools.build:gradle:7.3.0")
+    }
+}
+
+rootProject.allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+// 加载 local.properties 文件
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withInputStream { stream ->
+        localProperties.load(stream)
+    }
+}
+
+// 读取变量
+def flutterSdk = localProperties.getProperty('flutter.sdk')
+
+apply plugin: "com.android.library"
+
+android {
+    if (project.android.hasProperty("namespace")) {
+        namespace = "com.atmob.map_google_android"
+    }
+
+    compileSdk = 34
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+    }
+
+    defaultConfig {
+        minSdk = 21
+    }
+
+    dependencies {
+        //flutter
+        compileOnly files("$flutterSdk/bin/cache/artifacts/engine/android-arm/flutter.jar")
+
+        //AndroidX
+        compileOnly "androidx.annotation:annotation:1.1.0"
+    }
+
+    testOptions {
+        unitTests.all {
+            testLogging {
+                events "passed", "skipped", "failed", "standardOut", "standardError"
+                outputs.upToDateWhen { false }
+                showStandardStreams = true
+            }
+        }
+    }
+}

+ 3 - 0
plugins/map_google_android/android/gradle.properties

@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+android.enableJetifier=true

BIN
plugins/map_google_android/android/gradle/wrapper/gradle-wrapper.jar


+ 7 - 0
plugins/map_google_android/android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 249 - 0
plugins/map_google_android/android/gradlew

@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"

+ 92 - 0
plugins/map_google_android/android/gradlew.bat

@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 1 - 0
plugins/map_google_android/android/settings.gradle

@@ -0,0 +1 @@
+rootProject.name = 'map_google_android'

+ 2 - 0
plugins/map_google_android/android/src/main/AndroidManifest.xml

@@ -0,0 +1,2 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.atmob.map_google_android"></manifest>

+ 15 - 0
plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/FlutterViewPlugin.java

@@ -0,0 +1,15 @@
+package com.atmob.map_google_android;
+
+
+import android.app.Activity;
+import com.atmob.map_google_android.googlemap.MapViewFactory;
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+
+public class FlutterViewPlugin {
+
+
+    public static void registerWith(FlutterPlugin.FlutterPluginBinding flutterPluginBinding, Activity mActivity) {
+        flutterPluginBinding.getPlatformViewRegistry().registerViewFactory("com.atmob.flutter.map", new MapViewFactory(mActivity, flutterPluginBinding.getBinaryMessenger()));
+    }
+}

+ 76 - 0
plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/MapGoogleAndroidPlugin.java

@@ -0,0 +1,76 @@
+package com.atmob.map_google_android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.atmob.map_google_android.conts.FlutterMapNameConfig;
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.embedding.engine.plugins.activity.ActivityAware;
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
+import io.flutter.plugin.common.MethodChannel.Result;
+
+/**
+ * MapGoogleAndroidPlugin
+ */
+public class MapGoogleAndroidPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
+    /// The MethodChannel that will the communication between Flutter and native Android
+    ///
+    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
+    /// when the Flutter Engine is detached from the Activity
+    private MethodChannel channel;
+    private Activity mActivity;
+    private Context applicationContext;
+    private FlutterPluginBinding flutterPluginBinding;
+
+    @Override
+    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), FlutterMapNameConfig.MAP_METHOD_CHANNEL);
+        applicationContext = flutterPluginBinding.getApplicationContext();
+        channel.setMethodCallHandler(this);
+        this.flutterPluginBinding = flutterPluginBinding;
+    }
+
+    @Override
+    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
+        if (call.method.equals("getPlatformVersion")) {
+            result.success("Android " + android.os.Build.VERSION.RELEASE);
+        } else if (call.method.equals("checkIsSupport")) {
+            result.success(true);
+        } else {
+            result.notImplemented();
+        }
+    }
+
+    @Override
+    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+        channel.setMethodCallHandler(null);
+    }
+
+    @Override
+    public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
+        mActivity = activityPluginBinding.getActivity();
+        FlutterViewPlugin.registerWith(flutterPluginBinding, mActivity);
+    }
+
+    @Override
+    public void onDetachedFromActivityForConfigChanges() {
+
+    }
+
+    @Override
+    public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding activityPluginBinding) {
+
+    }
+
+    @Override
+    public void onDetachedFromActivity() {
+        mActivity = null;
+    }
+}

+ 9 - 0
plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/conts/FlutterMapNameConfig.java

@@ -0,0 +1,9 @@
+package com.atmob.map_google_android.conts;
+
+public class FlutterMapNameConfig {
+
+    private FlutterMapNameConfig() {
+    }
+
+    public static final String MAP_METHOD_CHANNEL = "atmob_map_method_channel";
+}

+ 70 - 0
plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/googlemap/GoogleMapView.java

@@ -0,0 +1,70 @@
+package com.atmob.map_google_android.googlemap;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.platform.PlatformView;
+
+public class GoogleMapView implements PlatformView {
+
+    private final String TAG = "GoogleMapView";
+
+    private final Context context;
+    private final Activity activity;
+
+    private final BinaryMessenger messenger;
+
+    private final int viewId;
+
+    Map<String, Object> args;
+
+    private MethodChannel channel;
+
+    int adFuncId;
+
+    private View mContainer = null;
+
+    public GoogleMapView(Context context, Activity activity, BinaryMessenger messenger, int viewId, Map<String, Object> args) {
+        this.context = context;
+        this.activity = activity;
+        this.messenger = messenger;
+        this.viewId = viewId;
+        this.args = args;
+        initParams();
+        initView();
+    }
+
+    void initView() {
+        TextView textView = new TextView(context);
+        textView.setText("谷歌地图 mapview");
+        mContainer = textView;
+    }
+
+
+    void initParams() {
+
+    }
+
+
+    @Nullable
+    @Override
+    public View getView() {
+        return mContainer;
+    }
+
+
+    @Override
+    public void dispose() {
+        channel.setMethodCallHandler(null);
+    }
+
+
+}

+ 36 - 0
plugins/map_google_android/android/src/main/java/com/atmob/map_google_android/googlemap/MapViewFactory.java

@@ -0,0 +1,36 @@
+package com.atmob.map_google_android.googlemap;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.StandardMessageCodec;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewFactory;
+
+public class MapViewFactory extends PlatformViewFactory {
+
+    private final Activity activity;
+    private final BinaryMessenger messenger;
+
+    public MapViewFactory(Activity activity, BinaryMessenger messenger) {
+        super(StandardMessageCodec.INSTANCE);
+        this.activity = activity;
+        this.messenger = messenger;
+    }
+
+    @NonNull
+    @Override
+    public PlatformView create(Context context, int viewId, @Nullable Object args) {
+        Map<String, Object> params = null;
+        if (args instanceof Map) {
+            params = (Map<String, Object>) args;
+        }
+        return new GoogleMapView(context, activity, messenger, viewId, params);
+    }
+}

+ 29 - 0
plugins/map_google_android/android/src/test/java/com/atmob/map_google_android/MapGoogleAndroidPluginTest.java

@@ -0,0 +1,29 @@
+package com.atmob.map_google_android;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import org.junit.Test;
+
+/**
+ * This demonstrates a simple unit test of the Java portion of this plugin's implementation.
+ *
+ * Once you have built the plugin's example app, you can run these tests from the command
+ * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
+ * you can run them directly from IDEs that support JUnit such as Android Studio.
+ */
+
+public class MapGoogleAndroidPluginTest {
+  @Test
+  public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
+    MapGoogleAndroidPlugin plugin = new MapGoogleAndroidPlugin();
+
+    final MethodCall call = new MethodCall("getPlatformVersion", null);
+    MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
+    plugin.onMethodCall(call, mockResult);
+
+    verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
+  }
+}

+ 3 - 0
plugins/map_google_android/lib/map_google_android.dart

@@ -0,0 +1,3 @@
+class MapGoogleAndroid {
+  MapGoogleAndroid._();
+}

+ 11 - 0
plugins/map_google_android/lib/map_google_android_method_channel.dart

@@ -0,0 +1,11 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+
+import 'map_google_android_platform_interface.dart';
+
+/// An implementation of [MapGoogleAndroidPlatform] that uses method channels.
+class MethodChannelMapGoogleAndroid extends MapGoogleAndroidPlatform {
+  /// The method channel used to interact with the native platform.
+  @visibleForTesting
+  final methodChannel = const MethodChannel('map_google_android');
+}

+ 28 - 0
plugins/map_google_android/lib/map_google_android_platform_interface.dart

@@ -0,0 +1,28 @@
+import 'package:flutter/cupertino.dart';
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+import 'map_google_android_method_channel.dart';
+
+abstract class MapGoogleAndroidPlatform extends PlatformInterface {
+  /// Constructs a MapGoogleAndroidPlatform.
+  MapGoogleAndroidPlatform() : super(token: _token) {
+    debugPrint('MapGoogleAndroidPlatform');
+  }
+
+  static final Object _token = Object();
+
+  static MapGoogleAndroidPlatform _instance = MethodChannelMapGoogleAndroid();
+
+  /// The default instance of [MapGoogleAndroidPlatform] to use.
+  ///
+  /// Defaults to [MethodChannelMapGoogleAndroid].
+  static MapGoogleAndroidPlatform get instance => _instance;
+
+  /// Platform-specific implementations should set this with their own
+  /// platform-specific class that extends [MapGoogleAndroidPlatform] when
+  /// they register themselves.
+  static set instance(MapGoogleAndroidPlatform instance) {
+    PlatformInterface.verifyToken(instance, _token);
+    _instance = instance;
+  }
+}

+ 74 - 0
plugins/map_google_android/pubspec.yaml

@@ -0,0 +1,74 @@
+name: map_google_android
+description: "定位谷歌原生Android端实现插件"
+version: 0.0.1
+homepage:
+
+environment:
+  sdk: ^3.5.0
+  flutter: '>=3.3.0'
+
+dependencies:
+  flutter:
+    sdk: flutter
+  plugin_platform_interface: ^2.0.2
+
+  map_platform_interface:
+    path: ../map_platform_interface
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+  flutter_lints: ^4.0.0
+
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+  # This section identifies this Flutter project as a plugin project.
+  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
+  # which should be registered in the plugin registry. This is required for
+  # using method channels.
+  # The Android 'package' specifies package in which the registered class is.
+  # This is required for using method channels on Android.
+  # The 'ffiPlugin' specifies that native code should be built and bundled.
+  # This is required for using `dart:ffi`.
+  # All these are used by the tooling to maintain consistency when
+  # adding or updating assets for this project.
+  plugin:
+    platforms:
+      android:
+        package: com.atmob.map_google_android
+        pluginClass: MapGoogleAndroidPlugin
+
+  # To add assets to your plugin package, add an assets section, like this:
+  # assets:
+  #   - images/a_dot_burr.jpeg
+  #   - images/a_dot_ham.jpeg
+  #
+  # For details regarding assets in packages, see
+  # https://flutter.dev/to/asset-from-package
+  #
+  # An image asset can refer to one or more resolution-specific "variants", see
+  # https://flutter.dev/to/resolution-aware-images
+
+  # To add custom fonts to your plugin package, add a fonts section here,
+  # in this "flutter" section. Each entry in this list should have a
+  # "family" key with the font family name, and a "fonts" key with a
+  # list giving the asset and other descriptors for the font. For
+  # example:
+  # fonts:
+  #   - family: Schyler
+  #     fonts:
+  #       - asset: fonts/Schyler-Regular.ttf
+  #       - asset: fonts/Schyler-Italic.ttf
+  #         style: italic
+  #   - family: Trajan Pro
+  #     fonts:
+  #       - asset: fonts/TrajanPro.ttf
+  #       - asset: fonts/TrajanPro_Bold.ttf
+  #         weight: 700
+  #
+  # For details regarding fonts in packages, see
+  # https://flutter.dev/to/font-from-package

+ 29 - 0
plugins/map_platform_interface/.gitignore

@@ -0,0 +1,29 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+build/

+ 10 - 0
plugins/map_platform_interface/.metadata

@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819"
+  channel: "stable"
+
+project_type: package

+ 3 - 0
plugins/map_platform_interface/CHANGELOG.md

@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.

+ 1 - 0
plugins/map_platform_interface/LICENSE

@@ -0,0 +1 @@
+TODO: Add your license here.

+ 39 - 0
plugins/map_platform_interface/README.md

@@ -0,0 +1,39 @@
+<!--
+This README describes the package. If you publish this package to pub.dev,
+this README's contents appear on the landing page for your package.
+
+For information about how to write a good package README, see the guide for
+[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
+
+For general information about developing packages, see the Dart guide for
+[creating packages](https://dart.dev/guides/libraries/create-packages)
+and the Flutter guide for
+[developing packages and plugins](https://flutter.dev/to/develop-packages).
+-->
+
+TODO: Put a short description of the package here that helps potential users
+know whether this package might be useful for them.
+
+## Features
+
+TODO: List what your package can do. Maybe include images, gifs, or videos.
+
+## Getting started
+
+TODO: List prerequisites and provide or point to information on how to
+start using the package.
+
+## Usage
+
+TODO: Include short and useful examples for package users. Add longer examples
+to `/example` folder.
+
+```dart
+const like = 'sample';
+```
+
+## Additional information
+
+TODO: Tell users more about the package: where to find more information, how to
+contribute to the package, how to file issues, what response they can expect
+from the package authors, and more.

+ 4 - 0
plugins/map_platform_interface/analysis_options.yaml

@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

+ 14 - 0
plugins/map_platform_interface/lib/map_interface.dart

@@ -0,0 +1,14 @@
+library map_platform_interface;
+
+//公共配置.
+export 'package:map_platform_interface/src/consts/flutter_map_name_config.dart';
+export 'package:map_platform_interface/src/consts/constants.dart';
+
+//实体类
+export 'package:map_platform_interface/src/consts/marker_type.dart';
+export 'package:map_platform_interface/src/entity/marker.dart';
+export 'package:map_platform_interface/src/entity/camera_position.dart';
+
+//接口
+export 'package:map_platform_interface/src/interface/map_platform_interface.dart';
+export 'package:map_platform_interface/src/interface/map_overlays_interface.dart';

+ 19 - 0
plugins/map_platform_interface/lib/src/consts/constants.dart

@@ -0,0 +1,19 @@
+class Constants {
+  Constants._();
+
+  //地图渠道名称
+  static const String mapViewChannelName = "com.atmob.flutter_map/map_view_";
+
+  //基础功能
+  static const String initClient = 'initClient';
+  static const String getPlatformMapName = 'getPlatformMapName';
+
+  //地图相关方法
+  static const String methodMapMoveCamera = "map#moveCamera";
+  static const String methodMapAnimateCamera = "map#animateCamera";
+
+  //标记物相关方法
+  static const String methodUpdateMarkers = "marker#updateMarkers";
+  static const String methodDeleteMarkers = "marker#deleteMarkers";
+  static const String methodMarkerOnTap = "marker#onTap";
+}

+ 5 - 0
plugins/map_platform_interface/lib/src/consts/flutter_map_name_config.dart

@@ -0,0 +1,5 @@
+class FlutterMapNameConfig {
+  FlutterMapNameConfig._();
+
+  static const String mapMethodChannel = 'atmob_map_method_channel';
+}

+ 22 - 0
plugins/map_platform_interface/lib/src/consts/marker_type.dart

@@ -0,0 +1,22 @@
+import '../../map_interface.dart';
+
+enum MarkerType {
+  mine(1),
+  friend(2),
+  signalStartingPoint(3),
+  signalEndPoint(4),
+  trajectoryTargetPoint(5);
+
+  final int value;
+
+  const MarkerType(this.value);
+
+  static MarkerType fromValue(int value) {
+    return values.firstWhere(
+      (type) => type.value == value,
+      orElse: () => throw Exception('转换异常'),
+    );
+  }
+}
+
+typedef MarkerTapCallback = void Function(Marker marker);

+ 29 - 0
plugins/map_platform_interface/lib/src/entity/camera_position.dart

@@ -0,0 +1,29 @@
+class CameraPosition {
+  double? longitude;
+
+  double? latitude;
+
+  int zoom;
+
+  CameraPosition({
+    required this.longitude,
+    required this.latitude,
+    required this.zoom,
+  });
+
+  Map<String, dynamic> toJson() {
+    return {
+      'longitude': longitude,
+      'latitude': latitude,
+      'zoom': zoom,
+    };
+  }
+
+  factory CameraPosition.fromJson(Map<String, dynamic> map) {
+    return CameraPosition(
+      longitude: map['longitude'],
+      latitude: map['latitude'],
+      zoom: map['zoom'],
+    );
+  }
+}

+ 46 - 0
plugins/map_platform_interface/lib/src/entity/marker.dart

@@ -0,0 +1,46 @@
+import '../consts/marker_type.dart';
+
+class Marker {
+  String id;
+
+  String markerName;
+
+  double? longitude;
+
+  double? latitude;
+
+  MarkerType markerType;
+
+  bool isSelected;
+
+  Marker({
+    required this.id,
+    required this.markerName,
+    required this.longitude,
+    required this.latitude,
+    required this.markerType,
+    required this.isSelected,
+  });
+
+  Map<String, dynamic> toJson() {
+    return {
+      'id': id,
+      'markerName': markerName,
+      'longitude': longitude,
+      'latitude': latitude,
+      'markerType': markerType.value,
+      'isSelected': isSelected,
+    };
+  }
+
+  factory Marker.fromJson(Map<String, dynamic> map) {
+    return Marker(
+      id: map['id'],
+      markerName: map['markerName'],
+      longitude: map['longitude'],
+      latitude: map['latitude'],
+      markerType: MarkerType.fromValue(map['markerType'] as int),
+      isSelected: map['isSelected'],
+    );
+  }
+}

+ 12 - 0
plugins/map_platform_interface/lib/src/interface/map_overlays_interface.dart

@@ -0,0 +1,12 @@
+import '../entity/camera_position.dart';
+import '../entity/marker.dart';
+
+abstract class MapOverlaysInterface {
+  void addMarkers(List<Marker> markers);
+
+  void moveCamera(CameraPosition cameraPosition);
+
+  void animateCamera(CameraPosition cameraPosition);
+
+  void updateMarkers(List<Marker> markers);
+}

+ 0 - 0
plugins/map_platform_interface/lib/src/interface/map_platform_interface.dart


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.