diff --git a/ln_jq_app/android/app/build.gradle.kts b/ln_jq_app/android/app/build.gradle.kts
index 2539bdf..24d95b7 100644
--- a/ln_jq_app/android/app/build.gradle.kts
+++ b/ln_jq_app/android/app/build.gradle.kts
@@ -68,3 +68,8 @@ android {
flutter {
source = "../.."
}
+
+dependencies {
+ implementation("com.amap.api:navi-3dmap-location-search:10.0.700_3dmap10.0.700_loc6.4.5_sea9.7.2")
+ implementation("com.squareup.okhttp3:okhttp:4.12.0")
+}
diff --git a/ln_jq_app/android/app/src/main/AndroidManifest.xml b/ln_jq_app/android/app/src/main/AndroidManifest.xml
index 5392736..7a6ef64 100644
--- a/ln_jq_app/android/app/src/main/AndroidManifest.xml
+++ b/ln_jq_app/android/app/src/main/AndroidManifest.xml
@@ -22,6 +22,14 @@
android:label="小羚羚"
android:name="${applicationName}"
android:icon="@mipmap/logo">
+
+
+
+
+
+
{
+ switch (call.method) {
+ case "requestPermissions":
+ requestPermissions();
+ result.success(null);
+ break;
+ case "onResume":
+ if (mapView != null) {
+ mapView.onResume();
+ }
+ result.success(null);
+ break;
+ case "onPause":
+ if (mapView != null) {
+ mapView.onPause();
+ }
+ result.success(null);
+ break;
+ case "onDestroy":
+ if (mapView != null) {
+ mapView.dispose();
+ mapView = null;
+ }
+ result.success(null);
+ break;
+ default:
+ result.notImplemented();
+ break;
+ }
+ });
+ }
+
+ /**
+ * 获取当前系统版本需要申请的权限列表
+ */
+ private String[] getRequiredPermissions() {
+ List permissions = new ArrayList<>();
+ // 定位权限是必须的
+ permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
+ permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ // 存储权限处理:Android 13 (API 33) 以下才需要申请 legacy 存储权限
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ }
+
+ return permissions.toArray(new String[0]);
+ }
+
+ /**
+ * 检查并申请权限
+ */
+ private void checkAndRequestPermissions() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ String[] requiredPermissions = getRequiredPermissions();
+ List deniedPermissions = new ArrayList<>();
+
+ for (String permission : requiredPermissions) {
+ if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
+ deniedPermissions.add(permission);
+ }
+ }
+
+ if (!deniedPermissions.isEmpty()) {
+ ActivityCompat.requestPermissions(
+ this,
+ deniedPermissions.toArray(new String[0]),
+ PERMISSION_REQUEST_CODE
+ );
+ } else {
+ Log.d(TAG, "所有必要权限已授予");
+ if (mapView != null) {
+ mapView.startLocation();
+ }
+ }
+ } else {
+ if (mapView != null) {
+ mapView.startLocation();
+ }
+ }
+ }
+
+ private void requestPermissions() {
+ checkAndRequestPermissions();
+ }
+
+ public void setMapView(NativeMapView mapView) {
+ this.mapView = mapView;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // 注意:高德SDK合规检查通过后再进行定位相关操作
+ // 这里仅保留地图生命周期调用,权限建议在Flutter端或按需触发
+ if (mapView != null) {
+ mapView.onResume();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mapView != null) {
+ mapView.onPause();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mapView != null) {
+ mapView.dispose();
+ mapView = null;
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (mapView != null) {
+ mapView.onSaveInstanceState(outState);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+
+ if (requestCode == PERMISSION_REQUEST_CODE) {
+ boolean locationGranted = false;
+ for (int i = 0; i < permissions.length; i++) {
+ if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i])
+ && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
+ locationGranted = true;
+ break;
+ }
+ }
+
+ if (locationGranted) {
+ if (mapView != null) {
+ mapView.startLocation();
+ }
+ } else {
+ // 只有在定位权限确实被拒绝时才弹出提示
+ Toast.makeText(this, "请授予应用定位权限以正常使用地图功能", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
}
diff --git a/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapFactory.java b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapFactory.java
new file mode 100644
index 0000000..9e8ff31
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapFactory.java
@@ -0,0 +1,37 @@
+package com.lnkj.ln_jq_app;
+
+import android.content.Context;
+
+import io.flutter.plugin.common.MessageCodec;
+import io.flutter.plugin.common.StandardMessageCodec;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewFactory;
+
+/**
+ * 高德地图导航 Platform View Factory
+ * 对应iOS的NativeViewFactory
+ */
+public class NativeMapFactory extends PlatformViewFactory {
+
+ private static final String VIEW_TYPE_ID = "NativeFirstPage";
+ private static NativeMapView mapViewInstance = null;
+ private final Context context;
+
+ public NativeMapFactory(Context context) {
+ super(StandardMessageCodec.INSTANCE);
+ this.context = context;
+ }
+
+ @Override
+ public PlatformView create(Context context, int viewId, Object args) {
+ mapViewInstance = new NativeMapView(context, viewId, args);
+ return mapViewInstance;
+ }
+
+ /**
+ * 获取地图实例,供MainActivity使用
+ */
+ public static NativeMapView getMapView() {
+ return mapViewInstance;
+ }
+}
diff --git a/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapView.java b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapView.java
new file mode 100644
index 0000000..63cece6
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapView.java
@@ -0,0 +1,143 @@
+package com.lnkj.ln_jq_app;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import com.amap.api.location.AMapLocation;
+import com.amap.api.location.AMapLocationClient;
+import com.amap.api.location.AMapLocationClientOption;
+import com.amap.api.location.AMapLocationListener;
+import com.amap.api.maps.AMap;
+import com.amap.api.maps.LocationSource;
+import com.amap.api.maps.MapView;
+import com.amap.api.maps.MapsInitializer;
+import com.amap.api.maps.model.MyLocationStyle;
+
+import io.flutter.plugin.platform.PlatformView;
+
+public class NativeMapView implements PlatformView, LocationSource, AMapLocationListener {
+ private static final String TAG = "NativeMapView";
+ private final MapView mapView;
+ private final AMap aMap;
+ private OnLocationChangedListener mListener;
+ private AMapLocationClient mlocationClient;
+ private final Context mContext;
+
+ public NativeMapView(Context context, int id, Object args) {
+ this.mContext = context;
+
+ // 隐私合规接口,必须在创建MapView之前设置
+ MapsInitializer.updatePrivacyShow(context, true, true);
+ MapsInitializer.updatePrivacyAgree(context, true);
+
+ mapView = new MapView(context);
+ // 在 Flutter PlatformView 中,savedInstanceState 通常为 null
+ mapView.onCreate(null);
+ aMap = mapView.getMap();
+
+ // 设置地图的 UI 选项
+ setupMapUi();
+
+ // 注册到 MainActivity 以便同步生命周期
+ if (context instanceof MainActivity) {
+ ((MainActivity) context).setMapView(this);
+ }
+ }
+
+ private void setupMapUi() {
+ aMap.setLocationSource(this);
+ aMap.setMyLocationEnabled(true);
+
+ MyLocationStyle myLocationStyle = new MyLocationStyle();
+ myLocationStyle.interval(2000);
+ // 连续定位并将视角移动到地图中心点,定位蓝点跟随设备移动
+ myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE);
+ myLocationStyle.showMyLocation(true);
+ myLocationStyle.strokeColor(Color.TRANSPARENT);
+ myLocationStyle.radiusFillColor(Color.TRANSPARENT);
+ aMap.setMyLocationStyle(myLocationStyle);
+
+ // 显示缩放按钮
+ aMap.getUiSettings().setZoomControlsEnabled(true);
+ }
+
+ @Override
+ public View getView() {
+ return mapView;
+ }
+
+ @Override
+ public void activate(OnLocationChangedListener listener) {
+ mListener = listener;
+ startLocation();
+ }
+
+ @Override
+ public void deactivate() {
+ mListener = null;
+ if (mlocationClient != null) {
+ mlocationClient.stopLocation();
+ mlocationClient.onDestroy();
+ }
+ mlocationClient = null;
+ }
+
+ public void startLocation() {
+ if (mlocationClient == null) {
+ try {
+ AMapLocationClient.updatePrivacyShow(mContext, true, true);
+ AMapLocationClient.updatePrivacyAgree(mContext, true);
+ mlocationClient = new AMapLocationClient(mContext);
+ AMapLocationClientOption mLocationOption = new AMapLocationClientOption();
+ mlocationClient.setLocationListener(this);
+ mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
+ mlocationClient.setLocationOption(mLocationOption);
+ mlocationClient.startLocation();
+ } catch (Exception e) {
+ Log.e(TAG, "startLocation error", e);
+ }
+ }
+ }
+
+ @Override
+ public void onLocationChanged(AMapLocation amapLocation) {
+ if (mListener != null && amapLocation != null) {
+ if (amapLocation.getErrorCode() == 0) {
+ mListener.onLocationChanged(amapLocation);
+ // 首次定位成功后缩放地图
+ // aMap.moveCamera(CameraUpdateFactory.zoomTo(15));
+ } else {
+ Log.e(TAG, "定位失败," + amapLocation.getErrorCode() + ": " + amapLocation.getErrorInfo());
+ }
+ }
+ }
+
+ public void onResume() {
+ if (mapView != null) {
+ mapView.onResume();
+ }
+ }
+
+ public void onPause() {
+ if (mapView != null) {
+ mapView.onPause();
+ }
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ if (mapView != null) {
+ mapView.onSaveInstanceState(outState);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ if (mapView != null) {
+ mapView.onDestroy();
+ }
+ deactivate();
+ }
+}
diff --git a/ln_jq_app/android/app/src/main/res/drawable/amap_loc.xml b/ln_jq_app/android/app/src/main/res/drawable/amap_loc.xml
new file mode 100644
index 0000000..a58e47e
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/res/drawable/amap_loc.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/ln_jq_app/android/app/src/main/res/drawable/ic_end_marker.xml b/ln_jq_app/android/app/src/main/res/drawable/ic_end_marker.xml
new file mode 100644
index 0000000..58bb442
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/res/drawable/ic_end_marker.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/ln_jq_app/android/app/src/main/res/drawable/ic_location.xml b/ln_jq_app/android/app/src/main/res/drawable/ic_location.xml
new file mode 100644
index 0000000..4e99139
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/res/drawable/ic_location.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/ln_jq_app/android/app/src/main/res/drawable/ic_route.xml b/ln_jq_app/android/app/src/main/res/drawable/ic_route.xml
new file mode 100644
index 0000000..e16e2fe
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/res/drawable/ic_route.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/ln_jq_app/android/app/src/main/res/drawable/ic_start_marker.xml b/ln_jq_app/android/app/src/main/res/drawable/ic_start_marker.xml
new file mode 100644
index 0000000..0af42cf
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/res/drawable/ic_start_marker.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/ln_jq_app/android/app/src/main/res/drawable/ic_station_marker.xml b/ln_jq_app/android/app/src/main/res/drawable/ic_station_marker.xml
new file mode 100644
index 0000000..ef73731
--- /dev/null
+++ b/ln_jq_app/android/app/src/main/res/drawable/ic_station_marker.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/ln_jq_app/lib/pages/c_page/base_widgets/NativePageIOS.dart b/ln_jq_app/lib/pages/c_page/base_widgets/NativePageIOS.dart
index a21b0cc..150ab11 100644
--- a/ln_jq_app/lib/pages/c_page/base_widgets/NativePageIOS.dart
+++ b/ln_jq_app/lib/pages/c_page/base_widgets/NativePageIOS.dart
@@ -1,58 +1,82 @@
+import 'dart:io';
+
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
-import 'dart:io';
-
import 'package:flutter/services.dart';
+/// 原生地图页面
class NativePageIOS extends StatelessWidget {
- const NativePageIOS({super.key});
+ const NativePageIOS({super.key});
- @override
- Widget build(BuildContext context) {
- if (Platform.isIOS) {
- return Container(
- // onTap: () => _handleTap(context),
- width: MediaQuery.of(context).size.width,
- height: MediaQuery.of(context).size.width - 100,
- // padding: EdgeInsetsGeometry.all(15),
- color: Colors.red[100],
-
- child: UiKitView(
- viewType: 'NativeFirstPage', // 与原生端注册的标识一致
- gestureRecognizers: >{}.toSet(),
- hitTestBehavior: PlatformViewHitTestBehavior.opaque,
- creationParamsCodec: const StandardMessageCodec(),
- layoutDirection: TextDirection.ltr,
- )
- );
- } else {
- return const Center(child: Text('not ios'));
- }
- }
-
-
- void _handleTap(BuildContext context) {
- print("页面被点击");
- _showDialog(context, 'Tip', '点击了');
- }
-
-
- void _showDialog(BuildContext context, String title, String content) {
- showDialog(
- context: context,
- builder: (context) => AlertDialog(
- title: Text(title),
- content: Text(content),
- actions: [
- TextButton(
- onPressed: () => Navigator.pop(context),
- child: const Text('确定'),
- ),
- ],
- ),
+ @override
+ Widget build(BuildContext context) {
+ if (Platform.isIOS) {
+ return _buildIOSView(context);
+ } else if (Platform.isAndroid) {
+ return _buildAndroidView(context);
+ } else {
+ return const Center(
+ child: Text('不支持的平台', style: TextStyle(fontSize: 16)),
);
}
+ }
+ /// 构建iOS Platform View
+ Widget _buildIOSView(BuildContext context) {
+ return Container(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height - 100,
+ color: Colors.white,
+ child: UiKitView(
+ viewType: 'NativeFirstPage', // 与iOS原生端注册的标识一致
+ gestureRecognizers: >{}.toSet(),
+ hitTestBehavior: PlatformViewHitTestBehavior.opaque,
+ creationParamsCodec: const StandardMessageCodec(),
+ layoutDirection: TextDirection.ltr,
+ ),
+ );
+ }
+
+ /// 构建Android Platform View
+ Widget _buildAndroidView(BuildContext context) {
+ return Container(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height - 100,
+ color: Colors.white,
+ child: AndroidView(
+ viewType: 'NativeFirstPage', // 与Android原生端注册的标识一致
+ gestureRecognizers: >{}.toSet(),
+ hitTestBehavior: PlatformViewHitTestBehavior.opaque,
+ creationParamsCodec: const StandardMessageCodec(),
+ layoutDirection: TextDirection.ltr,
+ ),
+ );
+ }
+
+ /// 处理点击事件(如需要)
+ void _handleTap(BuildContext context) {
+ if (kDebugMode) {
+ print("NativePage被点击");
+ }
+ _showDialog(context, '提示', '点击了原生地图页面');
+ }
+
+ /// 显示对话框
+ void _showDialog(BuildContext context, String title, String content) {
+ showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ title: Text(title),
+ content: Text(content),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context),
+ child: const Text('确定'),
+ ),
+ ],
+ ),
+ );
+ }
}
\ No newline at end of file