Flutter项目中接入百度地图(2025 最新 Android版)

百度地图的Flutter官方文档非常老,参考价值不大,网上的教程也都是好几年前的,和目前的版本都有点出入。最终爬坑无数对接成功,也是不容易。

百度地图Flutter的官方文档地址:lbsyun.baidu.com/faq/api?tit...

先说下我的Flutter环境:flutter_windows_3.22.2,Android language:Kotlin Android Studio:2025.1.1 Patch 1

我这只是用来展示一些美术馆的位置,所以只用到2个插件,可以根据情况加载需要的插件

csharp 复制代码
flutter pub add flutter_baidu_mapapi_map
flutter pub add flutter_baidu_mapapi_base

Android环境下的配置: 1,\android\build.gradle.kts文件,添加maven源

ini 复制代码
maven {
    url = uri("https://mapapi.bdimg.com/repository/maven-releases/")
    // 如果是HTTP协议(非HTTPS),需要添加:
    isAllowInsecureProtocol = true
}

2,新建自定义Application

新建文件:\androidl\app\src\main\kotlin\com\自己的包名\MyApplication.kt

内容如下:

kotlin 复制代码
package com.*****.*****//自己的包名

import android.app.Application
import com.baidu.mapapi.CoordType
import com.baidu.mapapi.SDKInitializer

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // 必须先设置隐私合规
        SDKInitializer.setAgreePrivacy(applicationContext, true)

        // 初始化SDK
        SDKInitializer.initialize(applicationContext)
        SDKInitializer.setCoordType(CoordType.BD09LL)
    }
}

3,配置签名证书

生成证书CMD命令:

arduino 复制代码
keytool -genkey -v -keystore release.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my_key

命令最后的my_key是别名,生成证书一定要记好密码

生成好的证书是一个.jks文件,可以重命名为release.jks,放到\android目录下

另外再新建个空白文档,输入以下内容:

ini 复制代码
storePassword=你的证书密码
keyPassword=你的证书密码
keyAlias=my_key
storeFile=../release.jks

也保存至\android,命名:key.properties

验证证书的CMD命令,百度申请SDK KEY,需要查询SHA1的时候用的到:

arduino 复制代码
keytool -list -v -keystore release.jks -alias my_key

4,加载自定义Application

修改\android\app\src\main\AndroidManifest.xml

修改顶部manifest

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.*****.*****"> <!-- 确保package与您的应用包名一致 -->

添加权限:

xml 复制代码
<!--访问网络-->
<uses-permission android:name="android.permission.INTERNET" />
<!--粗略定位-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--精确定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--申请调用 A-GPS 模块-->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<!--用于获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--用于访问 wifi 网络信息,wifi 信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--用于获取 wifi 的获取权限,wifi 信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!--用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--用于写入缓存数据到扩展存储卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

修改application:

xml 复制代码
<application
    android:label="@string/appName"
    android:name=".MyApplication"
    android:icon="@mipmap/ic_launcher"
    android:requestLegacyExternalStorage="true"> <!-- 添加以兼容Android 10+存储权限 -->

添加meta-data百度地图的SDK Key:

ini 复制代码
<meta-data
    android:name="com.baidu.lbsapi.API_KEY"
    android:value="你的百度SDK KEY 自行到百度地图开放平台申请" />

5,修改build.gradle.kts

文件目录:\android\app\build.gradle.kts

添加签名配置,key.properties就是刚刚你放在\android目录下key.properties:

javascript 复制代码
// 加载签名配置(确保项目根目录有 key.properties 文件)
val keystoreProperties = Properties().apply {
    load(File(rootProject.rootDir, "key.properties").inputStream())
}

signingConfigs {
    create("release") { // 配置名为 "release" 的签名
        storeFile = file(keystoreProperties["storeFile"] as String)   // keystore 文件路径
        storePassword = keystoreProperties["storePassword"] as String // keystore 密码
        keyAlias = keystoreProperties["keyAlias"] as String           // key 别名
        keyPassword = keystoreProperties["keyPassword"] as String     // key 密码
    }

    getByName("debug") {
        storeFile = file(keystoreProperties["storeFile"] as String)   // keystore 文件路径
        storePassword = keystoreProperties["storePassword"] as String // keystore 密码
        keyAlias = keystoreProperties["keyAlias"] as String           // key 别名
        keyPassword = keystoreProperties["keyPassword"] as String     // key 密码
    }
}
ini 复制代码
buildTypes {
    release {
        signingConfig = signingConfigs.getByName("release") // 应用签名配置
        isMinifyEnabled = true   // 启用代码混淆
        isShrinkResources = true // 移除无用资源
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
    }
    debug {
        signingConfig = signingConfigs.getByName("debug") // debug模式也使用证书
    }
}

依赖的百度地图包配置:

scss 复制代码
dependencies {
    implementation("com.baidu.lbsyun:BaiduMapSDK_Map:7.6.4")
    implementation("com.baidu.lbsyun:BaiduMapSDK_Search:7.6.4")
    implementation("com.baidu.lbsyun:BaiduMapSDK_Util:7.6.4")
}

另外百度地图要求最低minSdk,不小于22

ini 复制代码
minSdk = 22

还需要新建一个\android\app、proguard-rules.pro文件,用于安卓release版本打包时,保留百度地图的所有类,不然会APP闪退 文件内容如下:

php 复制代码
# 保留百度地图的所有类(防止被混淆或移除)
-keep class com.baidu.** { *; }
-keep class vi.** { *; }
-keep class com.baidu.platform.** { *; }
-keep class com.baidu.mapsdkplatform.** { *; }

# 保留所有 native 方法
-keepclasseswithmembernames class * {
    native <methods>;
}

# 保留 Parcelable 序列化类
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

# 保留 Flutter 相关类(可选,如果用到其他插件)
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }

目前为止,安卓端的配置就好了,然后就是Flutter代码部分

项目入口文件main.dart的百度地图全局初始化:

顶部导入组件

arduino 复制代码
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';

在main()中添加初始化代码:

scss 复制代码
/// 设置用户是否同意SDK隐私协议
BMFMapSDK.setAgreePrivacy(true);

// 百度地图sdk初始化鉴权
if (Platform.isIOS) {
    BMFMapSDK.setApiKeyAndCoordType('****你的百度SDK KEY****', BMF_COORD_TYPE.BD09LL);
} else if (Platform.isAndroid) {
    // Android 目前不支持接口设置Apikey,
    // 请在主工程的Manifest文件里设置,详细配置方法请参考官网(https://lbsyun.baidu.com/)demo
    BMFMapSDK.setCoordType(BMF_COORD_TYPE.BD09LL);
}

然后再项目页面中,就可以根据实际项目要求,使用百度地图,给个我在的DEMO仅供参考:

dart 复制代码
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart';
import 'package:url_launcher/url_launcher.dart';
import '../components/pagebox_article.dart';

// 坐标系转换函数,腾讯坐标系转百度坐标系 GCJ-02 to BD-09 conversion 
Map<String, double> gcj02ToBd09(double lng, double lat) {
  const xPi = 3.14159265358979324 * 3000.0 / 180.0;
  final x = lng, y = lat;
  final z = sqrt(x * x + y * y) + 0.00002 * sin(y * xPi);
  final theta = atan2(y, x) + 0.000003 * cos(x * xPi);
  final bdLng = z * cos(theta) + 0.0065;
  final bdLat = z * sin(theta) + 0.006;
  return {'lng': bdLng, 'lat': bdLat};
}

class ArtExhibitionsMapController extends GetxController {
  String? latitude;
  String? longitude;
  String? title;
  String? address;
  BMFMapController? mapController;
  bool isMapReady = false; // 添加地图准备状态

  @override
  void onInit() {
    super.onInit();
    latitude = Get.parameters['latitude'];
    longitude = Get.parameters['longitude'];
    title = Get.parameters['title'];
    address = Get.parameters['address'];

    // 延迟初始化地图版本检查
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _initMap();
    });
  }

  Future<void> _initMap() async {
    try {
      Map? map = await BMFMapBaseVersion.nativeBaseVersion;
      print('+++////////////////////////////////////////获取原生地图版本号:$map');
      isMapReady = true;
      update(); // 通知UI更新
    } catch (e) {
      print('地图初始化失败: $e');
    }
  }

  @override
  void onClose() {
    print('释放地图控制器');
    mapController = null;
    isMapReady = false;
    super.onClose();
  }
}

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

  @override
  Widget build(BuildContext context) {
    final ArtExhibitionsMapController c = Get.put(ArtExhibitionsMapController());

    return ArticlePageBox(
      appbarTitle: tr("map"),
      body: GetBuilder<ArtExhibitionsMapController>(
        builder: (c) {
          if (c.latitude == null || c.longitude == null || !c.isMapReady) {
            return const Center(child: CircularProgressIndicator());
          }

          final lat = double.tryParse(c.latitude ?? '') ?? 39.915;
          final lng = double.tryParse(c.longitude ?? '') ?? 116.404;

          // 腾讯地图 GCJ-02 坐标转百度地图 BD-09 坐标
          final bd09Coord = gcj02ToBd09(lng, lat);

          return SizedBox(
            width: double.infinity,
            height: double.infinity,
            child: Stack(
              children: [
                // 百度地图:全屏填充
                BMFMapWidget(
                  onBMFMapCreated: (controller) {
                    c.mapController = controller;

                    /// 地图加载回调
                    c.mapController?.setMapDidLoadCallback(callback:() {
                      print('地图加载完成');
                    });

                    /// 地图标记点点击回调
                    c.mapController?.setMapClickedMarkerCallback(callback: (BMFMarker marker) {
                      print('marker点击----${marker.toMap()}');
                    });

                    // 地图addMarker,如果不放在回调里面,就放在这个延时函数里面
                    Future.delayed(const Duration(milliseconds: 300), () {
                      controller.updateMapOptions(BMFMapOptions(
                      center: BMFCoordinate(bd09Coord['lat']!, bd09Coord['lng']!),
                        zoomLevel: 17,
                      ));

                      final marker = BMFMarker.icon(
                        position: BMFCoordinate(bd09Coord['lat']!, bd09Coord['lng']!),
                        title: c.title ?? "",
                        subtitle: c.address ?? "",
                        identifier: 'galleryMarker',
                        icon: 'images/marker.png',
                        titleOptions: BMFTitleOptions( //Android独有
                            text: c.title ?? "",
                            bgColor: Colors.white,
                            fontColor: Colors.black,
                            fontSize: 48
                        ),
                        canShowCallout: true,  // 允许显示信息窗口iOS独有
                        selected: true,        // 默认选中(自动显示信息窗口)iOS独有
                      );

                      controller.addMarker(marker);
                    });
                  },
                  mapOptions: BMFMapOptions(
                    center: BMFCoordinate(
                      bd09Coord['lat'] ?? 39.915,
                      bd09Coord['lng'] ?? 116.404,
                    ),
                    zoomLevel: 17,
                  ),
                ),

                // 悬浮在底部的导航按钮
                MapsPositioned(
                  lat: bd09Coord['lat'] ?? 39.915,
                  lng: bd09Coord['lng'] ?? 116.404,
                  title: c.title ?? "",
                )
              ],
            ),
          );
        },
      ),
    );
  }
}

// Positioned按钮组件
class MapsPositioned extends StatelessWidget {
  final double lat;
  final double lng;
  final String title;
  const MapsPositioned({super.key, required this.lat, required this.lng, required this.title});

  @override
  Widget build(BuildContext context) {

    return Positioned(
      bottom: 30,
      left: MediaQuery.of(context).size.width * 0.1, // (100% - 80%) / 2 = 居中
      width: MediaQuery.of(context).size.width * 0.8,
      child: ElevatedButton(
        onPressed: () async {

          final baiduUri = Uri.parse('baidumap://map/marker?location=$lat,$lng&title=$title&coord_type=bd09ll');
          final webUri = Uri.parse('https://api.map.baidu.com/marker?location=$lat,$lng&title=$title&output=html');

          try {
            if (await canLaunchUrl(baiduUri)) {
              await launchUrl(baiduUri, mode: LaunchMode.externalApplication);
            } else {
              await launchUrl(webUri);
            }
          } catch (e) {
            await launchUrl(webUri);
          }
        },
        style: ElevatedButton.styleFrom(
          backgroundColor: Colors.black,
          foregroundColor: Colors.white,
          padding: const EdgeInsets.symmetric(vertical: 16.0),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(8.0),
          ),
          textStyle: const TextStyle(
            fontSize: 16,
            fontWeight: FontWeight.bold,
          ),
        ),
        child: Text("navigation").tr(),
      ),
    );
  }
}

APP中打开百度地图的页面,只能在真机上测试,模拟器会闪退。不管安卓机还是iOS模拟器,都闪退。

如果内容对Flutter开发者有些许帮助的话,之后再写一下iOS版本的配置

相关推荐
wilinz2 小时前
Flutter 图片压缩性能对比
flutter
程序员老刘21 小时前
Dart MCP翻车了!3.9.0版本无法运行,这个坑你踩过吗?
flutter·ai编程·客户端
ideal树叶1 天前
flutter 中 的 关键字
flutter
世界不及妳微笑1 天前
Flutter 记录应用授权登录踩坑之旅
flutter
鹏多多1 天前
flutter-使用confetti制作炫酷纸屑爆炸粒子动画
android·前端·flutter
耳東陳12511 天前
【重磅发布】flutter_chen_updater-版本升级更新
flutter
HH思️️无邪2 天前
Flutter 开发技巧 AI 快速构建 json_annotation model 的提示词
flutter·json
笔沫拾光2 天前
hooks_riverpod框架解析
flutter·hooks·hooks_riverpod
problc2 天前
Flutter桌面应用实战:Windows系统代理切换工具开发
windows·flutter
程序员老刘2 天前
Cursor vs Claude Code vs AS+AI助手:谁才是客户端的编程神器?
flutter·ai编程·客户端