Browse Source

[new]完善地图poi功能

zk 3 months ago
parent
commit
38d2a91be3

BIN
assets/images/icon_poi_location.webp


BIN
assets/images/icon_select_current_location.webp


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

@@ -447,4 +447,5 @@
     <string name="select_address_please_enter_place_name">请输入地方名称</string>
     <string name="select_address_please_enter_place_name">请输入地方名称</string>
     <string name="select_address_current_location">[当前位置]</string>
     <string name="select_address_current_location">[当前位置]</string>
     <string name="location_amap_co">合作单位:高德软件有限公司 审图号 GS(2021)6375号</string>
     <string name="location_amap_co">合作单位:高德软件有限公司 审图号 GS(2021)6375号</string>
+    <string name="current_location">[当前位置]</string>
 </resources>
 </resources>

+ 2 - 2
lib/di/get_it.config.dart

@@ -82,8 +82,6 @@ extension GetItInjectableX on _i174.GetIt {
     gh.factory<_i973.SplashController>(() => _i973.SplashController());
     gh.factory<_i973.SplashController>(() => _i973.SplashController());
     gh.factory<_i756.TrackDetailController>(
     gh.factory<_i756.TrackDetailController>(
         () => _i756.TrackDetailController());
         () => _i756.TrackDetailController());
-    gh.factory<_i440.CommonPointSelectAddressController>(
-        () => _i440.CommonPointSelectAddressController());
     gh.singleton<_i361.Dio>(() => networkModule.createDefaultDio());
     gh.singleton<_i361.Dio>(() => networkModule.createDefaultDio());
     gh.lazySingleton<_i772.InternetConnectionHelper>(
     gh.lazySingleton<_i772.InternetConnectionHelper>(
         () => _i772.InternetConnectionHelper());
         () => _i772.InternetConnectionHelper());
@@ -113,6 +111,8 @@ extension GetItInjectableX on _i174.GetIt {
         () => _i220.AtmobLocationClient(gh<_i772.InternetConnectionHelper>()));
         () => _i220.AtmobLocationClient(gh<_i772.InternetConnectionHelper>()));
     gh.factory<_i1008.LoginController>(
     gh.factory<_i1008.LoginController>(
         () => _i1008.LoginController(gh<_i20.AccountRepository>()));
         () => _i1008.LoginController(gh<_i20.AccountRepository>()));
+    gh.factory<_i440.CommonPointSelectAddressController>(() =>
+        _i440.CommonPointSelectAddressController(gh<_i20.AccountRepository>()));
     gh.factory<_i489.NewsController>(
     gh.factory<_i489.NewsController>(
         () => _i489.NewsController(gh<_i791.MessageRepository>()));
         () => _i489.NewsController(gh<_i791.MessageRepository>()));
     gh.lazySingleton<_i825.ConfigRepository>(() => _i825.ConfigRepository(
     gh.lazySingleton<_i825.ConfigRepository>(() => _i825.ConfigRepository(

+ 68 - 2
lib/module/commonpoint/select_address/common_point_select_address_controller.dart

@@ -5,13 +5,18 @@ import 'package:get/get.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:get/get_core/src/get_main.dart';
 import 'package:injectable/injectable.dart';
 import 'package:injectable/injectable.dart';
 import 'package:location/base/base_controller.dart';
 import 'package:location/base/base_controller.dart';
+import 'package:location/data/repositories/account_repository.dart';
+import 'package:location/handler/error_handler.dart';
 import 'package:location/sdk/map/map_helper.dart';
 import 'package:location/sdk/map/map_helper.dart';
+import 'package:location/utils/atmob_log.dart';
 import 'package:permission_handler/permission_handler.dart';
 import 'package:permission_handler/permission_handler.dart';
+import 'package:pull_to_refresh/pull_to_refresh.dart';
 import 'package:sliding_sheet2/sliding_sheet2.dart';
 import 'package:sliding_sheet2/sliding_sheet2.dart';
-
+import '../../../data/bean/user_info.dart';
 import '../../../dialog/common_confirm_dialog_impl.dart';
 import '../../../dialog/common_confirm_dialog_impl.dart';
 import '../../../dialog/location_permission_dialog.dart';
 import '../../../dialog/location_permission_dialog.dart';
 import '../../../resource/string.gen.dart';
 import '../../../resource/string.gen.dart';
+import '../../../utils/de_bounce.dart';
 import '../../../utils/permission_util.dart';
 import '../../../utils/permission_util.dart';
 import '../../../utils/toast_util.dart';
 import '../../../utils/toast_util.dart';
 
 
@@ -36,12 +41,29 @@ class CommonPointSelectAddressController extends BaseController {
 
 
   bool _isFirstMoveCamera = true;
   bool _isFirstMoveCamera = true;
 
 
+  UserInfo get mineUserInfo => accountRepository.mineUserInfo.value;
+
+  RxList<PoiItem> poiList = RxList();
+
+  final refreshController = RefreshController(initialRefresh: false);
+
+  final Debounce _searchDebounce = Debounce(debounceTime: 500);
+
+  final AccountRepository accountRepository;
+
+  CommonPointSelectAddressController(this.accountRepository);
+
   final MapPadding mapPadding =
   final MapPadding mapPadding =
       MapPadding(top: 80, left: 80, right: 80, bottom: 80);
       MapPadding(top: 80, left: 80, right: 80, bottom: 80);
 
 
   final CircleOptions circleOptions = CircleOptions(
   final CircleOptions circleOptions = CircleOptions(
       fillColor: '#4D7B7DFF', strokeWidth: 2.w, strokeColor: '#92AEE6');
       fillColor: '#4D7B7DFF', strokeWidth: 2.w, strokeColor: '#92AEE6');
 
 
+  final PoiQuery _poiQuery = PoiQuery();
+
+  final int _pageSize = 20;
+  int _pageNum = 1;
+
   @override
   @override
   void onReady() {
   void onReady() {
     super.onReady();
     super.onReady();
@@ -68,11 +90,48 @@ class CommonPointSelectAddressController extends BaseController {
             _isFirstMoveCamera = false;
             _isFirstMoveCamera = false;
             _moveCameraToBounds();
             _moveCameraToBounds();
           }
           }
+          //重新还原数据
+          _resetPoiQuery();
+          _poiSearch();
         }
         }
       },
       },
     );
     );
   }
   }
 
 
+  void _resetPoiQuery() {
+    _pageNum = 1;
+  }
+
+  void _poiSearch() async {
+    _searchDebounce.onClick(() {
+      AtmobLog.d('zk', '_poiSearch: $_poiQuery');
+      FlutterMap.paginatePoiSearch(
+              pageNum: _pageNum,
+              pageSize: _pageSize,
+              query: _poiQuery,
+              bound: PoiSearchBound.circle(
+                  center: LatLng(
+                      latitude: circleOptions.latitude,
+                      longitude: circleOptions.longitude),
+                  radius: circleOptions.radius?.toInt()))
+          .then((list) {
+        if (_pageNum == 1) {
+          poiList.clear();
+        }
+        if (list != null && list.isNotEmpty) {
+          poiList.addAll(list);
+          refreshController.loadComplete();
+        }
+        if (list == null || list.length < _pageSize) {
+          refreshController.loadNoData();
+        }
+      }).catchError((error) {
+        ErrorHandler.toastError(error);
+        refreshController.loadFailed();
+      });
+    });
+  }
+
   void _updateOrAddCircle() {
   void _updateOrAddCircle() {
     mapController.updateOrAddCircle('DC', circleOptions);
     mapController.updateOrAddCircle('DC', circleOptions);
   }
   }
@@ -106,7 +165,7 @@ class CommonPointSelectAddressController extends BaseController {
   setSheetProgress(double progress) {
   setSheetProgress(double progress) {
     _sheetProgress.value = progress;
     _sheetProgress.value = progress;
     if (progress == 1) {
     if (progress == 1) {
-      _requestFocus();
+      // _requestFocus();
     }
     }
   }
   }
 
 
@@ -193,4 +252,11 @@ class CommonPointSelectAddressController extends BaseController {
       });
       });
     }
     }
   }
   }
+
+  onPoiItemClick(PoiItem item) {}
+
+  void onLoadMorePoiData() {
+    _pageNum++;
+    _poiSearch();
+  }
 }
 }

+ 155 - 26
lib/module/commonpoint/select_address/common_point_select_address_page.dart

@@ -12,8 +12,10 @@ import 'package:location/resource/assets.gen.dart';
 import 'package:location/resource/colors.gen.dart';
 import 'package:location/resource/colors.gen.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/resource/string.gen.dart';
 import 'package:location/utils/common_expand.dart';
 import 'package:location/utils/common_expand.dart';
+import 'package:pull_to_refresh/pull_to_refresh.dart';
 import 'package:sliding_sheet2/sliding_sheet2.dart';
 import 'package:sliding_sheet2/sliding_sheet2.dart';
 import '../../../router/app_pages.dart';
 import '../../../router/app_pages.dart';
+import '../../../utils/common_util.dart';
 import '../../../widget/common_view.dart';
 import '../../../widget/common_view.dart';
 import 'common_point_select_address_controller.dart';
 import 'common_point_select_address_controller.dart';
 
 
@@ -111,27 +113,31 @@ class CommonPointSelectAddressPage
   }
   }
 
 
   Widget buildSlidingSheetView() {
   Widget buildSlidingSheetView() {
-    return SlidingSheet(
-        listener: (SheetState state) {
-          controller.setSheetProgress(state.progress);
-        },
-        controller: controller.sheetController,
-        color: ColorName.transparent,
-        elevation: 0,
-        shadowColor: ColorName.transparent,
-        cornerRadius: 0,
-        snapSpec: SnapSpec(
-          initialSnap: 0.45,
-          // Enable snapping. This is true by default.
-          snap: true,
-          // Set custom snapping points.
-          snappings: [0.45, 0.94],
-          // Define to what the snappings relate to. In this case,
-          // the total available space that the sheet can expand to.
-          positioning: SnapPositioning.relativeToAvailableSpace,
-        ),
-        headerBuilder: selectAddressHeaderBuilder,
-        customBuilder: selectAddressCustomBuilder);
+    return SizedBox(
+      width: double.infinity,
+      height: 1.sh,
+      child: SlidingSheet(
+          listener: (SheetState state) {
+            controller.setSheetProgress(state.progress);
+          },
+          controller: controller.sheetController,
+          color: ColorName.transparent,
+          elevation: 0,
+          shadowColor: ColorName.transparent,
+          cornerRadius: 0,
+          snapSpec: SnapSpec(
+            initialSnap: 0.45,
+            // Enable snapping. This is true by default.
+            snap: true,
+            // Set custom snapping points.
+            snappings: [0.45, 0.94],
+            // Define to what the snappings relate to. In this case,
+            // the total available space that the sheet can expand to.
+            positioning: SnapPositioning.relativeToAvailableSpace,
+          ),
+          headerBuilder: selectAddressHeaderBuilder,
+          customBuilder: selectAddressCustomBuilder),
+    );
   }
   }
 
 
   Widget buildToolbarView() {
   Widget buildToolbarView() {
@@ -177,11 +183,73 @@ class CommonPointSelectAddressPage
 
 
   Widget selectAddressCustomBuilder(BuildContext context,
   Widget selectAddressCustomBuilder(BuildContext context,
       ScrollController scrollController, SheetState state) {
       ScrollController scrollController, SheetState state) {
-    return ListView.builder(
-      padding: EdgeInsets.zero,
-      controller: scrollController,
-      itemBuilder: (ctx, index) => Text('selectAddressCustomBuilder--$index'),
-      itemCount: 100,
+    return Container(
+      color: Colors.white,
+      height: double.infinity,
+      child: SmartRefresher(
+        enablePullUp: true,
+        enablePullDown: false,
+        controller: controller.refreshController,
+        onLoading: controller.onLoadMorePoiData,
+        child: CustomScrollView(
+          controller: scrollController,
+          slivers: [
+            SliverToBoxAdapter(child: buildCurrentLocationView()),
+            Obx(() {
+              return SliverList.builder(
+                  itemBuilder: (ctx, index) =>
+                      buildPoiSearchItem(controller.poiList[index]),
+                  itemCount: controller.poiList.length);
+            })
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget buildCurrentLocationView() {
+    return GestureDetector(
+      onTap: controller.moveToCurrentLocation,
+      child: Container(
+        decoration: BoxDecoration(
+          color: '#F2F5FE'.color,
+          borderRadius: BorderRadius.circular(8.r),
+          border: Border.all(
+            color: '#D0D9F4'.color,
+            width: 0.5.w,
+          ),
+        ),
+        margin: EdgeInsets.only(left: 12.w, right: 12.w, bottom: 8.w, top: 4.w),
+        padding: EdgeInsets.symmetric(horizontal: 13.w, vertical: 8.w),
+        child: Row(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Container(
+                margin: EdgeInsets.only(top: 3.w),
+                child:
+                    Assets.images.iconSelectCurrentLocation.image(width: 10.w)),
+            SizedBox(width: 8.w),
+            Column(
+              crossAxisAlignment: CrossAxisAlignment.start,
+              children: [
+                Text(StringName.currentLocation,
+                    style: TextStyle(
+                        fontSize: 12.sp,
+                        color: ColorName.black90,
+                        fontWeight: FontWeight.bold)),
+                SizedBox(height: 4.w),
+                Obx(() {
+                  return Text(
+                      addressCheck(
+                          controller.mineUserInfo.lastLocation.value?.address),
+                      style:
+                          TextStyle(fontSize: 10.sp, color: ColorName.black50));
+                })
+              ],
+            )
+          ],
+        ),
+      ),
     );
     );
   }
   }
 
 
@@ -350,6 +418,67 @@ class CommonPointSelectAddressPage
           ],
           ],
         ));
         ));
   }
   }
+
+  Widget buildPoiSearchItem(PoiItem item) {
+    return GestureDetector(
+      onTap: controller.onPoiItemClick(item),
+      child: Container(
+        margin: EdgeInsets.only(left: 12.w, right: 12.w, bottom: 8.w, top: 4.w),
+        padding: EdgeInsets.only(left: 13.w, top: 8.w, bottom: 8.w),
+        child: Row(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Container(
+                margin: EdgeInsets.only(top: 3.w),
+                child: Assets.images.iconPoiLocation.image(width: 10.w)),
+            SizedBox(width: 8.w),
+            Expanded(
+              child: IntrinsicHeight(
+                child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    Row(
+                      children: [
+                        Expanded(
+                          child: Text(item.title ?? '',
+                              overflow: TextOverflow.ellipsis,
+                              style: TextStyle(
+                                  fontSize: 12.sp,
+                                  color: ColorName.black90,
+                                  fontWeight: FontWeight.bold)),
+                        ),
+                        SizedBox(width: 16.w),
+                        buildDistanceTextView(item)
+                      ],
+                    ),
+                    SizedBox(height: 4.w),
+                    Text(item.address ?? '',
+                        style: TextStyle(
+                            fontSize: 10.sp, color: ColorName.black50))
+                  ],
+                ),
+              ),
+            )
+          ],
+        ),
+      ),
+    );
+  }
+
+  Widget buildDistanceTextView(PoiItem item) {
+    final location = controller.mineUserInfo.lastLocation.value;
+    if (location == null ||
+        location.latitude == null ||
+        location.longitude == null ||
+        item.longitude == null ||
+        item.latitude == null) {
+      return SizedBox.shrink();
+    }
+    final distance = MapUtil.calculateLineDistance(location.longitude!,
+        location.latitude!, item.longitude!, item.latitude!);
+    return Text(MapUtil.format(distance),
+        style: TextStyle(fontSize: 10.sp, color: ColorName.black30));
+  }
 }
 }
 
 
 class CustomTrackShape extends RoundedRectSliderTrackShape {
 class CustomTrackShape extends RoundedRectSliderTrackShape {

+ 10 - 0
lib/resource/assets.gen.dart

@@ -603,6 +603,14 @@ class $AssetsImagesGen {
   AssetGenImage get iconNewsReportAgree =>
   AssetGenImage get iconNewsReportAgree =>
       const AssetGenImage('assets/images/icon_news_report_agree.webp');
       const AssetGenImage('assets/images/icon_news_report_agree.webp');
 
 
+  /// File path: assets/images/icon_poi_location.webp
+  AssetGenImage get iconPoiLocation =>
+      const AssetGenImage('assets/images/icon_poi_location.webp');
+
+  /// File path: assets/images/icon_select_current_location.webp
+  AssetGenImage get iconSelectCurrentLocation =>
+      const AssetGenImage('assets/images/icon_select_current_location.webp');
+
   /// File path: assets/images/icon_share_phone.webp
   /// File path: assets/images/icon_share_phone.webp
   AssetGenImage get iconSharePhone =>
   AssetGenImage get iconSharePhone =>
       const AssetGenImage('assets/images/icon_share_phone.webp');
       const AssetGenImage('assets/images/icon_share_phone.webp');
@@ -958,6 +966,8 @@ class $AssetsImagesGen {
         iconNewsItem,
         iconNewsItem,
         iconNewsReport,
         iconNewsReport,
         iconNewsReportAgree,
         iconNewsReportAgree,
+        iconPoiLocation,
+        iconSelectCurrentLocation,
         iconSharePhone,
         iconSharePhone,
         iconShareStay,
         iconShareStay,
         iconShareTrackDetail,
         iconShareTrackDetail,

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

@@ -393,6 +393,7 @@ class StringName {
       'select_address_current_location'.tr; // [当前位置]
       'select_address_current_location'.tr; // [当前位置]
   static String get locationAmapCo =>
   static String get locationAmapCo =>
       'location_amap_co'.tr; // 合作单位:高德软件有限公司 审图号 GS(2021)6375号
       'location_amap_co'.tr; // 合作单位:高德软件有限公司 审图号 GS(2021)6375号
+  static String get currentLocation => 'current_location'.tr; // [当前位置]
 }
 }
 class StringMultiSource {
 class StringMultiSource {
   StringMultiSource._();
   StringMultiSource._();
@@ -770,6 +771,7 @@ class StringMultiSource {
       'select_address_please_enter_place_name': '请输入地方名称',
       'select_address_please_enter_place_name': '请输入地方名称',
       'select_address_current_location': '[当前位置]',
       'select_address_current_location': '[当前位置]',
       'location_amap_co': '合作单位:高德软件有限公司 审图号 GS(2021)6375号',
       'location_amap_co': '合作单位:高德软件有限公司 审图号 GS(2021)6375号',
+      'current_location': '[当前位置]',
     },
     },
   };
   };
 }
 }

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

@@ -23,6 +23,9 @@ export 'package:flutter_map/src/entity/lat_lng.dart';
 export 'package:flutter_map/src/entity/map_padding.dart';
 export 'package:flutter_map/src/entity/map_padding.dart';
 export 'package:flutter_map/src/entity/popup.dart';
 export 'package:flutter_map/src/entity/popup.dart';
 export 'package:flutter_map/src/entity/polyline.dart';
 export 'package:flutter_map/src/entity/polyline.dart';
+export 'package:flutter_map/src/entity/poi_query.dart';
+export 'package:flutter_map/src/entity/poi_search_bound.dart';
+export 'package:flutter_map/src/entity/poi_item.dart';
 
 
 //工具类
 //工具类
 export 'package:flutter_map/src/utils/map_util.dart';
 export 'package:flutter_map/src/utils/map_util.dart';

+ 2 - 0
plugins/map/lib/src/consts/map_constants.dart

@@ -46,8 +46,10 @@ class MapConstants {
   static const String methodMarkerOnTap = "marker#onTap";
   static const String methodMarkerOnTap = "marker#onTap";
   static const String methodMarkerRemoveMarker = "marker#removeMarker";
   static const String methodMarkerRemoveMarker = "marker#removeMarker";
 
 
+  //////地图sdk相关功能//////
   //轨迹纠偏
   //轨迹纠偏
   static const String methodQueryProcessedTrace = "trace#queryProcessedTrace";
   static const String methodQueryProcessedTrace = "trace#queryProcessedTrace";
+  static const String methodPaginatePoiSearch = "poi#paginatePoiSearch";
 
 
   //polyline
   //polyline
   static const String methodAddPolyline = "polyline#addPolyline";
   static const String methodAddPolyline = "polyline#addPolyline";

+ 17 - 3
plugins/map/lib/src/core/flutter_map.dart

@@ -1,12 +1,16 @@
 import 'dart:async';
 import 'dart:async';
 import 'package:flutter/services.dart';
 import 'package:flutter/services.dart';
 import '../../flutter_map.dart';
 import '../../flutter_map.dart';
-import 'map_platform.dart';
+import '../entity/poi_item.dart';
+import '../entity/poi_query.dart';
+import '../entity/poi_search_bound.dart';
+import 'map_base_platform.dart';
+import 'map_platform_impl.dart';
 
 
 class FlutterMap {
 class FlutterMap {
   FlutterMap._();
   FlutterMap._();
 
 
-  static MapPlatform? _mapPlatform;
+  static MapBasePlatform? _mapPlatform;
   static Completer<void>? _initCompleter;
   static Completer<void>? _initCompleter;
 
 
   static Future<void> init() {
   static Future<void> init() {
@@ -17,7 +21,7 @@ class FlutterMap {
     _initCompleter = Completer<void>();
     _initCompleter = Completer<void>();
 
 
     const MethodChannel channel = MethodChannel(MapConstants.mapMethodChannel);
     const MethodChannel channel = MethodChannel(MapConstants.mapMethodChannel);
-    final MapPlatform mapPlatform = MapPlatformImpl(channel);
+    final MapBasePlatform mapPlatform = MapPlatformImpl(channel);
 
 
     mapPlatform.init().then((ok) {
     mapPlatform.init().then((ok) {
       if (!ok) throw Exception("地图初始化失败,未找到适配的地图,请检查是否添加地图库");
       if (!ok) throw Exception("地图初始化失败,未找到适配的地图,请检查是否添加地图库");
@@ -51,4 +55,14 @@ class FlutterMap {
     return _mapPlatform!
     return _mapPlatform!
         .queryProcessedTrace(lineID: lineID, locations: locations);
         .queryProcessedTrace(lineID: lineID, locations: locations);
   }
   }
+
+  static Future<List<PoiItem>?> paginatePoiSearch(
+      {required int pageNum,
+      required int pageSize,
+      required PoiQuery query,
+      PoiSearchBound? bound}) {
+    assert(_mapPlatform != null, "请先调用 FlutterMap.init()");
+    return _mapPlatform!.paginatePoiSearch(
+        pageNum: pageNum, pageSize: pageSize, query: query, bound: bound);
+  }
 }
 }

+ 5 - 0
plugins/map/lib/src/core/map_base_platform.dart

@@ -0,0 +1,5 @@
+import 'package:flutter_map/src/interface/function/map_function_interface.dart';
+import '../interface/map_sdk_interface.dart';
+
+abstract class MapBasePlatform
+    implements MapSDKInterface, MapFunctionInterface {}

+ 30 - 6
plugins/map/lib/src/core/map_platform.dart

@@ -1,13 +1,14 @@
-import 'dart:async';
 import 'dart:convert';
 import 'dart:convert';
+
 import 'package:flutter/services.dart';
 import 'package:flutter/services.dart';
-import 'package:flutter_map/src/interface/function/map_trace_interface.dart';
-import '../../flutter_map.dart';
-import '../interface/map_sdk_interface.dart';
+import 'package:flutter_map/src/entity/poi_query.dart';
+import 'package:flutter_map/src/entity/poi_search_bound.dart';
 
 
-abstract class MapPlatform implements MapSDKInterface, MapTraceInterface {}
+import '../../flutter_map.dart';
+import '../entity/poi_item.dart';
+import 'map_base_platform.dart';
 
 
-class MapPlatformImpl implements MapPlatform {
+class MapPlatformImpl implements MapBasePlatform {
   final MethodChannel _channel;
   final MethodChannel _channel;
 
 
   final EventChannel _eventChannel =
   final EventChannel _eventChannel =
@@ -62,4 +63,27 @@ class MapPlatformImpl implements MapPlatform {
       }
       }
     });
     });
   }
   }
+
+  @override
+  Future<List<PoiItem>?> paginatePoiSearch(
+      {required int pageNum,
+      required int pageSize,
+      required PoiQuery query,
+      PoiSearchBound? bound}) {
+    return _channel
+        .invokeMethod<List<dynamic>>(MapConstants.methodPaginatePoiSearch, {
+      "pageNum": pageNum,
+      "pageSize": pageSize,
+      "query": query.toJson(),
+      if (bound != null) "bound": bound.toJson(),
+    }).then((value) {
+      if (value == null || value.isEmpty) {
+        return null;
+      } else {
+        return value
+            .map((e) => PoiItem.fromJson(Map<String, dynamic>.from(e)))
+            .toList();
+      }
+    });
+  }
 }
 }

+ 29 - 0
plugins/map/lib/src/entity/poi_item.dart

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

+ 23 - 0
plugins/map/lib/src/entity/poi_query.dart

@@ -0,0 +1,23 @@
+class PoiQuery {
+  String? keyWord;
+
+  String? ctgr;
+
+  String? city;
+
+  PoiQuery({this.keyWord, this.ctgr, this.city});
+
+  Map<String, dynamic> toJson() {
+    return {"keyWord": keyWord, "ctgr": ctgr, "city": city};
+  }
+
+  factory PoiQuery.fromJson(Map<String, dynamic> json) {
+    return PoiQuery(
+        keyWord: json["keyWord"], ctgr: json["ctgr"], city: json["city"]);
+  }
+
+  @override
+  String toString() {
+    return '{keyWord: $keyWord, ctgr: $ctgr, city: $city}';
+  }
+}

+ 36 - 0
plugins/map/lib/src/entity/poi_search_bound.dart

@@ -0,0 +1,36 @@
+import '../../flutter_map.dart';
+
+class PoiSearchBound {
+  final LatLng? center; // 圆形范围中心点
+  final int? radius; // 半径(米)
+  final List<LatLng>? points; // 多边形点集
+
+  /// 圆形范围构造
+  PoiSearchBound.circle({
+    required this.center,
+    required this.radius,
+  }) : points = null;
+
+  /// 多边形范围构造
+  PoiSearchBound.polygon({
+    required this.points,
+  })  : center = null,
+        radius = null;
+
+  /// 转 Map,用于传给 MethodChannel
+  Map<String, dynamic> toJson() {
+    if (center != null && radius != null) {
+      return {
+        'type': 'circle',
+        'center': center!.toJson(),
+        'radius': radius,
+      };
+    } else if (points != null) {
+      return {
+        'type': 'polygon',
+        'points': points!.map((e) => e.toJson()).toList(),
+      };
+    }
+    throw Exception('Invalid PoiSearchBound: 必须是 circle 或 polygon');
+  }
+}

+ 12 - 1
plugins/map/lib/src/interface/function/map_trace_interface.dart

@@ -1,8 +1,19 @@
 import 'package:flutter_map/flutter_map.dart';
 import 'package:flutter_map/flutter_map.dart';
 
 
-abstract class MapTraceInterface {
+import '../../entity/poi_item.dart';
+import '../../entity/poi_query.dart';
+import '../../entity/poi_search_bound.dart';
+
+abstract class MapFunctionInterface {
   //lineID: 用于标示一条轨迹,支持多轨迹纠偏,如果多条轨迹调起纠偏接口,则lineID需不同。
   //lineID: 用于标示一条轨迹,支持多轨迹纠偏,如果多条轨迹调起纠偏接口,则lineID需不同。
   // locations : 一条轨迹的点集合。建议为一条行车GPS高精度定位轨迹。
   // locations : 一条轨迹的点集合。建议为一条行车GPS高精度定位轨迹。
   Future<List<LatLng>?> queryProcessedTrace(
   Future<List<LatLng>?> queryProcessedTrace(
       {required int lineID, required List<TraceLocation> locations});
       {required int lineID, required List<TraceLocation> locations});
+
+  //Poi搜索
+  Future<List<PoiItem>?> paginatePoiSearch(
+      {required int pageNum,
+      required int pageSize,
+      required PoiQuery query,
+      PoiSearchBound? bound});
 }
 }

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

@@ -9,6 +9,7 @@ import androidx.lifecycle.Lifecycle;
 import com.atmob.map_amap_android.contants.Constants;
 import com.atmob.map_amap_android.contants.Constants;
 import com.atmob.map_amap_android.event.FlutterLocationEventPlugin;
 import com.atmob.map_amap_android.event.FlutterLocationEventPlugin;
 import com.atmob.map_amap_android.lifecycle.FlutterLifecycleAdapter;
 import com.atmob.map_amap_android.lifecycle.FlutterLifecycleAdapter;
+import com.atmob.map_amap_android.poi.PoiHelper;
 import com.atmob.map_amap_android.trace.TraceClientHelper;
 import com.atmob.map_amap_android.trace.TraceClientHelper;
 import com.atmob.map_amap_android.util.AMapHelper;
 import com.atmob.map_amap_android.util.AMapHelper;
 import com.atmob.map_amap_android.util.LogUtil;
 import com.atmob.map_amap_android.util.LogUtil;
@@ -66,6 +67,8 @@ public class MapAmapAndroidPlugin implements FlutterPlugin, MethodCallHandler, A
                 result.success(true);
                 result.success(true);
             } else if (Objects.equals(method, Constants.METHOD_QUERY_PROCESSED_TRACE)) {
             } else if (Objects.equals(method, Constants.METHOD_QUERY_PROCESSED_TRACE)) {
                 TraceClientHelper.queryProcessedTrace(applicationContext, call, result);
                 TraceClientHelper.queryProcessedTrace(applicationContext, call, result);
+            } else if (Objects.equals(method, Constants.METHOD_POI_PAGINATE_SEARCH)) {
+                PoiHelper.paginatePoiSearch(applicationContext, call, result);
             } else {
             } else {
                 result.notImplemented();
                 result.notImplemented();
             }
             }

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

@@ -27,10 +27,17 @@ public class Constants {
     public static final String METHOD_START_LOCATION = "startLocation";//开启定位
     public static final String METHOD_START_LOCATION = "startLocation";//开启定位
 
 
 
 
+
+    ///////////////////SDK 相关的功能//////////////
     /**
     /**
      * 轨迹
      * 轨迹
      */
      */
     public static final String METHOD_QUERY_PROCESSED_TRACE = "trace#queryProcessedTrace";
     public static final String METHOD_QUERY_PROCESSED_TRACE = "trace#queryProcessedTrace";
+    /**
+     * POI检索功能
+     */
+    public static final String METHOD_POI_PAGINATE_SEARCH = "poi#paginatePoiSearch";
+
 
 
 
 
     /***********************************************地图操作相关********************************/
     /***********************************************地图操作相关********************************/

+ 109 - 0
plugins/map_amap_android/android/src/main/java/com/atmob/map_amap_android/poi/PoiHelper.java

@@ -0,0 +1,109 @@
+package com.atmob.map_amap_android.poi;
+
+import android.content.Context;
+
+import com.amap.api.services.core.LatLonPoint;
+import com.amap.api.services.core.PoiItemV2;
+import com.amap.api.services.poisearch.PoiResultV2;
+import com.amap.api.services.poisearch.PoiSearchV2;
+import com.atmob.map_amap_android.util.LogUtil;
+import com.atmob.map_amap_android.util.ParamUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+public class PoiHelper {
+
+    static final String TAG = "PoiHelper";
+
+    public static void paginatePoiSearch(Context context, MethodCall call, MethodChannel.Result result) {
+        try {
+            Map<String, Object> arguments = call.arguments();
+            Map<String, Object> queryMap = (Map<String, Object>) arguments.get("query");
+            if (queryMap == null || queryMap.isEmpty()) {
+                result.error("query is null", "query is null", null);
+                return;
+            }
+            Integer pageSize = ParamUtil.getInt(arguments, "pageSize");
+            Integer pageNum = ParamUtil.getInt(arguments, "pageNum");
+            if (pageNum == null || pageSize == null) {
+                result.error("pageNum or pageSize is null", "pageNum or pageSize is null", null);
+                return;
+            }
+
+            String keyWord = ParamUtil.getString(queryMap, "keyWord");
+            String ctgr = ParamUtil.getString(queryMap, "ctgr");
+            String city = ParamUtil.getString(queryMap, "city");
+
+
+            PoiSearchV2.Query query = new PoiSearchV2.Query(keyWord, ctgr, city);
+            query.setPageSize(pageSize);
+            query.setPageNum(pageNum);
+            PoiSearchV2 poiSearch = new PoiSearchV2(context, query);
+            Map<String, Object> bound = (Map<String, Object>) arguments.get("bound");
+            if (bound != null && !bound.isEmpty()) {
+                String type = ParamUtil.getString(bound, "type");
+                //以中心点半径方式检索
+                if (Objects.equals(type, "circle")) {
+                    int radius = ParamUtil.getInt(bound, "radius");
+                    Map<String, Object> center = (Map<String, Object>) bound.get("center");
+                    if (center == null || center.isEmpty()) {
+                        result.error("center is null", "center is null", null);
+                        return;
+                    }
+                    double latitude = ParamUtil.getDouble(center, "latitude", 0);
+                    double longitude = ParamUtil.getDouble(center, "longitude", 0);
+                    LogUtil.d(TAG, "paginatePoiSearch: latitude=" + latitude + " longitude=" + longitude + " radius=" + radius);
+                    final PoiSearchV2.SearchBound searchBound = new PoiSearchV2.SearchBound(new LatLonPoint(latitude, longitude), radius);
+                    poiSearch.setBound(searchBound);
+                }
+            }
+            poiSearch.setOnPoiSearchListener(new PoiSearchV2.OnPoiSearchListener() {
+                @Override
+                public void onPoiSearched(PoiResultV2 poiResultV2, int errorCode) {
+                    //返回POI搜索异步处理的结果。
+                    LogUtil.d(TAG, "onPoiSearched: errorCode=" + errorCode + " poiResultV2=" + poiResultV2);
+                    if (errorCode == 1000) {
+                        //成功
+                        ArrayList<PoiItemV2> pois = poiResultV2.getPois();
+                        //返回list
+                        ArrayList<Map<String, Object>> list = new ArrayList<>();
+                        for (PoiItemV2 poiItemV2 : pois) {
+                            Map<String, Object> resultMap = new HashMap<>();
+                            resultMap.put("title", poiItemV2.getTitle());
+                            String address = poiItemV2.getProvinceName() + poiItemV2.getCityName() + poiItemV2.getAdName() + poiItemV2.getSnippet();
+                            resultMap.put("address", address);
+                            resultMap.put("latitude", poiItemV2.getLatLonPoint().getLatitude());
+                            resultMap.put("longitude", poiItemV2.getLatLonPoint().getLongitude());
+                            list.add(resultMap);
+                            LogUtil.d(TAG, "onPoiSearched: resultMap=" + resultMap);
+                        }
+                        try {
+                            result.success(list);
+                        } catch (Exception e) {
+                            LogUtil.e(TAG, "onPoiSearched error", e);
+                            result.error("onPoiSearched error", e.getMessage(), null);
+                        }
+                    } else {
+                        result.error("onPoiSearched error", "errorCode=" + errorCode, null);
+                    }
+                }
+
+                @Override
+                public void onPoiItemSearched(PoiItemV2 poiItemV2, int onPoiItemSearched) {
+                    //poi id搜索的结果回调
+                    LogUtil.d(TAG, "onPoiItemSearched: onPoiItemSearched=" + onPoiItemSearched + " poiItemV2=" + poiItemV2);
+                }
+            });
+            poiSearch.searchPOIAsyn();
+        } catch (Exception e) {
+            result.error("paginatePoiSearch error", e.getMessage(), null);
+        }
+    }
+
+}