种地 发表于 2025-3-12 10:24:39

【Flutter】高德舆图接入Android/IOS(定位&舆图)

定位

Android

参考链接

官方文档
创建应用

https://i-blog.csdnimg.cn/blog_migrate/75e5a705641c286271e4b5383cadfb90.png
https://i-blog.csdnimg.cn/blog_migrate/c54822e39cdb822fd74cb8407bea18ae.png
创建Key

https://i-blog.csdnimg.cn/blog_migrate/4f4de866f1011363819cbd86f150c983.png
输入Key的名称
服务平台选择Android平台
接下里获取SHA1
https://i-blog.csdnimg.cn/blog_migrate/38e7424b5dbde02ce35c1ff130e2bfd8.png
PackageName输入项目的包名
https://i-blog.csdnimg.cn/blog_migrate/4a7e34bc24233efaaa0706896b6e6098.png
打开Android目次,并生成签名文件
https://i-blog.csdnimg.cn/blog_migrate/b093391a628b64f03f2a03dd3b9081f7.png
假如存在签名文件,如下图
https://i-blog.csdnimg.cn/blog_migrate/609c22adf468b0236e2c568c65110b8d.png
若没有签名文件,则需要Create New 创建新的签名文件
https://i-blog.csdnimg.cn/blog_migrate/40865c2766091d1a534094bb9d86205c.png
填写密码,别名、文件位置等信息
https://i-blog.csdnimg.cn/blog_migrate/69e74c99cf759b2e8e7c02e1f324759d.png
创建完后,会发现目次下有签名文件,可以移到app目次下
https://i-blog.csdnimg.cn/blog_migrate/ec0be84facbddf670b8c9c5671717cbd.png
配置签名文件
https://i-blog.csdnimg.cn/blog_migrate/125d86c35b64386bd65fe6c1c01004c5.png
    signingConfigs {
      release {
            //keystore中key的别名
            keyAlias 'key0'
            //keystore中key的密码
            keyPassword '123456'
            //keystore的文件路径,可以是绝对路径也可以是相对路径
            storeFile file('./deman_mobo.jks')
            //keystore的密码l
            storePassword '123456'
      }
    }

    buildTypes {
      release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.release
            minifyEnabled true //删除无用代码
            shrinkResources true //删除无用资源
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
      }
      debug {
            signingConfig signingConfigs.release
            debuggable true
      }
    }
获取签名文件的SHA1
https://i-blog.csdnimg.cn/blog_migrate/84e667c83073a9e2977274a04ce97ed3.png
keytool -list -v -keystore ./gaode_key.jks
引入依赖

https://i-blog.csdnimg.cn/blog_migrate/c5663e1edb7d4c0ebd111bf0fcd8b800.png
dependencies {
    implementation('com.amap.api:location:5.6.0')
}
Flutter 配置项目

https://i-blog.csdnimg.cn/blog_migrate/71f9e975956644fa472e8d41bd7a587e.png
amap_flutter_location: ^3.0.0
permission_handler: ^11.3.0
说明权限

https://i-blog.csdnimg.cn/blog_migrate/8231fd90290b14835a677d7dc2e6bebd.png
    <!--允许访问网络,必选权限-->
    <uses-permission android:name="android.permission.INTERNET" />

    <!--允许获取精确位置,精准定位必选-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!--允许获取粗略位置,粗略定位必选-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!--允许获取设备和运营商信息,用于问题排查和网络定位(无gps情况下的定位),若需网络定位功能则必选-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <!--允许获取网络状态,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!--允许获取wifi网络信息,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <!--允许获取wifi状态改变,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <!--后台获取位置信息,若需后台定位则必选-->
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <!--用于申请调用A-GPS模块,卫星定位加速-->
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />

    <!--允许写设备缓存,用于问题排查-->
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

    <!--允许写入扩展存储,用于写入缓存定位数据-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!--允许读设备等信息,用于问题排查-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
配置服务

<!-- 配置定位Service -->
      <service android:name="com.amap.api.location.APSService" />
https://i-blog.csdnimg.cn/blog_migrate/5df00762bc4d5b68254d9950b75b41a7.png
测试代码

import 'dart:async';
import 'dart:io';

import 'package:amap_flutter_location/amap_flutter_location.dart';
import 'package:amap_flutter_location/amap_location_option.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      useMaterial3: true,
      ),
      home: HomePage(),
    );
}
}

class HomePage extends StatefulWidget {
const HomePage({super.key});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
final AMapFlutterLocation flutterLocation = AMapFlutterLocation();
final AMapLocationOption aMapLocationOption = AMapLocationOption(
    needAddress: true,
    geoLanguage: GeoLanguage.DEFAULT,
    onceLocation: false,
    locationMode: AMapLocationMode.Hight_Accuracy,
    locationInterval: 2000,
    pausesLocationUpdatesAutomatically: false,
    desiredAccuracy: DesiredAccuracy.Best,
    desiredLocationAccuracyAuthorizationMode:
      AMapLocationAccuracyAuthorizationMode.FullAccuracy,
    distanceFilter: -1,
);
late final StreamSubscription<Map<String, Object>> subscription;
late int count = 0;

@override
void initState() {
    AMapFlutterLocation.updatePrivacyShow(true, true);
    AMapFlutterLocation.updatePrivacyAgree(true);
    requestPermission();
    AMapFlutterLocation.setApiKey(
      "e51a737b3742762791f3c89f4dc61e6d",
      "cb341ecb2fb63ff6965c62a009979f29",
    );
    if (Platform.isIOS) {
      requestAccuracyAuthorization();
    }
    subscription = flutterLocation.onLocationChanged().listen((event) {
      print(event.toString());
    });

    super.initState();
}

@override
void dispose() {
    subscription.cancel();
    flutterLocation.destroy();
    super.dispose();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
      child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ElevatedButton(
            onPressed: () {
                flutterLocation.setLocationOption(aMapLocationOption);
                Timer.periodic(const Duration(seconds: 1), (timer) {
                  count++;
                  print("定位序列号$count");
                  flutterLocation.startLocation();
                });
            },
            child: Text("开始定位"),
            ),
            ElevatedButton(
            onPressed: () {
                flutterLocation.stopLocation();
            },
            child: Text("停止定位"),
            ),
          ],
      ),
      ),
    );
}

/// 动态申请定位权限
void requestPermission() async {
    bool hasLocationWhenInUsePermission =
      await requestIosLocationWhenInUserPermission();
    if (hasLocationWhenInUsePermission) {
      bool hasLocationAlwaysWhenInUsePermission =
          await requestIosLocationAlwaysWhenInUserPermission();
      if (hasLocationAlwaysWhenInUsePermission) {
      } else {}
    } else {}
}

/// 申请定位权限
Future<bool> requestLocationPermission() async {
    var status = await Permission.location.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.location.request();
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

Future<bool> requestIosLocationPermission() async {
    var status = await Permission.location.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.location.request();
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

Future<bool> requestIosLocationWhenInUserPermission() async {
    var status = await Permission.locationWhenInUse.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.locationWhenInUse.request();
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

Future<bool> requestIosLocationAlwaysWhenInUserPermission() async {
    var status = await Permission.locationAlways.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.locationAlways.request();
      print("Permission.locationAlways - $status");
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

void requestAccuracyAuthorization() async {
    AMapAccuracyAuthorization currentAccuracyAuthorization =
      await flutterLocation.getSystemAccuracyAuthorization();
    if (currentAccuracyAuthorization ==
      AMapAccuracyAuthorization.AMapAccuracyAuthorizationFullAccuracy) {
      print("精确定位类型");
    } else if (currentAccuracyAuthorization ==
      AMapAccuracyAuthorization.AMapAccuracyAuthorizationReducedAccuracy) {
      print("模糊定位类型");
    } else {
      print("未知定位类型");
    }
}
}
运行结果

https://i-blog.csdnimg.cn/blog_migrate/b2768c8207675addf5443d763cd23ef0.png
IOS

创建Key

https://i-blog.csdnimg.cn/blog_migrate/7c23a35e79cbedb34fa4cb2b98dbb42c.png
Bundle ID为包名
https://i-blog.csdnimg.cn/blog_migrate/3e8d72a1dcba93ba068640a5786abbd5.png
配置Podfile

https://i-blog.csdnimg.cn/blog_migrate/4b1c8b12f93372689a4662ac07552248.png
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
          '$(inherited)',
          'PERMISSION_LOCATION=1',
      ]
配置info.plist

权限参考官方文档
下面是按照后台定位的标准来配置的,就是APP在后台,也能定位。
https://i-blog.csdnimg.cn/blog_migrate/e5675375c4e91c8bc7e338ea9dc4ca49.png
        <key>NSLocationTemporaryUsageDescriptionDictionary</key>
    <dict>
      <key>AnotherUsageDescription</key>
      <string>This app needs accurate location so it can show you relevant results.</string>
      <key>ExampleUsageDescription</key>
      <string>This app needs accurate location so it can verify that you are in a supported region.</string>
    </dict>
        <key>NSLocationAlwaysUsageDescription</key>
        <string>can I has location always?</string>
        <key>NSLocationWhenInUseUsageDescription</key>
        <string>need location when in use?</string>
        <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
        <string>always and when in use!</string>
        <key>NSLocationUsageDescription</key>
        <string>older devices need location.</string>
权限对应

https://i-blog.csdnimg.cn/blog_migrate/7fc254003f46d2f9cba8de739aa4e3aa.png
https://i-blog.csdnimg.cn/blog_migrate/33d80384a0633008393ba83b55191ad0.png
拒绝了上面的这个权限,那就没办法后台定位了
https://i-blog.csdnimg.cn/blog_migrate/392e1de104e83db51ae945fda85207da.png
其余几个权限说明也加上,点我检察参考链接
https://i-blog.csdnimg.cn/blog_migrate/f6fb616998b0fcf2a01f81de03d11f80.png
Background Mode

https://i-blog.csdnimg.cn/blog_migrate/2e3dbb2a41e65850cd216baaefda3395.png
https://i-blog.csdnimg.cn/blog_migrate/0e24dcbe9f6e7f774b9ca6426838f6e2.png
勾上location updates
测试代码

import 'dart:async';
import 'dart:io';

import 'package:amap_flutter_location/amap_flutter_location.dart';
import 'package:amap_flutter_location/amap_location_option.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
      colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      useMaterial3: true,
      ),
      home: HomePage(),
    );
}
}

class HomePage extends StatefulWidget {
const HomePage({super.key});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
final AMapFlutterLocation flutterLocation = AMapFlutterLocation();
final AMapLocationOption aMapLocationOption = AMapLocationOption(
    needAddress: true,
    geoLanguage: GeoLanguage.DEFAULT,
    onceLocation: false,
    locationMode: AMapLocationMode.Hight_Accuracy,
    locationInterval: 2000,
    pausesLocationUpdatesAutomatically: false,
    desiredAccuracy: DesiredAccuracy.Best,
    desiredLocationAccuracyAuthorizationMode:
      AMapLocationAccuracyAuthorizationMode.FullAccuracy,
    distanceFilter: -1,
);
late final StreamSubscription<Map<String, Object>> subscription;
late int count = 0;

@override
void initState() {
    AMapFlutterLocation.updatePrivacyShow(true, true);
    AMapFlutterLocation.updatePrivacyAgree(true);
    requestPermission();
    AMapFlutterLocation.setApiKey(
      "e51a737b3742762791f3c89f4dc61e6d",
      "cb341ecb2fb63ff6965c62a009979f29",
    );
    if (Platform.isIOS) {
      requestAccuracyAuthorization();
    }
    subscription = flutterLocation.onLocationChanged().listen((event) {
      print(event.toString());
    });

    super.initState();
}

@override
void dispose() {
    subscription.cancel();
    flutterLocation.destroy();
    super.dispose();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
      child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            ElevatedButton(
            onPressed: () {
                flutterLocation.setLocationOption(aMapLocationOption);
                Timer.periodic(const Duration(seconds: 1), (timer) {
                  count++;
                  print("定位序列号$count");
                  flutterLocation.startLocation();
                });
            },
            child: Text("开始定位"),
            ),
            ElevatedButton(
            onPressed: () {
                flutterLocation.stopLocation();
            },
            child: Text("停止定位"),
            ),
          ],
      ),
      ),
    );
}

/// 动态申请定位权限
void requestPermission() async {
    bool hasLocationWhenInUsePermission =
      await requestIosLocationWhenInUserPermission();
    if (hasLocationWhenInUsePermission) {
      bool hasLocationAlwaysWhenInUsePermission =
          await requestIosLocationAlwaysWhenInUserPermission();
      if (hasLocationAlwaysWhenInUsePermission) {
      } else {}
    } else {}
}

/// 申请定位权限
Future<bool> requestLocationPermission() async {
    var status = await Permission.location.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.location.request();
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

Future<bool> requestIosLocationPermission() async {
    var status = await Permission.location.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.location.request();
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

Future<bool> requestIosLocationWhenInUserPermission() async {
    var status = await Permission.locationWhenInUse.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.locationWhenInUse.request();
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

Future<bool> requestIosLocationAlwaysWhenInUserPermission() async {
    var status = await Permission.locationAlways.status;
    if (status == PermissionStatus.granted) {
      return true;
    } else {
      status = await Permission.locationAlways.request();
      print("Permission.locationAlways - $status");
      if (status == PermissionStatus.granted) {
      return true;
      } else {
      return false;
      }
    }
}

void requestAccuracyAuthorization() async {
    AMapAccuracyAuthorization currentAccuracyAuthorization =
      await flutterLocation.getSystemAccuracyAuthorization();
    if (currentAccuracyAuthorization ==
      AMapAccuracyAuthorization.AMapAccuracyAuthorizationFullAccuracy) {
      print("精确定位类型");
    } else if (currentAccuracyAuthorization ==
      AMapAccuracyAuthorization.AMapAccuracyAuthorizationReducedAccuracy) {
      print("模糊定位类型");
    } else {
      print("未知定位类型");
    }
}
}
运行结果

https://i-blog.csdnimg.cn/blog_migrate/046dec98300f885a901675a2bf9fc21e.png
舆图

官方文档
引入插件

amap_flutter_map插件地点
amap_flutter_base插件地点
Android

引入依赖

https://i-blog.csdnimg.cn/blog_migrate/37773fbc58c095140df701b1d70e2c6c.png
implementation('com.amap.api:3dmap:latest.integration')
测试代码(表现舆图)

import 'package:amap_flutter_map/amap_flutter_map.dart';
import 'package:amap_flutter_base/amap_flutter_base.dart';
import 'package:flutter/material.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    );
}
}

class HomePage extends StatefulWidget {
const HomePage({super.key});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
late AMapController _mapController;
late AMapWidget mapWidget;

@override
void initState() {
    mapWidget = AMapWidget(
      apiKey: const AMapApiKey(
      iosKey: "cb341ecb2fb63ff6965c62a009979f29",
      androidKey: "e51a737b3742762791f3c89f4dc61e6d",
      ),
      privacyStatement: const AMapPrivacyStatement(
      hasContains: true,
      hasShow: true,
      hasAgree: true,
      ),
      onMapCreated: onMapCreated,
    );
    super.initState();
}

@override
void dispose() {
    super.dispose();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: SizedBox(
      height: MediaQuery.of(context).size.height,
      width: MediaQuery.of(context).size.width,
      child: Stack(
          children: [
            mapWidget,
            Positioned(
            right: 10,
            bottom: 10,
            child: FutureBuilder<String?>(
                future: getApprovalNumber(),
                builder: (ctx, snapshot) {
                  return Column(
                  children: [
                      Text("${snapshot.data}"),
                  ],
                  );
                },
            ),
            ),
          ],
      ),
      ),
    );
}

void onMapCreated(AMapController controller) {
    CameraUpdate cameraUpdate = CameraUpdate.newCameraPosition(
      const CameraPosition(
      target: LatLng(30, 121.473658),
      zoom: 10,
      tilt: 30,
      bearing: 0,
      ),
    );
    controller.moveCamera(cameraUpdate);
    setState(() {
      _mapController = controller;
    });
}

Future<String?> getApprovalNumber() async {
    // 普通地图审图号
    String? mapContentApprovalNumber =
      await _mapController.getMapContentApprovalNumber();
    // // 卫星地图审图号
    // String? satelliteImageApprovalNumber =
    //   await _mapController.getSatelliteImageApprovalNumber();
    return mapContentApprovalNumber;
}
}
运行结果

https://i-blog.csdnimg.cn/blog_migrate/2b0af95f0b3a78222f79004d2b45cd78.png
舆图标点

舆图标点参考文档
测试代码(舆图标点)

import 'package:amap_flutter_map/amap_flutter_map.dart';
import 'package:amap_flutter_base/amap_flutter_base.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    );
}
}

class HomePage extends StatefulWidget {
const HomePage({super.key});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
late AMapController _mapController;
late Map<String, Marker> markerMap;
late double nowLatitude;
late double nowLongitude;
late AMapApiKey aMapApiKey;
late AMapPrivacyStatement aMapPrivacyStatement;

@override
void initState() {
    markerMap = {};
    nowLatitude = 30;
    nowLongitude = 121.473658;
    aMapApiKey = const AMapApiKey(
      iosKey: "cb341ecb2fb63ff6965c62a009979f29",
      androidKey: "e51a737b3742762791f3c89f4dc61e6d",
    );
    aMapPrivacyStatement = const AMapPrivacyStatement(
      hasContains: true,
      hasShow: true,
      hasAgree: true,
    );
    super.initState();
}

@override
void dispose() {
    super.dispose();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: SizedBox(
      height: MediaQuery.of(context).size.height,
      width: MediaQuery.of(context).size.width,
      child: Stack(
          children: [
            AMapWidget(
            apiKey: aMapApiKey,
            privacyStatement: aMapPrivacyStatement,
            onMapCreated: onMapCreated,
            markers: Set.of(markerMap.values),
            ),
            Positioned(
            right: 10,
            bottom: 10,
            child: FutureBuilder<String?>(
                future: getApprovalNumber(),
                builder: (ctx, snapshot) {
                  return Column(
                  children: [
                      Text("${snapshot.data}"),
                  ],
                  );
                },
            ),
            ),
          ],
      ),
      ),
    );
}

void onMapCreated(AMapController controller) {
    CameraUpdate cameraUpdate = CameraUpdate.newCameraPosition(
      CameraPosition(
      target: LatLng(nowLatitude, nowLongitude),
      zoom: 10,
      tilt: 30,
      bearing: 0,
      ),
    );
    controller.moveCamera(cameraUpdate);
    setState(() {
      _mapController = controller;
    });
    getMarker(
      nowLatitude,
      nowLongitude,
      image: "assets/images/my_position.png",
      title: "我",
    );
}

Future<String?> getApprovalNumber() async {
    // 普通地图审图号
    String? mapContentApprovalNumber =
      await _mapController.getMapContentApprovalNumber();
    // // 卫星地图审图号
    // String? satelliteImageApprovalNumber =
    //   await _mapController.getSatelliteImageApprovalNumber();
    return mapContentApprovalNumber;
}

Future<void> getMarker(
    double latitude,
    double longitude, {
    String? image,
    String? title,
    String? snippet,
}) async {
    LatLng position = LatLng(latitude, longitude);
    Marker marker = Marker(
      onTap: (s) {
      print(s);
      },
      infoWindow: InfoWindow(
      title: title,
      snippet: snippet,
      ),
      position: position,
      icon: image != null
          ? await getBitmapDescriptorFromAssetBytes(image, 100, 100)
          : BitmapDescriptor.defaultMarker,
    );
    markerMap = marker;
    setState(() {});
}

Future<BitmapDescriptor> getBitmapDescriptorFromAssetBytes(
    String path,
    double width,
    double height,
) async {
    var imageFile = await rootBundle.load(path);
    var pictureRecorder = ui.PictureRecorder();
    var canvas = Canvas(pictureRecorder);
    var imageUint8List = imageFile.buffer.asUint8List();
    var codec = await ui.instantiateImageCodec(imageUint8List);
    var imageFI = await codec.getNextFrame();
    paintImage(
      canvas: canvas,
      rect: Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()),
      image: imageFI.image,
      filterQuality: FilterQuality.medium,
    );
    var image = await pictureRecorder
      .endRecording()
      .toImage(width.toInt(), height.toInt());
    var data = await image.toByteData(format: ui.ImageByteFormat.png);
    return BitmapDescriptor.fromBytes(data!.buffer.asUint8List());
}
}

运行结果

https://i-blog.csdnimg.cn/blog_migrate/ccb0ca099b4b347db5b68b17fc153059.png
Ios

配置pod

按照上面的步骤,用Xcode打开ios目次
https://i-blog.csdnimg.cn/blog_migrate/8ddc3397ddc46e18cbe3262401f5016b.png
pod 'AMapLocation'
pod 'AMap3DMap'
最好加上,不加可能会在打包时堕落
加上后pod install
https://i-blog.csdnimg.cn/blog_migrate/2c7fe4e6b659a2d25141f04bba8dc131.png
另有pod repo update也要试试(遇到一些bug时)
https://i-blog.csdnimg.cn/blog_migrate/73f3aa4d230de4e35e81f93c121e576b.png
以及
https://i-blog.csdnimg.cn/blog_migrate/a601f8ae9a70a23a9a0ba35cb9d29ced.png
测试代码(同Android)

import 'package:amap_flutter_map/amap_flutter_map.dart';
import 'package:amap_flutter_base/amap_flutter_base.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:ui' as ui;

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    );
}
}

class HomePage extends StatefulWidget {
const HomePage({super.key});

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
late AMapController _mapController;
late Map<String, Marker> markerMap;
late double nowLatitude;
late double nowLongitude;
late AMapApiKey aMapApiKey;
late AMapPrivacyStatement aMapPrivacyStatement;

@override
void initState() {
    markerMap = {};
    nowLatitude = 30;
    nowLongitude = 121.473658;
    aMapApiKey = const AMapApiKey(
      iosKey: "cb341ecb2fb63ff6965c62a009979f29",
      androidKey: "e51a737b3742762791f3c89f4dc61e6d",
    );
    aMapPrivacyStatement = const AMapPrivacyStatement(
      hasContains: true,
      hasShow: true,
      hasAgree: true,
    );
    super.initState();
}

@override
void dispose() {
    super.dispose();
}

@override
Widget build(BuildContext context) {
    return Scaffold(
      body: SizedBox(
      height: MediaQuery.of(context).size.height,
      width: MediaQuery.of(context).size.width,
      child: Stack(
          children: [
            AMapWidget(
            apiKey: aMapApiKey,
            privacyStatement: aMapPrivacyStatement,
            onMapCreated: onMapCreated,
            markers: Set.of(markerMap.values),
            ),
            Positioned(
            right: 10,
            bottom: 10,
            child: FutureBuilder<String?>(
                future: getApprovalNumber(),
                builder: (ctx, snapshot) {
                  return Column(
                  children: [
                      Text("${snapshot.data}"),
                  ],
                  );
                },
            ),
            ),
          ],
      ),
      ),
    );
}

void onMapCreated(AMapController controller) {
    CameraUpdate cameraUpdate = CameraUpdate.newCameraPosition(
      CameraPosition(
      target: LatLng(nowLatitude, nowLongitude),
      zoom: 10,
      tilt: 30,
      bearing: 0,
      ),
    );
    controller.moveCamera(cameraUpdate);
    setState(() {
      _mapController = controller;
    });
    getMarker(
      nowLatitude,
      nowLongitude,
      image: "assets/images/my_position.png",
      title: "我",
    );
}

Future<String?> getApprovalNumber() async {
    // 普通地图审图号
    String? mapContentApprovalNumber =
      await _mapController.getMapContentApprovalNumber();
    // // 卫星地图审图号
    // String? satelliteImageApprovalNumber =
    //   await _mapController.getSatelliteImageApprovalNumber();
    return mapContentApprovalNumber;
}

Future<void> getMarker(
    double latitude,
    double longitude, {
    String? image,
    String? title,
    String? snippet,
}) async {
    LatLng position = LatLng(latitude, longitude);
    Marker marker = Marker(
      onTap: (s) {
      print(s);
      },
      infoWindow: InfoWindow(
      title: title,
      snippet: snippet,
      ),
      position: position,
      icon: image != null
          ? await getBitmapDescriptorFromAssetBytes(image, 100, 100)
          : BitmapDescriptor.defaultMarker,
    );
    markerMap = marker;
    setState(() {});
}

Future<BitmapDescriptor> getBitmapDescriptorFromAssetBytes(
    String path,
    double width,
    double height,
) async {
    var imageFile = await rootBundle.load(path);
    var pictureRecorder = ui.PictureRecorder();
    var canvas = Canvas(pictureRecorder);
    var imageUint8List = imageFile.buffer.asUint8List();
    var codec = await ui.instantiateImageCodec(imageUint8List);
    var imageFI = await codec.getNextFrame();
    paintImage(
      canvas: canvas,
      rect: Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()),
      image: imageFI.image,
      filterQuality: FilterQuality.medium,
    );
    var image = await pictureRecorder
      .endRecording()
      .toImage(width.toInt(), height.toInt());
    var data = await image.toByteData(format: ui.ImageByteFormat.png);
    return BitmapDescriptor.fromBytes(data!.buffer.asUint8List());
}
}

运行结果

https://i-blog.csdnimg.cn/blog_migrate/67f6a2b6bad0ed206d019b87ff2626d9.png
打包也没标题就行了
https://i-blog.csdnimg.cn/blog_migrate/f1a478570151ad49b8f100eeb08dc9ab.png
发布apk

出现的闪退标题

再全部配置完后,发现一个bug,实行
flutter build apk --no-tree-shake-icons --obfuscate --split-debug-info=./
安装完apk后,点开舆图会直接闪退
并出现下面
https://i-blog.csdnimg.cn/blog_migrate/853f1be81ee10fdebd00cf1cd0b36521.png
F/obo2.deman_mob(25374): java_vm_ext.cc:570] JNI DETECTED ERROR IN APPLICATION: java_class == null
F/obo2.deman_mob(25374): java_vm_ext.cc:570]   in call to GetStaticMethodID
F/obo2.deman_mob(25374): java_vm_ext.cc:570]   from java.lang.String java.lang.Runtime.nativeLoad(java.lang.String, java.lang.ClassLoader, java.lang.Class)
F/obo2.deman_mob(25374): thread.cc:2560] No pending exception expected: java.lang.ClassNotFoundException: com.autonavi.base.amap.mapcore.ClassTools
F/obo2.deman_mob(25374): thread.cc:2560]   at java.lang.String java.lang.Runtime.nativeLoad(java.lang.String, java.lang.ClassLoader, java.lang.Class) (Runtime.java:-2)
F/obo2.deman_mob(25374): thread.cc:2560]   at java.lang.String java.lang.Runtime.nativeLoad(java.lang.String, java.lang.ClassLoader) (Runtime.java:1115)
F/obo2.deman_mob(25374): thread.cc:2560]   at void java.lang.Runtime.loadLibrary0(java.lang.ClassLoader, java.lang.Class, java.lang.String) (Runtime.java:1069)
F/obo2.deman_mob(25374): thread.cc:2560]   at void java.lang.Runtime.loadLibrary0(java.lang.Class, java.lang.String) (Runtime.java:1007)
F/obo2.deman_mob(25374): thread.cc:2560]   at void java.lang.System.loadLibrary(java.lang.String) (System.java:1668)
F/obo2.deman_mob(25374): thread.cc:2560]   at boolean o5.a.k(android.content.Context) (:-1)
F/obo2.deman_mob(25374): thread.cc:2560]   at void z3.v7.N1(int, javax.microedition.khronos.opengles.GL10, javax.microedition.khronos.egl.EGLConfig) (:-1)
F/obo2.deman_mob(25374): thread.cc:2560]   at void z3.v7.F0(javax.microedition.khronos.opengles.GL10, javax.microedition.khronos.egl.EGLConfig) (:-1)
F/obo2.deman_mob(25374): thread.cc:2560]   at void d5.c.onSurfaceCreated(javax.microedition.khronos.opengles.GL10, javax.microedition.khronos.egl.EGLConfig) (:-1)
F/obo2.deman_mob(25374): thread.cc:2560]   at void z3.p8$i.q() (:-1)
F/obo2.deman_mob(25374): thread.cc:2560]   at void z3.p8$i.run() (:-1)
去修改build.gradle
https://i-blog.csdnimg.cn/blog_migrate/f2d1ad296a3ce40e64e5119b405ade4a.png
参考文章即可修复bug
这样就不会闪退了

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Flutter】高德舆图接入Android/IOS(定位&舆图)