瀏覽代碼

[new]增加我的定位显示

zk 1 年之前
父節點
當前提交
a966b3b1d1
共有 29 個文件被更改,包括 457 次插入137 次删除
  1. 1 1
      android/build.gradle
  2. 13 2
      lib/data/bean/location_info.dart
  3. 1 1
      lib/data/bean/location_info.g.dart
  4. 15 6
      lib/data/bean/user_info.dart
  5. 0 4
      lib/data/bean/user_info.g.dart
  6. 2 0
      lib/data/consts/constants.dart
  7. 11 2
      lib/data/repositories/account_repository.dart
  8. 50 22
      lib/module/main/main_controller.dart
  9. 5 4
      lib/module/main/main_friend_item.dart
  10. 3 3
      lib/module/main/main_page.dart
  11. 34 9
      lib/sdk/map/map_helper.dart
  12. 25 0
      lib/utils/base_expand.dart
  13. 22 0
      lib/utils/location_convert_marker_util.dart
  14. 3 0
      lib/utils/location_permission_util.dart
  15. 13 8
      plugins/map/lib/src/consts/map_constants.dart
  16. 1 1
      plugins/map/lib/src/core/flutter_map.dart
  17. 13 36
      plugins/map/lib/src/core/map_platform.dart
  18. 11 1
      plugins/map/lib/src/entity/map_location.dart
  19. 4 0
      plugins/map/lib/src/interface/map_overlays_interface.dart
  20. 4 3
      plugins/map/lib/src/interface/map_platform_interface.dart
  21. 12 1
      plugins/map/lib/src/widget/map_controller.dart
  22. 1 1
      plugins/map/lib/src/widget/map_widget.dart
  23. 93 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/FlutterLocationEventPlugin.java
  24. 2 1
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/FlutterViewPlugin.java
  25. 22 9
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/MapAmapAndroidPlugin.java
  26. 12 2
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java
  27. 61 11
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/AMapHelper.java
  28. 23 0
      plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/PermissionUtil.java
  29. 0 9
      pubspec.lock

+ 1 - 1
android/build.gradle

@@ -1,7 +1,7 @@
 allprojects {
     ext {
         compileSdkVersion = 34
-        applicationId = "com.manbu.shouji.android"
+        applicationId = "com.manbu.shouji"
         minSdkVersion = 23
         targetSdkVersion = 34
     }

+ 13 - 2
lib/data/bean/location_info.dart

@@ -1,11 +1,13 @@
+import 'package:flutter_map/src/entity/map_location.dart';
 import 'package:json_annotation/json_annotation.dart';
+import 'package:location/data/consts/constants.dart';
 
 part 'location_info.g.dart';
 
 @JsonSerializable()
 class LocationInfo {
   @JsonKey(name: 'userId')
-  final String userId;
+  final String? userId;
 
   @JsonKey(name: 'lng')
   final double? longitude;
@@ -20,7 +22,7 @@ class LocationInfo {
   final int? lastUpdateTime;
 
   LocationInfo({
-    required this.userId,
+    this.userId,
     this.longitude,
     this.latitude,
     this.address,
@@ -31,4 +33,13 @@ class LocationInfo {
       _$LocationInfoFromJson(json);
 
   Map<String, dynamic> toJson() => _$LocationInfoToJson(this);
+
+  static LocationInfo fromMapLocation(MapLocation location) {
+    return LocationInfo(
+      longitude: location.longitude,
+      latitude: location.latitude,
+      address: location.address,
+      lastUpdateTime: location.time,
+    );
+  }
 }

+ 1 - 1
lib/data/bean/location_info.g.dart

@@ -7,7 +7,7 @@ part of 'location_info.dart';
 // **************************************************************************
 
 LocationInfo _$LocationInfoFromJson(Map<String, dynamic> json) => LocationInfo(
-      userId: json['userId'] as String,
+      userId: json['userId'] as String?,
       longitude: (json['lng'] as num?)?.toDouble(),
       latitude: (json['lat'] as num?)?.toDouble(),
       address: json['addr'] as String?,

+ 15 - 6
lib/data/bean/user_info.dart

@@ -1,3 +1,4 @@
+import 'package:get/get.dart';
 import 'package:json_annotation/json_annotation.dart';
 
 import 'location_info.dart';
@@ -24,8 +25,8 @@ class UserInfo {
   @JsonKey(name: 'blockedMe')
   final bool? blockedMe;
 
-  @JsonKey(name: 'location')
-  final LocationInfo? lastLocation;
+  @JsonKey(name: 'location', ignore: true)
+  final Rxn<LocationInfo> lastLocation = Rxn<LocationInfo>();
 
   @JsonKey(name: 'virtual')
   final bool? virtual;
@@ -39,17 +40,25 @@ class UserInfo {
     this.timestamp,
     this.blockedHim,
     this.blockedMe,
-    this.lastLocation,
     this.virtual,
     this.isMine,
   });
 
-  factory UserInfo.fromJson(Map<String, dynamic> json) =>
-      _$UserInfoFromJson(json);
+  factory UserInfo.fromJson(Map<String, dynamic> json) {
+    final userinfo = _$UserInfoFromJson(json);
 
-  Map<String, dynamic> toJson() => _$UserInfoToJson(this);
+    LocationInfo? locationInfo = json['location'] == null
+        ? null
+        : LocationInfo.fromJson(json['location'] as Map<String, dynamic>);
+    userinfo.lastLocation.value = locationInfo;
+    return userinfo;
+  }
 
   String getUserNickName() {
     return remark ?? phoneNumber;
   }
+
+  void updateLocation(LocationInfo location) {
+    lastLocation.value = location;
+  }
 }

+ 0 - 4
lib/data/bean/user_info.g.dart

@@ -13,9 +13,6 @@ UserInfo _$UserInfoFromJson(Map<String, dynamic> json) => UserInfo(
       timestamp: (json['timestamp'] as num?)?.toInt(),
       blockedHim: json['blockedHim'] as bool?,
       blockedMe: json['blockedMe'] as bool?,
-      lastLocation: json['location'] == null
-          ? null
-          : LocationInfo.fromJson(json['location'] as Map<String, dynamic>),
       virtual: json['virtual'] as bool?,
       isMine: json['isMine'] as bool?,
     );
@@ -27,7 +24,6 @@ Map<String, dynamic> _$UserInfoToJson(UserInfo instance) => <String, dynamic>{
       'timestamp': instance.timestamp,
       'blockedHim': instance.blockedHim,
       'blockedMe': instance.blockedMe,
-      'location': instance.lastLocation,
       'virtual': instance.virtual,
       'isMine': instance.isMine,
     };

+ 2 - 0
lib/data/consts/constants.dart

@@ -30,6 +30,8 @@ class Constants {
   static const String appChanelName = "app_channel_name";
   static const String appChannelId = "app_channel_id";
   static const String appTgPlatformId = "app_tg_platform_id";
+
+  static const String mineLocationId = '-1';
 }
 
 String getBaseUrl() {

+ 11 - 2
lib/data/repositories/account_repository.dart

@@ -6,8 +6,10 @@ import 'package:location/base/app_base_request.dart';
 import 'package:location/data/api/atmob_api.dart';
 import 'package:location/data/api/request/login_request.dart';
 import 'package:location/data/api/request/send_code_request.dart';
+import 'package:location/data/bean/location_info.dart';
 import 'package:location/data/bean/member_status_info.dart';
 import 'package:location/data/bean/user_info.dart';
+import 'package:location/data/consts/constants.dart';
 import 'package:location/data/consts/error_code.dart';
 import 'package:location/data/repositories/friends_repository.dart';
 import 'package:location/di/get_it.dart';
@@ -16,6 +18,7 @@ import 'package:location/utils/async_util.dart';
 import 'package:location/utils/atmob_log.dart';
 import 'package:location/utils/http_handler.dart';
 import 'package:location/utils/mmkv_util.dart';
+import '../../sdk/map/map_helper.dart';
 import '../api/response/login_response.dart';
 
 @lazySingleton
@@ -40,8 +43,10 @@ class AccountRepository {
 
   late final FriendsRepository friendsRepository;
 
-  final Rx<UserInfo> locationUserInfo = Rx<UserInfo>(
-      UserInfo(id: "-1", phoneNumber: StringName.locationMine, isMine: true));
+  final Rx<UserInfo> mineUserInfo = Rx<UserInfo>(UserInfo(
+      id: Constants.mineLocationId,
+      phoneNumber: StringName.locationMine,
+      isMine: true));
 
   AccountRepository(this.atmobApi) {
     AtmobLog.d(tag, '$tag....init');
@@ -56,6 +61,10 @@ class AccountRepository {
     friendsRepository = FriendsRepository.getInstance();
 
     refreshMemberStatus();
+    MapHelper.addLocationListener((location) {
+      mineUserInfo.value.lastLocation.value =
+          LocationInfo.fromMapLocation(location);
+    });
   }
 
   Future<void> loginSendCode(String phoneNum) {

+ 50 - 22
lib/module/main/main_controller.dart

@@ -1,15 +1,22 @@
+import 'dart:async';
+
 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/consts/constants.dart';
 import 'package:location/data/repositories/account_repository.dart';
 import 'package:location/data/repositories/friends_repository.dart';
 import 'package:flutter_map/flutter_map.dart';
+import 'package:location/sdk/map/map_helper.dart';
+import 'package:location/utils/atmob_log.dart';
+import 'package:location/utils/base_expand.dart';
 import '../../dialog/add_friend_dialog.dart';
 import '../../dialog/check_loation_permission_dialog.dart';
 import '../../dialog/location_permission_dialog.dart';
+import '../../utils/location_convert_marker_util.dart';
 import '../../utils/location_permission_util.dart';
 import '../add_friend/add_friend_page.dart';
 import '../mine/mine_page.dart';
@@ -21,7 +28,7 @@ class MainController extends BaseController {
 
   RxList<UserInfo> get friendsList => friendsRepository.friendsList;
 
-  UserInfo get mineLocation => accountRepository.locationUserInfo.value;
+  UserInfo get mineUserInfo => accountRepository.mineUserInfo.value;
 
   MainController(this.friendsRepository, this.accountRepository);
 
@@ -31,11 +38,16 @@ class MainController extends BaseController {
 
   final MapController mapController = MapController();
 
+  StreamSubscription? mineLocationSubscription;
+
+  bool isFirstShowMineLocation = true;
+
   @override
   void onReady() {
     super.onReady();
     friendsList.listen((list) {
-      mapController.addMarkers(_convertToMarker(list));
+      mapController.addMarkers(
+          Location2MarkerUtil.userInfoList2MarkerList(list, selectedFriend));
     });
     AddFriendDialog.show(
         onAddClick: () async {
@@ -43,6 +55,16 @@ class MainController extends BaseController {
           checkLocationPermissionCallback();
         },
         onNotAddDismiss: checkLocationPermissionCallback);
+    mineLocationSubscription =
+        accountRepository.mineUserInfo.value.lastLocation.listen((location) {
+      final mineInfo = accountRepository.mineUserInfo.value;
+      mapController.updateMarker(Location2MarkerUtil.userInfo2Marker(
+          mineInfo, selectedFriend == mineInfo));
+      if (isFirstShowMineLocation && location != null) {
+        isFirstShowMineLocation = false;
+        animateCameraToUser(mineInfo);
+      }
+    });
   }
 
   Future<void> onAddFriendClick() {
@@ -67,7 +89,7 @@ class MainController extends BaseController {
     //修改地图选中
     _updateMapSelected(oldInfo, mineLocation);
     //移动到选中的位置
-    animateCamera(mineLocation);
+    animateCameraToUser(mineLocation);
   }
 
   void _updateMapSelected(UserInfo? oldInfo, UserInfo newInfo) {
@@ -76,37 +98,34 @@ class MainController extends BaseController {
       markers.add(oldInfo);
     }
     markers.add(newInfo);
-    mapController.updateMarkers(_convertToMarker(markers));
+    mapController.updateMarkers(
+        Location2MarkerUtil.userInfoList2MarkerList(markers, selectedFriend));
   }
 
-  void animateCamera(UserInfo userInfo) {
-    LocationInfo? locationInfo = userInfo.lastLocation;
+  void animateCameraToUser(UserInfo userInfo) {
+    LocationInfo? locationInfo = userInfo.lastLocation.value;
     if (locationInfo == null ||
+        locationInfo.longitude == null ||
+        locationInfo.latitude == null ||
         locationInfo.longitude == 0 ||
         locationInfo.latitude == 0) {
       return;
     }
-    mapController.animateCamera(CameraPosition(
-        longitude: locationInfo.longitude,
-        latitude: locationInfo.latitude,
-        zoom: 18));
+    animateCamera(
+        latitude: locationInfo.latitude, longitude: locationInfo.longitude);
   }
 
-  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 animateCamera({required double? latitude, required double? longitude}) {
+    mapController.animateCamera(
+        CameraPosition(longitude: longitude, latitude: latitude, zoom: 18));
   }
 
   void onMarkerTap(Marker marker) {
     String id = marker.id;
+    if (id == Constants.mineLocationId) {
+      onSelectClick(mineUserInfo);
+      return;
+    }
     UserInfo? userInfo =
         friendsList.firstWhereOrNull((element) => element.id == id);
     if (userInfo == null) {
@@ -145,5 +164,14 @@ class MainController extends BaseController {
     }
   }
 
-  void _updateCurrentLocation() {}
+  void _updateCurrentLocation() {
+    MapHelper.getLastLocation()?.let((it) {
+      animateCamera(latitude: it.latitude, longitude: it.longitude);
+    });
+  }
+
+  @override
+  void onClose() {
+    mineLocationSubscription?.cancel();
+  }
 }

+ 5 - 4
lib/module/main/main_friend_item.dart

@@ -6,8 +6,6 @@ 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/utils/project_expand.dart';
-import 'package:marquee/marquee.dart';
 import '../../utils/common_style.dart';
 import '../../utils/common_util.dart';
 import '../../widget/marquee_text.dart';
@@ -105,7 +103,9 @@ Widget mainSelectedFriendItem(UserInfo userInfo) {
                             color: '#202020'.color),
                       ),
                       SizedBox(width: 7.w),
-                      Text(time2TimeDesc(userInfo.timestamp),
+                      Text(
+                          time2TimeDesc(
+                              userInfo.lastLocation.value?.lastUpdateTime),
                           style: TextStyle(
                               fontSize: 12.sp, color: '#A7A7A7'.color)),
                       Spacer(),
@@ -123,7 +123,8 @@ Widget mainSelectedFriendItem(UserInfo userInfo) {
                     child: Container(
                       margin: EdgeInsets.only(right: 17.w),
                       child: MarqueeText.marquee(
-                          text: addressCheck(userInfo.lastLocation?.address),
+                          text: addressCheck(
+                              userInfo.lastLocation.value?.address),
                           textStyle: TextStyle(
                               fontSize: 13.sp, color: ColorName.black50),
                           containerWidth: 244.w),

+ 3 - 3
lib/module/main/main_page.dart

@@ -225,10 +225,10 @@ class MainPage extends BasePage<MainController> {
           scrollDirection: Axis.horizontal,
           children: [
             Obx(() {
-              return mainFriendItem(controller.mineLocation,
-                  controller.selectedFriend?.id == controller.mineLocation.id,
+              return mainFriendItem(controller.mineUserInfo,
+                  controller.selectedFriend?.id == controller.mineUserInfo.id,
                   onTap: () {
-                controller.onSelectClick(controller.mineLocation);
+                controller.onSelectClick(controller.mineUserInfo);
               });
             }),
             for (UserInfo userInfo in controller.friendsList)

+ 34 - 9
lib/sdk/map/map_helper.dart

@@ -2,25 +2,50 @@ import 'package:flutter_map/flutter_map.dart';
 import '../../utils/atmob_log.dart';
 
 class MapHelper {
-  static const String tag = "MapHelper";
+  MapHelper._();
+
+  static const String tag = "FlutterMapHelper";
 
   static MapLocation? _lastLocation;
   static late MapPlatform _mapPlatform;
 
+  static final List<MapLocationListener> _locationListeners = [];
+
   static Future<void> init() async {
     await FlutterMap.init();
     _mapPlatform = FlutterMap.getMapPlatform();
     initLocationClient();
   }
 
-  static initLocationClient() {
-    _mapPlatform.setLocationListener(
-        interval: 5000,
-        listener: (location) {
-          AtmobLog.d(tag, "onLocationChanged: $location");
-          if (location.errorCode == 0) {
-            _lastLocation = location;
+  static void initLocationClient() {
+    _mapPlatform.receiveLocationStream(listener: (location) {
+      AtmobLog.d(tag, "onLocationChanged: $location");
+      if (location.errorCode == 0) {
+        _lastLocation = location;
+        try {
+          for (MapLocationListener listener in _locationListeners) {
+            listener.call(location);
           }
-        });
+        } catch (e) {
+          AtmobLog.e(tag, "onLocationChanged error: $e");
+        }
+      }
+    });
+  }
+
+  static Future<void> startLocation() {
+    return _mapPlatform.startLocation();
+  }
+
+  static MapLocation? getLastLocation() {
+    return _lastLocation;
+  }
+
+  static void addLocationListener(MapLocationListener listener) {
+    _locationListeners.add(listener);
+  }
+
+  static void removeLocationListener(MapLocationListener listener) {
+    _locationListeners.remove(listener);
   }
 }

+ 25 - 0
lib/utils/base_expand.dart

@@ -0,0 +1,25 @@
+extension LetExtension<T> on T {
+  /// 类似 Kotlin 的 let 函数,允许对任意对象执行代码块
+  R let<R>(R Function(T it) block) {
+    return block(this);
+  }
+}
+
+extension ApplyExtension<T> on T {
+  /// 类似 Kotlin 的 apply 函数,允许对对象执行配置操作,并返回自身
+  T apply(void Function(T it) block) {
+    block(this);
+    return this;
+  }
+}
+
+extension AlsoExtension<T> on T {
+  T also(void Function(T it) block) {
+    block(this);
+    return this;
+  }
+}
+
+extension RunExtension<T> on T {
+  R run<R>(R Function(T it) block) => block(this);
+}

+ 22 - 0
lib/utils/location_convert_marker_util.dart

@@ -0,0 +1,22 @@
+import 'package:flutter_map/flutter_map.dart';
+import 'package:location/data/bean/user_info.dart';
+
+class Location2MarkerUtil {
+  Location2MarkerUtil._();
+
+  static Marker userInfo2Marker(UserInfo e, bool isSelected) {
+    return Marker(
+      id: e.id,
+      markerName: e.remark ?? e.phoneNumber,
+      longitude: e.lastLocation.value?.longitude,
+      latitude: e.lastLocation.value?.latitude,
+      markerType: MarkerType.friend,
+      isSelected: isSelected,
+    );
+  }
+
+  static List<Marker> userInfoList2MarkerList(
+      List<UserInfo> list, UserInfo? selectedFriend) {
+    return list.map((e) => userInfo2Marker(e, e == selectedFriend)).toList();
+  }
+}

+ 3 - 0
lib/utils/location_permission_util.dart

@@ -1,3 +1,4 @@
+import 'package:location/sdk/map/map_helper.dart';
 import 'package:permission_handler/permission_handler.dart';
 
 class LocationPermissionUtil {
@@ -6,6 +7,7 @@ class LocationPermissionUtil {
   static Future<bool> requestLocationPermission() async {
     final status = await Permission.locationWhenInUse.request();
     if (status.isGranted) {
+      MapHelper.startLocation();
       return true;
     } else {
       return false;
@@ -25,6 +27,7 @@ class LocationPermissionUtil {
   static Future<bool> requestShowLocationAlways() async {
     final status = await Permission.locationAlways.request();
     if (status.isGranted) {
+      MapHelper.startLocation();
       return true;
     } else {
       return false;

+ 13 - 8
plugins/map/lib/src/consts/map_constants.dart

@@ -1,25 +1,30 @@
 class MapConstants {
   MapConstants._();
 
-  //地图调用方法通道
-  static const String mapMethodChannel = 'atmob_map_method_channel';
+  //定位持续流通道名称
+  static const String mapLocationEventChannel =
+      'atmob_map_location_event_channel';
 
-  //地图定位持续监听通道
-  static const String mapEventChannel = 'atmob_map_event_channel';
+  //地图方法通道名称
+  static const String mapMethodChannel = 'atmob_map_method_channel';
 
   //地图渠道名称
   static const String mapViewChannelName = "com.atmob.flutter_map/map_view_";
 
+  //地图widget_viewType名称
+  static const String mapWidgetViewType = "com.atmob.flutter.map";
+
   /// *********************************************************************************************************************
-  //基础方法
-  static const String initClient = 'initClient';
+  //基础方法名称
+  static const String init = 'init';
   static const String getPlatformMapName = 'getPlatformMapName';
+  static const String startLocation = "startLocation";
 
-  //地图相关方法
+  //地图相关方法名称
   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";

+ 1 - 1
plugins/map/lib/src/core/flutter_map.dart

@@ -13,7 +13,7 @@ class FlutterMap {
     MethodChannel channel = const MethodChannel(MapConstants.mapMethodChannel);
 
     MapPlatform mapPlatform = MapPlatform(channel);
-    if (!await mapPlatform.initClient()) {
+    if (!await mapPlatform.init()) {
       throw Exception("地图初始化失败,未找到适配的地图,请检查是否添加的地图库");
     }
     _mapPlatform = mapPlatform;

+ 13 - 36
plugins/map/lib/src/core/map_platform.dart

@@ -6,10 +6,7 @@ class MapPlatform extends MapInterface {
   final MethodChannel _channel;
 
   final EventChannel _eventChannel =
-      const EventChannel(MapConstants.mapEventChannel);
-
-  MapLocationListener? _currentListener;
-  StreamSubscription<dynamic>? _locationSubscription;
+      const EventChannel(MapConstants.mapLocationEventChannel);
 
   MapPlatform(this._channel);
 
@@ -19,10 +16,9 @@ class MapPlatform extends MapInterface {
   }
 
   @override
-  Future<bool> initClient() async {
+  Future<bool> init() async {
     try {
-      bool? isSupport =
-          await _channel.invokeMethod<bool>(MapConstants.initClient);
+      bool? isSupport = await _channel.invokeMethod<bool>(MapConstants.init);
       if (isSupport == true) {
         return true;
       }
@@ -31,37 +27,18 @@ class MapPlatform extends MapInterface {
   }
 
   @override
-  Stream<dynamic> setLocationListener(
-      {required int interval, required MapLocationListener listener}) {
-    return _eventChannel.receiveBroadcastStream(<String, dynamic>{
-      'interval': interval,
+  void receiveLocationStream({required MapLocationListener listener}) {
+    _eventChannel.receiveBroadcastStream().map((event) {
+      // 显式转换为 Map<String, dynamic>
+      return (event as Map).cast<String, dynamic>();
+    }).listen((data) {
+      MapLocation location = MapLocation.fromJson(data);
+      listener.call(location);
     });
   }
 
-  StreamSubscription registerLocationStream({required int interval}) {
-    return _eventChannel.receiveBroadcastStream(<String, dynamic>{
-      'interval': interval,
-    }).listen((data) {});
-  }
-
-  void _handleLocationUpdate(dynamic event) {
-    try {
-      // final data = Map<String, dynamic>.from(event as FlutterMap);
-      // final location = MapLocationData(
-      //   latitude: data['latitude'] as double,
-      //   longitude: data['longitude'] as double,
-      //   accuracy: data['accuracy']?.toDouble(),
-      //   timestamp: DateTime.fromMillisecondsSinceEpoch(
-      //       data['timestamp'] as int),
-      // );
-      // _currentListener?.call(location);
-    } catch (e) {
-      // _currentListener?.call(null, "Data format error: $e");
-    }
-  }
-
-  void _handleLocationError(Object error) {
-    if (error is PlatformException) {
-    } else {}
+  @override
+  Future<void> startLocation() {
+    return _channel.invokeMethod<void>(MapConstants.startLocation);
   }
 }

+ 11 - 1
plugins/map/lib/src/entity/map_location.dart

@@ -9,10 +9,16 @@ class MapLocation {
 
   final int? errorCode;
 
+  final double? speed;
+
+  final double? bearing;
+
   MapLocation({
     this.time,
     this.address,
     this.errorCode,
+    this.speed,
+    this.bearing,
     required this.latitude,
     required this.longitude,
   });
@@ -24,6 +30,8 @@ class MapLocation {
       'latitude': latitude,
       'longitude': longitude,
       'errorCode': errorCode,
+      'speed': speed,
+      'bearing': bearing,
     };
   }
 
@@ -34,11 +42,13 @@ class MapLocation {
       latitude: map['latitude'],
       longitude: map['longitude'],
       errorCode: map['errorCode'],
+      speed: map['speed'],
+      bearing: map['bearing'],
     );
   }
 
   @override
   String toString() {
-    return 'MapLocation{time: $time, address: $address, latitude: $latitude, longitude: $longitude, errorCode: $errorCode}';
+    return 'MapLocation{time: $time, address: $address, latitude: $latitude, longitude: $longitude, errorCode: $errorCode, speed: $speed, bearing: $bearing}';
   }
 }

+ 4 - 0
plugins/map/lib/src/interface/map_overlays_interface.dart

@@ -2,6 +2,8 @@ import '../entity/camera_position.dart';
 import '../entity/marker.dart';
 
 abstract class MapOverlaysInterface {
+  void addMarker(Marker marker);
+
   void addMarkers(List<Marker> markers);
 
   void moveCamera(CameraPosition cameraPosition);
@@ -9,4 +11,6 @@ abstract class MapOverlaysInterface {
   void animateCamera(CameraPosition cameraPosition);
 
   void updateMarkers(List<Marker> markers);
+
+  void updateMarker(Marker marker);
 }

+ 4 - 3
plugins/map/lib/src/interface/map_platform_interface.dart

@@ -5,9 +5,10 @@ typedef MapLocationListener = void Function(MapLocation location);
 abstract class MapInterface {
   Future<String?> getPlatformName();
 
-  Future<bool> initClient();
+  Future<bool> init();
 
   ///interval 单位毫秒
-  void setLocationListener(
-      {required int interval, required MapLocationListener listener});
+  void receiveLocationStream({required MapLocationListener listener});
+
+  Future<void> startLocation();
 }

+ 12 - 1
plugins/map/lib/src/widget/map_controller.dart

@@ -28,7 +28,6 @@ class MapController extends MapOverlaysInterface {
   }
 
   void _executeMethod(Map<String, dynamic> op) {
-    debugPrint("_executeMethod...");
     _channel?.invokeMethod(op['method'], op['args']).catchError((e) {
       debugPrint('Method ${op['method']} failed: $e');
     });
@@ -87,4 +86,16 @@ class MapController extends MapOverlaysInterface {
       _pendingOperations.add(params);
     }
   }
+
+  @override
+  void addMarker(Marker marker) {
+    if (_isDisposed) return;
+    updateMarkers([marker]);
+  }
+
+  @override
+  void updateMarker(Marker marker) {
+    if (_isDisposed) return;
+    updateMarkers([marker]);
+  }
 }

+ 1 - 1
plugins/map/lib/src/widget/map_widget.dart

@@ -23,7 +23,7 @@ class MapWidget extends StatefulWidget {
 }
 
 class _MapWidgetState extends State<MapWidget> {
-  final String mapViewType = 'com.atmob.flutter.map';
+  final String mapViewType = MapConstants.mapWidgetViewType;
 
   @override
   Widget build(BuildContext context) {

+ 93 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/FlutterLocationEventPlugin.java

@@ -0,0 +1,93 @@
+package com.atmob.map_amap_android;
+
+import androidx.annotation.NonNull;
+
+import com.amap.api.location.AMapLocation;
+import com.atmob.map_amap_android.contants.Constants;
+import com.atmob.map_amap_android.util.AMapHelper;
+import com.atmob.map_amap_android.util.LogUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+import io.flutter.plugin.common.EventChannel;
+
+public class FlutterLocationEventPlugin implements FlutterPlugin, EventChannel.StreamHandler, AMapHelper.OnLocationListener {
+
+    private static final String TAG = "FlutterLocationEventPlugin";
+
+    private EventChannel eventChannel;
+    private EventChannel.EventSink eventSink;
+    private Map<String, Object> locationMap;
+
+    @Override
+    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+        eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), Constants.MAP_LOCATION_EVENT_CHANNEL);
+        eventChannel.setStreamHandler(this);
+        AMapHelper.addOnLocationListener(this);
+    }
+
+    @Override
+    public void onDetachedFromEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+        if (eventChannel != null) {
+            eventChannel.setStreamHandler(null);
+        }
+        AMapHelper.removeOnLocationListener(this);
+        eventChannel = null;
+        eventSink = null;
+        locationMap.clear();
+        locationMap = null;
+    }
+
+    @Override
+    public void onListen(Object o, EventChannel.EventSink eventSink) {
+        this.eventSink = eventSink;
+    }
+
+    @Override
+    public void onCancel(Object o) {
+        eventSink = null;
+        locationMap.clear();
+        locationMap = null;
+    }
+
+    @Override
+    public void onLocationChanged(@NonNull AMapLocation aMapLocation) {
+        senLocation(aMapLocation);
+    }
+
+
+    private void senLocation(AMapLocation mapLocation) {
+        try {
+            if (eventSink != null) {
+                eventSink.success(location2Map(mapLocation));
+                LogUtil.d(TAG, "senLocation success: " + mapLocation);
+            }
+        } catch (Exception e) {
+            LogUtil.d(TAG, "senLocation error: " + e.getMessage());
+            sendError("-1", e.getMessage());
+        }
+    }
+
+    private void sendError(String errorCode, String errorMessage) {
+        if (eventSink != null) {
+            eventSink.error(errorCode, errorMessage, null);
+        }
+    }
+
+    private Map<String, Object> location2Map(AMapLocation location) {
+        if (locationMap == null) {
+            locationMap = new HashMap<>(7);
+        }
+        locationMap.clear();
+        locationMap.put("errorCode", location.getErrorCode());
+        locationMap.put("latitude", location.getLatitude());
+        locationMap.put("longitude", location.getLongitude());
+        locationMap.put("address", location.getAddress());
+        locationMap.put("time", location.getTime());
+        locationMap.put("speed", location.getSpeed());
+        locationMap.put("bearing", location.getBearing());
+        return locationMap;
+    }
+}

+ 2 - 1
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/FlutterViewPlugin.java

@@ -6,6 +6,7 @@ import android.app.Activity;
 import androidx.lifecycle.LifecycleOwner;
 
 import com.atmob.map_amap_android.amap.MapViewFactory;
+import com.atmob.map_amap_android.contants.Constants;
 import com.google.gson.Gson;
 
 import io.flutter.embedding.engine.plugins.FlutterPlugin;
@@ -14,6 +15,6 @@ 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));
+        flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(Constants.MAP_WIDGET_VIEW_TYPE, new MapViewFactory(mActivity, flutterPluginBinding.getBinaryMessenger(), lifecycleProvider));
     }
 }

+ 22 - 9
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/MapAmapAndroidPlugin.java

@@ -12,6 +12,8 @@ import com.atmob.map_amap_android.util.AMapHelper;
 import com.atmob.map_amap_android.util.LogUtil;
 import com.google.gson.Gson;
 
+import java.util.Objects;
+
 import io.flutter.BuildConfig;
 import io.flutter.embedding.engine.plugins.FlutterPlugin;
 import io.flutter.embedding.engine.plugins.activity.ActivityAware;
@@ -29,6 +31,7 @@ public class MapAmapAndroidPlugin implements FlutterPlugin, MethodCallHandler, A
     ///
     /// 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 final String TAG = "MapAmapAndroidPlugin";
     private MethodChannel channel;
     private Activity mActivity;
     private Context applicationContext;
@@ -38,22 +41,32 @@ public class MapAmapAndroidPlugin implements FlutterPlugin, MethodCallHandler, A
     @Override
     public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
         LogUtil.setLogEnabled(BuildConfig.DEBUG);
-        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "atmob_map_method_channel");
+        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), Constants.MAP_METHOD_CHANNEL);
         applicationContext = flutterPluginBinding.getApplicationContext();
         channel.setMethodCallHandler(this);
         this.flutterPluginBinding = flutterPluginBinding;
-
+        FlutterLocationEventPlugin flutterLocationEventPlugin = new FlutterLocationEventPlugin();
+        flutterLocationEventPlugin.onAttachedToEngine(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();
+        try {
+            String method = call.method;
+            LogUtil.d(TAG, "onMethodCall: " + method + " " + call.arguments);
+            if (Objects.equals(method, Constants.METHOD_INIT)) {
+                AMapHelper.init(applicationContext);
+                result.success(true);
+            } else if (Objects.equals(method, Constants.METHOD_GET_PLATFORM_MAP_NAME)) {
+                result.success(Constants.MAP_NAME);
+            } else if (Objects.equals(method, Constants.METHOD_START_LOCATION)) {
+                AMapHelper.startLocation(applicationContext);
+                result.success(true);
+            } else {
+                result.notImplemented();
+            }
+        } catch (Exception e) {
+            result.error("error", e.getMessage(), null);
         }
     }
 

+ 12 - 2
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/contants/Constants.java

@@ -2,16 +2,26 @@ package com.atmob.map_amap_android.contants;
 
 public class Constants {
 
+    public static final String MAP_NAME = "amap map";
+
+    //定位持续流通道名称
+    public static final String MAP_LOCATION_EVENT_CHANNEL = "atmob_map_location_event_channel";
+
+    //地图方法通道名称
+    public static final String MAP_METHOD_CHANNEL = "atmob_map_method_channel";
 
     /*******渠道名称********/
     public static final String MAP_VIEW_CHANNEL_NAME = "com.atmob.flutter_map/map_view_";
 
 
+    //地图widget_viewType
+    public static final String MAP_WIDGET_VIEW_TYPE = "com.atmob.flutter.map";
+
     /*******方法名称********/
     /*******基础功能********/
-    public static final String METHOD_INIT_CLIENT = "initClient";
+    public static final String METHOD_INIT = "init";
     public static final String METHOD_GET_PLATFORM_MAP_NAME = "getPlatformMapName";
-    public static final String MAP_NAME = "amap map";
+    public static final String METHOD_START_LOCATION = "startLocation";//开启定位
 
 
     /*******地图操作相关********/

+ 61 - 11
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/AMapHelper.java

@@ -1,28 +1,41 @@
 package com.atmob.map_amap_android.util;
 
+import android.Manifest;
 import android.annotation.SuppressLint;
 import android.app.Application;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
-import android.util.Log;
+import android.content.Intent;
+import android.os.Build;
 
 import androidx.annotation.NonNull;
+
 import com.amap.api.location.AMapLocation;
 import com.amap.api.location.AMapLocationClient;
 import com.amap.api.location.AMapLocationClientOption;
+import com.atmob.map_amap_android.R;
+
 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";
+    public static final String KEY_LAST_TRACK_ID = "amap_last_track_id";
+    private static final int AMAP_NOTIFICATION_ID = 350;
+    private static final String AMAP_NOTIFICATION_CHANNEL_ID = "amap_notification_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;
+    private static final String[] LOCATION_PERMISSION = new String[]{
+            Manifest.permission.ACCESS_FINE_LOCATION,
+            Manifest.permission.ACCESS_COARSE_LOCATION
+    };
 
     public static void init(Context context) {
         if (ProcessUtil.isMainProcess(context)) {
@@ -33,7 +46,7 @@ public class AMapHelper {
     private static void initLocation(Context context) {
         if (isLocationInit.compareAndSet(false, true)) {
             try {
-                Log.d(TAG, "amap ... initLocation...");
+                LogUtil.d(TAG, "amap ... initLocation...");
                 AMapLocationClient.updatePrivacyShow(context, true, true);
                 AMapLocationClient.updatePrivacyAgree(context, true);
                 aMapLocationClient = new AMapLocationClient(context);
@@ -42,25 +55,62 @@ public class AMapHelper {
                     if (aMapLocation != null) {
                         if (aMapLocation.getErrorCode() == 0) {
                             lastLocation = aMapLocation;
-                            Log.d(TAG, "onLocationChanged: " + aMapLocation);
+                            LogUtil.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));
-//                }
+                if (PermissionUtil.hasPermission(context, LOCATION_PERMISSION)) {
+                    aMapLocationClient.startLocation();
+//                    aMapLocationClient.enableBackgroundLocation(AMAP_NOTIFICATION_ID, createLocationNotification(context));
+                }
             } catch (Exception e) {
                 isLocationInit.set(false);
-                Log.e(TAG, "initLocation failed.", e);
+                LogUtil.e(TAG, "initLocation failed.", e);
             }
         }
     }
 
+    public static void startLocation(Context context) {
+        if (isLocationInit.get()) {
+            aMapLocationClient.startLocation();
+//            aMapLocationClient.enableBackgroundLocation(AMAP_NOTIFICATION_ID, createLocationNotification(context));
+        } else {
+            initLocation(context);
+        }
+    }
+
 
+//    private static Notification createLocationNotification(Application application) {
+//        NotificationManager notificationManager = (NotificationManager) application.getSystemService(Application.NOTIFICATION_SERVICE);
+//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+//            NotificationChannel notificationChannel = new NotificationChannel(AMAP_NOTIFICATION_CHANNEL_ID,
+//                    AMAP_NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
+//            notificationManager.createNotificationChannel(notificationChannel);
+//        }
+//
+//        Intent splashIntent = new Intent(application, .class);
+//        splashIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+//
+//        PendingIntent contentIntent = PendingIntent.getActivity(
+//                application,
+//                12344,
+//                splashIntent,
+//                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
+//        );
+//
+//        return new NotificationCompat.Builder(application, AMAP_NOTIFICATION_CHANNEL_ID)
+//                .setContentTitle(application.getString(R.string.location_notification_title))
+//                .setContentText(application.getString(R.string.location_notification_content))
+//                .setSmallIcon(R.drawable.icon_location_notification_small)
+//                .setContentIntent(contentIntent)
+//                .setOngoing(true)
+//                .setSilent(true)
+//                .setAutoCancel(false)
+//                .build();
+//    }
 
     public static AMapLocation getLastLocation() {
         return lastLocation;

+ 23 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/util/PermissionUtil.java

@@ -0,0 +1,23 @@
+package com.atmob.map_amap_android.util;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class PermissionUtil {
+
+    private PermissionUtil() {
+        throw new RuntimeException("cannot be INSTANTIATED.");
+    }
+
+    public static boolean hasPermission(Context context,String... permissions) {
+        PackageManager packageManager = context.getPackageManager();
+        for (String permission : permissions) {
+            boolean hasPermission = packageManager.checkPermission(permission, context.getPackageName())
+                    == PackageManager.PERMISSION_GRANTED;
+            if (!hasPermission) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 0 - 9
pubspec.lock

@@ -640,15 +640,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.0"
-  oaid:
-    dependency: "direct main"
-    description:
-      path: "."
-      ref: "v0.0.1"
-      resolved-ref: "22c60c77cdbc3aa91277d83a711c659b74d24227"
-      url: "http://git.atmob.com:28999/Atmob-Flutter/Oaid.git"
-    source: git
-    version: "0.0.1"
   package_config:
     dependency: transitive
     description: