版本:flutter 2.10.4
百度地图3.2.0
IDE:Android Studio
最近公司项目需要集成地图,看着百度地图有flutter插件就开始忙活了,踩了一脑门子的坑,现在分享给大家。
2022-08-01 更新关于在调用poi搜索等功能时,提示鉴权失败的问题
引入的base包在as不提示时,建议手动添加引用
鉴权初始化后才可以使用搜索
import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart'
import 'dart:io' show Platform;
// 百度地图sdk初始化鉴权
if (Platform.isIOS) {
_locationPlugin.authAK('请在此输入您在开放平台上申请的API_KEY');
BMFMapSDK.setApiKeyAndCoordType(
'请在此输入您在开放平台上申请的API_KEY', BMF_COORD_TYPE.BD09LL);
} else if (Platform.isAndroid) {
// Android 目前不支持接口设置Apikey,
// 请在主工程的Manifest文件里设置,详细配置方法请参考官网(https://lbsyun.baidu.com/)demo
BMFMapSDK.setCoordType(BMF_COORD_TYPE.BD09LL);
}
目录
1.申请AK、SHA1值
在百度开发者后台申请Android所需的AK值,这个网上很多教程不细说
2.集成地图插件
在flutter项目的pubspec.yami中,dependencies下添加如下插件
dependencies:
#https://lbsyun.baidu.com/index.php?title=flutter/loc/create-project/note
#注意第三个插件在百度教程中是fflutter开头,实际为flutter开头
flutter_baidu_mapapi_map: ^3.2.0
flutter_baidu_mapapi_search: ^3.2.0
flutter_baidu_mapapi_utils: ^3.2.0
flutter_bmflocation: ^3.1.0
注意
第一个坑来了,
百度的地图教程多打了一个f,实际为 flutter_baidu_mapapi_utils: ^3.2.0
导致我第一次集成死活找不到插件。
之后使用pub get获取插件包即可
#如果获取不到插件,可能是百度地图的插件又更新了,建议使用any版本号重新在pubspec.lock中重新获取一下
例如flutter_baidu_mapapi_map: ^3.2.0修改为flutter_baidu_mapapi_map: any,然后pub get,打开项目下pubspec.lock翻到这个插件名,看看对应version版本修改一下
3.Android端配置BmfMapApplication
右击找到flutter菜单,用as打开android文件夹。
如果没有,直接用open打开android文件夹也行
菜单置灰解决方案:新建一个xxx_android.imi文件与local.properties同级,xxx为项目名
内容为
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/gen" />
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/gen" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/app/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/app/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/app/src/main/assets" />
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/app/src/main/libs" />
<option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/app/src/main/proguard_logs" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/app/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
</content>
<orderEntry type="jdk" jdkName="Android API 30 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Flutter for Android" level="project" />
</component>
</module>
如果新建后还是不能在菜单中打开,可以试试
还是不能打开,建议新建一个项目,找到imi文件,复制粘贴修改
文件类型为intelliJ.. 这个文件类型我没在Androidstudio找到
理论上可以用这个修改
打开后在MainActivity同级下新建BaseApplication
import com.baidu.mapapi.base.BmfMapApplication;
public class BaseApplication extends BmfMapApplication {
@Override
public void onCreate() {
super.onCreate();
}
}
可能会涉及到个别model中使用了min16版本,手动在build.gradle修改为19即可
如果BmfMapApplication不存在,重新在flutter中pubget几次,flutter clean几次
如果还是不存在,大概提供一下BmfMapApplication路径提供参考,是否是model没有编译进来,或者包没引入到
之后按教程修改application的name值为BaseApplication,添加权限
添加AK值
<application>
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="请输入百度开放平台申请的Android端API KEY" />
</application>
按教程还需要添加混淆
打开android 目录app下的build.gradle文件,在release代码块中添加如下内容。(默认情况下 flutter不会开启 Android 的混 淆)
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.debug
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
新建混淆文件
-keep class com.baidu.** {*;}
-keep class vi.com.** {*;}
-keep class com.baidu.vi.** {*;}
-dontwarn com.baidu.**
4.flutter中使用地图widget
百度地图的widget为BMFMapWidget
BMFMapWidget(
onBMFMapCreated: (controller) {
//自定义onBMFMapCreated方法,用于获取controller
onBMFMapCreated(controller);
},
mapOptions: BMFMapOptions(
center: BMFCoordinate(39.917215, 116.380341),
zoomLevel: 12,
mapPadding:
BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0)),
)
late BMFMapController _controller;
void onBMFMapCreated(BMFMapController controller) {
_controller = controller;
_controller.showUserLocation(true);
}
如果在widget中报错,找不到BMFMapOptions、BMFEdgeInsets等类,路径为
使用引入强行添加。可能原因是在android的BmfMapApplication没有正常引入,也可能是pubget获取的百度地图版本问题,更新最新的版本可能解决。
import 'package:flutter_baidu_mapapi_map/src/models/bmf_map_options.dart';
5.使用定位获取坐标,居中显示
声明所需的几个变量
late LocationFlutterPlugin _locationPlugin;
BaiduLocationIOSOption iosOption =
BaiduLocationIOSOption(coordType: BMFLocationCoordType.gcj02);
BaiduLocationAndroidOption androidOption =
BaiduLocationAndroidOption(coordType: BMFLocationCoordType.gcj02);
late BMFMapController _controller;
late BaiduLocation _location;
在initState中初始化。
注意
_locationPlugin.setAgreePrivacy(true);
这句话在百度教程没有,是否同意隐私协议的东西,不加上不会加载地图,神奇的玩意坑了我一下午
@override
void initState() {
_locationPlugin = LocationFlutterPlugin();
_locationPlugin.setAgreePrivacy(true);
_location = BaiduLocation();
_locationPlugin.seriesLocationCallback(callback: (BaiduLocation result) {
_location = result;
_stopLocation();
updatePosition();
});
_startLocation();
}
Android权限建议在androidmanifest配置单多添加一份。存储涉及的storage权限可能有坑,建议加上
// 申请权限
Future<bool> requestPermission() async {
// 申请权限
final status = await Permission.location.request();
//获取存储权限,可能涉及到地图的shareprefences读写问题,建议加上并进行判断
//await Permission.storage.status;
if (status.isGranted) {
print("定位权限申请通过");
return true;
} else {
print("定位权限申请不通过");
return false;
}
}
/// 停止定位
void _stopLocation() {
if (null != _locationPlugin) {
_locationPlugin.stopLocation();
}
}
// 开始定位
void _startLocation() {
if (null != _locationPlugin) {
//申请定位权限
requestPermission().then((value) => {
if (value)
{_setLocOption(), _locationPlugin.startLocation()}
else
{
EasyLoading.showToast("需要定位权限",
toastPosition:EasyLoadingToastPosition.bottom)
}
});
}
}
// 设置定位参数
void _setLocOption() {
androidOption.setCoorType("bd09ll"); // 设置返回的位置坐标系类型
androidOption.setIsNeedAltitude(true); // 设置是否需要返回海拔高度信息
androidOption.setIsNeedAddress(true); // 设置是否需要返回地址信息
androidOption.setIsNeedLocationPoiList(true); // 设置是否需要返回周边poi信息
androidOption.setIsNeedNewVersionRgc(true); // 设置是否需要返回最新版本rgc信息
androidOption.setIsNeedLocationDescribe(true); // 设置是否需要返回位置描述
androidOption.setOpenGps(true); // 设置是否需要使用gps
androidOption.setLocationMode(BMFLocationMode.hightAccuracy); // 设置定位模式
androidOption.setScanspan(1000); // 设置发起定位请求时间间隔
Map androidMap = androidOption.getMap();
Map iosdMap = iosOption.getMap();
_locationPlugin.prepareLoc(androidMap, iosdMap); //ios和安卓定位设置
}
//更新地点
void updatePosition() {
BMFCoordinate coordinate = BMFCoordinate(
_location.latitude ?? 39.917215, _location.longitude ?? 116.380341);
BMFMapOptions options = BMFMapOptions(
center: coordinate,
zoomLevel: 17,
mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0));
BMFLocation location = BMFLocation(
coordinate: coordinate,
altitude: 0,
horizontalAccuracy: 5,
verticalAccuracy: -1.0,
speed: -1.0,
course: -1.0);
BMFUserLocation userLocation = BMFUserLocation(
location: location,
);
_controller.updateLocationData(userLocation);
_controller.updateMapOptions(options);
}
申请权限、Toast等插件使用了如下插件。
dependencies:
#Tost
flutter_easyloading: ^3.0.3
#动态权限申请
permission_handler : ^6.0.2
分割线。大概踩坑结束,能够显示地图并且定位在附近地点,居中显示
最后贴一下全部代码作为参考
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart';
import 'package:flutter_baidu_mapapi_base/src/map/bmf_models.dart';
import 'package:flutter_baidu_mapapi_search/flutter_baidu_mapapi_search.dart';
import 'package:flutter_bmflocation/flutter_bmflocation.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:permission_handler/permission_handler.dart';
class MapRoute extends StatefulWidget {
const MapRoute({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return MapRouteState();
}
}
class MapRouteState extends State<MapRoute> {
late TextEditingController positionController;
late LocationFlutterPlugin _locationPlugin;
BaiduLocationIOSOption iosOption =
BaiduLocationIOSOption(coordType: BMFLocationCoordType.gcj02);
BaiduLocationAndroidOption androidOption =
BaiduLocationAndroidOption(coordType: BMFLocationCoordType.gcj02);
late BMFMapController _controller;
late BaiduLocation _location;
@override
void initState() {
positionController = TextEditingController();
_locationPlugin = LocationFlutterPlugin();
_locationPlugin.setAgreePrivacy(true);
_location = BaiduLocation();
_locationPlugin.seriesLocationCallback(callback: (BaiduLocation result) {
_location = result;
_stopLocation();
updatePosition();
});
_startLocation();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: const Text(
"选取地点",
style: TextStyle(color: Colors.black),
),
centerTitle: true,
backgroundColor: Colors.white,
actions: <Widget>[
MaterialButton(
child: const Text("确定", style: TextStyle(fontSize: 15)),
onPressed: () {},
)
],
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.arrow_back_ios, color: Colors.black)),
),
body: _map(),
);
}
Widget _map() {
return Container(
child: Column(
children: [
Container(
child: TextField(
controller: positionController,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
hintText: "请输入地点",
hintStyle: const TextStyle(color: Colors.grey, fontSize: 14),
suffix: Container(
child: MaterialButton(
child: const Text("确认"),
onPressed: () {
serchPosition(positionController.text);
},
),
))),
),
Expanded(
child: BMFMapWidget(
onBMFMapCreated: (controller) {
//自定义onBMFMapCreated方法,用于获取controller
onBMFMapCreated(controller);
},
mapOptions: BMFMapOptions(
center: BMFCoordinate(39.917215, 116.380341),
zoomLevel: 12,
mapPadding:
BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0)),
))
],
));
}
void onBMFMapCreated(BMFMapController controller) {
_controller = controller;
_controller.showUserLocation(true);
}
// 申请权限
Future<bool> requestPermission() async {
// 申请权限
final status = await Permission.location.request();
if (status.isGranted) {
print("定位权限申请通过");
return true;
} else {
print("定位权限申请不通过");
return false;
}
}
/// 停止定位
void _stopLocation() {
if (null != _locationPlugin) {
_locationPlugin.stopLocation();
}
}
// 开始定位
void _startLocation() {
if (null != _locationPlugin) {
//申请定位权限
requestPermission().then((value) => {
if (value)
{_setLocOption(), _locationPlugin.startLocation()}
else
{
EasyLoading.showToast("需要定位权限",
toastPosition:EasyLoadingToastPosition.bottom)
}
});
}
}
// 设置定位参数
void _setLocOption() {
androidOption.setCoorType("bd09ll"); // 设置返回的位置坐标系类型
androidOption.setIsNeedAltitude(true); // 设置是否需要返回海拔高度信息
androidOption.setIsNeedAddress(true); // 设置是否需要返回地址信息
androidOption.setIsNeedLocationPoiList(true); // 设置是否需要返回周边poi信息
androidOption.setIsNeedNewVersionRgc(true); // 设置是否需要返回最新版本rgc信息
androidOption.setIsNeedLocationDescribe(true); // 设置是否需要返回位置描述
androidOption.setOpenGps(true); // 设置是否需要使用gps
androidOption.setLocationMode(BMFLocationMode.hightAccuracy); // 设置定位模式
androidOption.setScanspan(1000); // 设置发起定位请求时间间隔
Map androidMap = androidOption.getMap();
Map iosdMap = iosOption.getMap();
_locationPlugin.prepareLoc(androidMap, iosdMap); //ios和安卓定位设置
}
Future<void> serchPosition(String text) async {
// 构造检索参数
BMFSuggestionSearchOption suggestionSearchOption =
BMFSuggestionSearchOption(
keyword: text, cityname: _location.city ?? "北京");
// 检索实例
BMFSuggestionSearch suggestionSearch = BMFSuggestionSearch();
// 检索回调
suggestionSearch.onGetSuggestSearchResult(callback:
(BMFSuggestionSearchResult result, BMFSearchErrorCode errorCode) {
print('sug检索回调 result = ${result.toMap()} \n errorCode = ${errorCode}');
// 解析reslut,具体参考demo
});
// 发起检索
bool flag = await suggestionSearch.suggestionSearch(suggestionSearchOption);
}
void updatePosition() {
BMFCoordinate coordinate = BMFCoordinate(
_location.latitude ?? 39.917215, _location.longitude ?? 116.380341);
BMFMapOptions options = BMFMapOptions(
center: coordinate,
zoomLevel: 17,
mapPadding: BMFEdgeInsets(left: 30, top: 0, right: 30, bottom: 0));
BMFLocation location = BMFLocation(
coordinate: coordinate,
altitude: 0,
horizontalAccuracy: 5,
verticalAccuracy: -1.0,
speed: -1.0,
course: -1.0);
BMFUserLocation userLocation = BMFUserLocation(
location: location,
);
_controller.updateLocationData(userLocation);
_controller.updateMapOptions(options);
}
}
————————————2022.7.7——————————————————————————