本节指标
- 百度地图业务
- 百度组件初始
- 编写定位代码 android 篇
环境
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 1.20.1, on Mac OS X 10.15.6 19G73, locale zh-Hans-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
[✓] Android Studio (version 4.0)
[✓] VS Code (version 1.47.3)
视频
https://www.bilibili.com/vide…
代码
https://github.com/ducafecat/…
能够间接用 ???? v1.0.3
https://github.com/ducafecat/…
注释
创立组件的几种形式
现成轮子间接用
- 官网仓库搜寻
https://pub.dev/flutter/packages
https://pub.flutter-io.cn/flu…
可参考的组件代码
- 通过仓库,查找 github 代码仓
- 网站、客服索取代码
参考官网集成文档编写组件
- 官网文档
http://lbsyun.baidu.com/index…
组件代码
百度利用治理,创立 AK
- 利用治理
https://lbsyun.baidu.com/apic…
- 查问 SHA1
http://lbsyun.baidu.com/index…
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey
- 设置 AK
example/android/app/src/main/AndroidManifest.xml
...
<!-- 在这里设置 android 端 ak-->
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="aCUtcLDufllGi4nEaKgU8FmBqufFyekh" />
</application>
</manifest>
设置 Android 权限
- 文档
http://lbsyun.baidu.com/index…
- android/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tech.ducafecat.flutter_baidu_plugin_ducafecat">
<!-- 这个权限用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
<!-- 这个权限用于拜访 GPS 定位 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<!-- 用于拜访 wifi 网络信息,wifi 信息会用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<!-- 获取运营商信息,用于反对提供运营商信息相干的接口 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!-- 这个权限用于获取 wifi 的获取权限,wifi 信息会用来进行网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<!-- 用于读取手机以后的状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<!-- 写入扩大存储,向扩展卡写入数据,用于写入离线定位数据 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!-- 拜访网络,网络定位须要上网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 读取零碎信息,蕴含零碎版本等信息,用作统计 -->
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<!-- 程序在手机屏幕敞开后后盾过程依然运行 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application>
<!-- 申明 service 组件 -->
<service
android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote" >
</service>
</application>
</manifest>
增加 android libs 库文件
- 目录 android/libs
- android/build.gradle
...
android {
compileSdkVersion 28
sourceSets {
main {jniLibs.srcDir 'libs'}
}
defaultConfig {minSdkVersion 16}
lintOptions {disable 'InvalidPackage'}
}
dependencies {implementation files('libs/BaiduLBS_Android.jar')
}
编写 Flutter 组件代码
- 目录 lib
- 地理信息 lib/entity/flutter_baidu_location.dart
/// 百度定位后果类,用于存储各类定位后果信息
class BaiduLocation {
/// 定位胜利工夫
final String locTime;
/// 定位后果类型
final int locType;
/// 半径
final double radius;
/// 纬度
final double latitude;
/// 经度
final double longitude;
/// 海拔
final double altitude;
/// 国家
final String country;
/// 省份
final String province;
/// 城市
final String city;
/// 区县
final String district;
/// 街道
final String street;
/// 地址
final String address;
/// 地位语义化形容,例如 "在百度大厦左近"
final String locationDetail;
/// 周边 poi 信息,每个 poi 之间用 "|" 隔开
final String poiList;
/// 定位后果回调工夫
final String callbackTime;
/// 错误码
final int errorCode;
/// 定位失败形容信息
final String errorInfo;
BaiduLocation(
{this.locTime,
this.locType,
this.radius,
this.latitude,
this.longitude,
this.altitude,
this.country,
this.province,
this.city,
this.district,
this.street,
this.address,
this.locationDetail,
this.poiList,
this.callbackTime,
this.errorCode,
this.errorInfo});
/// 依据传入的 map 生成 BaiduLocation 对象
factory BaiduLocation.fromMap(dynamic value) {
return new BaiduLocation(locTime: value['locTime'],
locType: value['locType'],
radius: value['radius'],
latitude: value['latitude'],
longitude: value['longitude'],
altitude: value['altitude'],
country: value['country'],
province: value['province'],
city: value['city'],
district: value['district'],
street: value['street'],
address: value['address'],
locationDetail: value['locationDetail'],
poiList: value['poiList'],
callbackTime: value['callbackTime'],
errorCode: value['errorCode'],
errorInfo: value['errorInfo'],
);
}
/// 获取对本类所有变量赋值后的 map 键值对
Map getMap() {
return {
"locTime": locTime,
"locType": locType,
"radius": radius,
"latitude": latitude,
"longitude": longitude,
"altitude": altitude,
"country": country,
"province": province,
"city": city,
"district": district,
"street": street,
"address": address,
"locationDescribe": locationDetail,
"poiList": poiList,
"callbackTime": callbackTime,
"errorCode": errorCode,
"errorInfo": errorInfo,
};
}
}
- android 配置项 lib/entity/flutter_baidu_location_android_option.dart
/// 设置 android 端定位参数类
class BaiduLocationAndroidOption {
/// 坐标系类型
String coorType;
/// 是否须要返回地址信息
bool isNeedAddres;
/// 是否须要返回海拔高度信息
bool isNeedAltitude;
/// 是否须要返回周边 poi 信息
bool isNeedLocationPoiList;
/// 是否须要返回新版本 rgc 信息
bool isNeedNewVersionRgc;
/// 是否须要返回地位形容信息
bool isNeedLocationDescribe;
/// 是否应用 gps
bool openGps;
/// 可选,设置发动定位申请的距离,int 类型,单位 ms
/// 如果设置为 0,则代表单次定位,即仅定位一次,默认为 0
/// 如果设置非 0,需设置 1000ms 以上才无效
int scanspan;
/// 设置定位模式,可选的模式有高精度、仅设施、仅网络。默认为高精度模式
int locationMode;
/// 可选,设置场景定位参数,包含签到场景、静止场景、出行场景
int locationPurpose;
/// 可选,设置返回经纬度坐标类型,默认 GCJ02
/// GCJ02:国测局坐标;/// BD09ll:百度经纬度坐标;/// BD09:百度墨卡托坐标;/// 海内地区定位,无需设置坐标类型,对立返回 WGS84 类型坐标
void setCoorType(String coorType) {this.coorType = coorType;}
/// 是否须要返回地址信息
void setIsNeedAddres(bool isNeedAddres) {this.isNeedAddres = isNeedAddres;}
/// 是否须要返回海拔高度信息
void setIsNeedAltitude(bool isNeedAltitude) {this.isNeedAltitude = isNeedAltitude;}
/// 是否须要返回周边 poi 信息
void setIsNeedLocationPoiList(bool isNeedLocationPoiList) {this.isNeedLocationPoiList = isNeedLocationPoiList;}
/// 是否须要返回地位形容信息
void setIsNeedLocationDescribe(bool isNeedLocationDescribe) {this.isNeedLocationDescribe = isNeedLocationDescribe;}
/// 是否须要返回新版本 rgc 信息
void setIsNeedNewVersionRgc(bool isNeedNewVersionRgc) {this.isNeedNewVersionRgc = isNeedNewVersionRgc;}
/// 是否应用 gps
void setOpenGps(bool openGps) {this.openGps = openGps;}
/// 可选,设置发动定位申请的距离,int 类型,单位 ms
/// 如果设置为 0,则代表单次定位,即仅定位一次,默认为 0
/// 如果设置非 0,需设置 1000ms 以上才无效
void setScanspan(int scanspan) {this.scanspan = scanspan;}
/// 设置定位模式,可选的模式有高精度、仅设施、仅网络,默认为高精度模式
void setLocationMode(LocationMode locationMode) {if (locationMode == LocationMode.Hight_Accuracy) {this.locationMode = 1;} else if (locationMode == LocationMode.Device_Sensors) {this.locationMode = 2;} else if (locationMode == LocationMode.Battery_Saving) {this.locationMode = 3;}
}
/// 可选,设置场景定位参数,包含签到场景、静止场景、出行场景
void setLocationPurpose(BDLocationPurpose locationPurpose) {if (locationPurpose == BDLocationPurpose.SignIn) {this.locationPurpose = 1;} else if (locationPurpose == BDLocationPurpose.Transport) {this.locationPurpose = 2;} else if (locationPurpose == BDLocationPurpose.Sport) {this.locationPurpose = 3;}
}
BaiduLocationAndroidOption(
{this.coorType,
this.isNeedAddres,
this.isNeedAltitude,
this.isNeedLocationPoiList,
this.isNeedNewVersionRgc,
this.openGps,
this.isNeedLocationDescribe,
this.scanspan,
this.locationMode,
this.locationPurpose});
/// 依据传入的 map 生成 BaiduLocationAndroidOption 对象
factory BaiduLocationAndroidOption.fromMap(dynamic value) {
return new BaiduLocationAndroidOption(coorType: value['coorType'],
isNeedAddres: value['isNeedAddres'],
isNeedAltitude: value['isNeedAltitude'],
isNeedLocationPoiList: value['isNeedLocationPoiList'],
isNeedNewVersionRgc: value['isNeedNewVersionRgc'],
openGps: value['openGps'],
isNeedLocationDescribe: value[''],
scanspan: value['scanspan'],
locationMode: value['locationMode'],
locationPurpose: value['LocationPurpose'],
);
}
/// 获取对本类所有变量赋值后的 map 键值对
Map getMap() {
return {
"coorType": coorType,
"isNeedAddres": isNeedAddres,
"isNeedAltitude": isNeedAltitude,
"isNeedLocationPoiList": isNeedLocationPoiList,
"isNeedNewVersionRgc": isNeedNewVersionRgc,
"openGps": openGps,
"isNeedLocationDescribe": isNeedLocationDescribe,
"scanspan": scanspan,
"locationMode": locationMode,
};
}
}
/// 定位模式枚举类
enum LocationMode {
/// 高精度模式
Hight_Accuracy,
/// 低功耗模式
Battery_Saving,
/// 仅设施 (Gps) 模式
Device_Sensors
}
/// 场景定位枚举类
enum BDLocationPurpose {
/// 签到场景
/// 只进行一次定位返回最靠近实在地位的定位后果(定位速度可能会提早 1 -3s)SignIn,
/// 出行场景
/// 高精度间断定位,实用于有户内外切换的场景,卫星定位和网络定位互相切换,卫星定位胜利之后网络定位不再返回,卫星信号断开之后一段时间才会返回网络后果
Sport,
/// 静止场景
/// 高精度间断定位,实用于有户内外切换的场景,卫星定位和网络定位互相切换,卫星定位胜利之后网络定位不再返回,卫星信号断开之后一段时间才会返回网络后果
Transport
}
- ios 配置项 lib/entity/flutter_baidu_location_ios_option.dart
/// 设置 ios 端定位参数类
class BaiduLocationIOSOption {
/// 设置地位获取超时工夫
int locationTimeout;
/// 设置获取地址信息超时工夫
int reGeocodeTimeout;
/// 设置利用地位类型
String activityType;
/// 设置返回地位的坐标系类型
String BMKLocationCoordinateType;
/// 设置预期精度参数
String desiredAccuracy;
/// 是否须要最新版本 rgc 数据
bool isNeedNewVersionRgc;
/// 指定定位是否会被零碎主动暂停
bool pausesLocationUpdatesAutomatically;
/// 指定是否容许后盾定位
bool allowsBackgroundLocationUpdates;
/// 设定定位的最小更新间隔
double distanceFilter;
/// 指定是否容许后盾定位
/// allowsBackgroundLocationUpdates 为 true 则容许后盾定位
/// allowsBackgroundLocationUpdates 为 false 则不容许后盾定位
void setAllowsBackgroundLocationUpdates(bool allowsBackgroundLocationUpdates) {this.allowsBackgroundLocationUpdates = allowsBackgroundLocationUpdates;}
/// 指定定位是否会被零碎主动暂停
/// pausesLocationUpdatesAutomatically 为 true 则定位会被零碎主动暂停
/// pausesLocationUpdatesAutomatically 为 false 则定位不会被零碎主动暂停
void setPauseLocUpdateAutomatically(bool pausesLocationUpdatesAutomatically) {
this.pausesLocationUpdatesAutomatically =
pausesLocationUpdatesAutomatically;
}
/// 设置地位获取超时工夫
void setLocationTimeout(int locationTimeout) {this.locationTimeout = locationTimeout;}
/// 设置获取地址信息超时工夫
void setReGeocodeTimeout(int reGeocodeTimeout) {this.reGeocodeTimeout = reGeocodeTimeout;}
/// 设置利用地位类型
/// activityType 可选值包含:
/// "CLActivityTypeOther"
/// "CLActivityTypeAutomotiveNavigation"
/// "CLActivityTypeFitness"
/// "CLActivityTypeOtherNavigation"
void setActivityType(String activityType) {this.activityType = activityType;}
/// 设置返回地位的坐标系类型
/// BMKLocationCoordinateType 可选值包含:
/// "BMKLocationCoordinateTypeBMK09LL"
/// "BMKLocationCoordinateTypeBMK09MC"
/// "BMKLocationCoordinateTypeWGS84"
/// "BMKLocationCoordinateTypeGCJ02"
void setBMKLocationCoordinateType(String BMKLocationCoordinateType) {this.BMKLocationCoordinateType = BMKLocationCoordinateType;}
/// 设置预期精度参数
/// desiredAccuracy 可选值包含:
/// "kCLLocationAccuracyBest"
/// "kCLLocationAccuracyNearestTenMeters"
/// "kCLLocationAccuracyHundredMeters"
/// "kCLLocationAccuracyKilometer"
void setDesiredAccuracy(String desiredAccuracy) {this.desiredAccuracy = desiredAccuracy;}
/// 设定定位的最小更新间隔
void setDistanceFilter(double distanceFilter) {this.distanceFilter = distanceFilter;}
/// 是否须要最新版本 rgc 数据
/// isNeedNewVersionRgc 为 true 则须要返回最新版本 rgc 数据
/// isNeedNewVersionRgc 为 false 则不须要返回最新版本 rgc 数据
void setIsNeedNewVersionRgc(bool isNeedNewVersionRgc) {this.isNeedNewVersionRgc = isNeedNewVersionRgc;}
BaiduLocationIOSOption(
{this.locationTimeout,
this.reGeocodeTimeout,
this.activityType,
this.BMKLocationCoordinateType,
this.desiredAccuracy,
this.isNeedNewVersionRgc,
this.pausesLocationUpdatesAutomatically,
this.allowsBackgroundLocationUpdates,
this.distanceFilter});
/// 依据传入的 map 生成 BaiduLocationIOSOption 对象
factory BaiduLocationIOSOption.fromMap(dynamic value) {
return new BaiduLocationIOSOption(locationTimeout: value['locationTimeout'],
reGeocodeTimeout: value['reGeocodeTimeout'],
activityType: value['activityType'],
BMKLocationCoordinateType: value['BMKLocationCoordinateType'],
desiredAccuracy: value['desiredAccuracy'],
isNeedNewVersionRgc: value['isNeedNewVersionRgc'],
pausesLocationUpdatesAutomatically:
value['pausesLocationUpdatesAutomatically'],
allowsBackgroundLocationUpdates: value['allowsBackgroundLocationUpdates'],
distanceFilter: value['distanceFilter'],
);
}
/// 获取对本类所有变量赋值后的 map 键值对
Map getMap() {
return {
"locationTimeout": locationTimeout,
"reGeocodeTimeout": reGeocodeTimeout,
"activityType": activityType,
"BMKLocationCoordinateType": BMKLocationCoordinateType,
"desiredAccuracy": desiredAccuracy,
"isNeedNewVersionRgc": isNeedNewVersionRgc,
"pausesLocationUpdatesAutomatically": pausesLocationUpdatesAutomatically,
"allowsBackgroundLocationUpdates": allowsBackgroundLocationUpdates,
"distanceFilter": distanceFilter,
};
}
}
- 接口 lib/flutter_baidu_plugin_ducafecat.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
class FlutterBaiduPluginDucafecat {
/// flutter 端被动调用原生端办法
static const MethodChannel _channel =
const MethodChannel('flutter_baidu_plugin_ducafecat');
/// 原生端被动回传后果数据到 flutter 端
static const EventChannel _stream =
const EventChannel("flutter_baidu_plugin_ducafecat_stream");
/// ios 下设置 key
/// android 在 AndroidManifest.xml 中设置
static Future<bool> setApiKey(String key) async {return await _channel.invokeMethod("setApiKey", key);
}
/// 设置定位参数
void prepareLoc(Map androidMap, Map iosMap) {
Map map;
if (Platform.isAndroid) {map = androidMap;} else {map = iosMap;}
_channel.invokeMethod("updateOption", map);
return;
}
/// 启动定位
void startLocation() {_channel.invokeMethod('startLocation');
return;
}
/// 进行定位
void stopLocation() {_channel.invokeMethod('stopLocation');
return;
}
/// 原生端回传键值对 map 到 flutter 端
/// map 中 key 为 isInChina 对应的 value,如果为 1 则判断是在国内,为 0 则判断是在国外
/// map 中存在 key 为 nearby 则判断为已达到设置监听地位左近
Stream<Map<String, Object>> onResultCallback() {
Stream<Map<String, Object>> _resultMap;
if (_resultMap == null) {_resultMap = _stream.receiveBroadcastStream().map<Map<String, Object>>((element) => element.cast<String, Object>());
}
return _resultMap;
}
}
触发 registerWith 的形式,老我的项目
// This static function is optional and equivalent to onAttachedToEngine. It supports the old
// pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
// plugin registration via this function while apps migrate to use the new Android APIs
// post-flutter-1.12 via https://flutter.dev/go/androi…
- android 组件代码
android/src/main/java/tech/ducafecat/flutter_baidu_plugin_ducafecat/FlutterBaiduPluginDucafecatPlugin.java
public static void registerWith(Registrar registrar) {......}
- example android 注册组件
example/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
public final class GeneratedPluginRegistrant {public static void registerWith(PluginRegistry registry) {if (alreadyRegisteredWith(registry)) {return;}
LocationFlutterPlugin.registerWith(registry.registrarFor("com.baidu.bdmap_location_flutter_plugin.LocationFlutterPlugin"));
PermissionHandlerPlugin.registerWith(registry.registrarFor("com.baseflow.permissionhandler.PermissionHandlerPlugin"));
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {return true;}
registry.registrarFor(key);
return false;
}
}
成员变量、同步、异步解决
MethodChannel 申请办法后,同步返回后果
EventChannel 组件被动推音讯到 Flutter
- android/src/main/java/tech/ducafecat/flutter_baidu_plugin_ducafecat/FlutterBaiduPluginDucafecatPlugin.java
用到的成员变量先定义下
public class FlutterBaiduPluginDucafecatPlugin implements FlutterPlugin, MethodCallHandler, EventChannel.StreamHandler {
// 通道名称
private static final String CHANNEL_METHOD_LOCATION = "flutter_baidu_plugin_ducafecat";
private static final String CHANNEL_STREAM_LOCATION = "flutter_baidu_plugin_ducafecat_stream";
private Context mContext = null; // flutter view context
private LocationClient mLocationClient = null; // 定位对象
private EventChannel.EventSink mEventSink = null; // 事件对象
private BDNotifyListener mNotifyListener; // 地位揭示对象
private boolean isPurporseLoc = false; // 签到场景
private boolean isInChina = false; // 是否启用国内外地位判断性能
private boolean isNotify = false; // 地位揭示
// 通道对象
private MethodChannel channel = null;
private EventChannel eventChannel = null;
组件生命周期
- 文件
android/src/main/java/tech/ducafecat/flutter_baidu_plugin_ducafecat/FlutterBaiduPluginDucafecatPlugin.java
- 组件注册 onAttachedToEngine
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {this.mContext = flutterPluginBinding.getApplicationContext();
/**
* 开始、进行定位
*/
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL_METHOD_LOCATION);
channel.setMethodCallHandler(this);
/**
* 监听地位变动
*/
eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL_STREAM_LOCATION);
eventChannel.setStreamHandler(this);
}
- 老我的项目 组件注册 registerWith
public static void registerWith(Registrar registrar) {FlutterBaiduPluginDucafecatPlugin plugin = new FlutterBaiduPluginDucafecatPlugin();
plugin.mContext = registrar.context();
/**
* 开始、进行定位
*/
final MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL_METHOD_LOCATION);
channel.setMethodCallHandler(plugin);
/**
* 监听地位变动
*/
final EventChannel eventChannel = new EventChannel(registrar.messenger(), CHANNEL_STREAM_LOCATION);
eventChannel.setStreamHandler(plugin);
// final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_baidu_plugin_ducafecat");
// channel.setMethodCallHandler(new FlutterBaiduPluginDucafecatPlugin());
}
- 登记组件 onCancel
@Override
public void onCancel(Object arguments) {stopLocation();
if (isNotify) {if (null != mLocationClient) {mLocationClient.removeNotifyEvent(mNotifyListener);
}
mNotifyListener = null;
}
}
- 销毁组件 onDetachedFromEngine
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {channel.setMethodCallHandler(null);
eventChannel.setStreamHandler(null);
}
- 办法调用 onMethodCall
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {if ("startLocation".equals(call.method)) {startLocation(); // 启动定位
} else if ("stopLocation".equals(call.method)) {stopLocation(); // 进行定位
} else if("updateOption".equals(call.method)) { // 设置定位参数
try {updateOption((Map) call.arguments);
} catch (Exception e) {e.printStackTrace();
}
} else if (("getPlatformVersion").equals(call.method)) {result.success("Android" + android.os.Build.VERSION.RELEASE);
} else {result.notImplemented();
}
}
- flutter onListen 回调对象
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {mEventSink = events;}
地图业务参数
- 更新参数 updateOption
/**
* 筹备定位
* @param arguments
*/
private void updateOption(Map arguments) {if (null == mLocationClient) {mLocationClient = new LocationClient(mContext);
}
// 判断是否启用地位揭示性能
if (arguments.containsKey("isNotify")) {
isNotify = true;
if (null == mNotifyListener) {mNotifyListener = new MyNotifyLister();
}
mLocationClient.registerNotify(mNotifyListener);
double lat = 0;
double lon = 0;
float radius = 0;
if (arguments.containsKey("latitude")) {lat = (double)arguments.get("latitude");
}
if (arguments.containsKey("longitude")) {lon = (double)arguments.get("longitude");
}
if (arguments.containsKey("radius")) {double radius1 = (double)arguments.get("radius");
radius = Float.parseFloat(String.valueOf(radius1));
}
String coorType = mLocationClient.getLocOption().getCoorType();
mNotifyListener.SetNotifyLocation(lat, lon, radius, coorType);
return;
} else {isNotify = false;}
mLocationClient.registerLocationListener(new CurrentLocationListener());
// 判断是否启用国内外地位判断性能
if (arguments.containsKey("isInChina")) {
isInChina = true;
return;
} else {isInChina =false;}
LocationClientOption option = new LocationClientOption();
parseOptions(option, arguments);
option.setProdName("flutter");
mLocationClient.setLocOption(option);
}
- 解析定位参数 parseOptions
/**
* 解析定位参数
* @param option
* @param arguments
*/
private void parseOptions(LocationClientOption option,Map arguments) {if (arguments != null) {
// 可选,设置是否返回逆天文地址信息。默认是 true
if (arguments.containsKey("isNeedAddres")) {if (((boolean)arguments.get("isNeedAddres"))) {option.setIsNeedAddress(true);
} else {option.setIsNeedAddress(false);
}
}
// 可选,设置定位模式,可选的模式有高精度、仅设施、仅网络。默认为高精度模式
if (arguments.containsKey("locationMode")) {if (((int)arguments.get("locationMode")) == 1) {option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); // 高精度模式
} else if (((int)arguments.get("locationMode")) == 2) {option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors); // 仅设施模式
} else if (((int)arguments.get("locationMode")) == 3) {option.setLocationMode(LocationClientOption.LocationMode.Battery_Saving); // 仅网络模式
}
}
// 可选,设置场景定位参数,包含签到场景、静止场景、出行场景
if ((arguments.containsKey("LocationPurpose"))) {
isPurporseLoc = true;
if (((int)arguments.get("LocationPurpose")) == 1) {option.setLocationPurpose(LocationClientOption.BDLocationPurpose.SignIn); // 签到场景
} else if (((int)arguments.get("LocationPurpose")) == 2) {option.setLocationPurpose(LocationClientOption.BDLocationPurpose.Transport); // 静止场景
} else if (((int)arguments.get("LocationPurpose")) == 3) {option.setLocationPurpose(LocationClientOption.BDLocationPurpose.Sport); // 出行场景
}
} else {isPurporseLoc = false;}
// 可选,设置须要返回海拔高度信息
if (arguments.containsKey("isNeedAltitude")) {if (((boolean)arguments.get("isNeedAltitude"))) {option.setIsNeedAddress(true);
} else {option.setIsNeedAltitude(false);
}
}
// 可选,设置是否应用 gps,默认 false
if (arguments.containsKey("openGps")) {if(((boolean)arguments.get("openGps"))) {option.setOpenGps(true);
} else {option.setOpenGps(false);
}
}
// 可选,设置是否容许返回逆天文地址信息,默认是 true
if (arguments.containsKey("isNeedLocationDescribe")) {if(((boolean)arguments.get("isNeedLocationDescribe"))) {option.setIsNeedLocationDescribe(true);
} else {option.setIsNeedLocationDescribe(false);
}
}
// 可选,设置发动定位申请的距离,int 类型,单位 ms
// 如果设置为 0,则代表单次定位,即仅定位一次,默认为 0
// 如果设置非 0,需设置 1000ms 以上才无效
if (arguments.containsKey("scanspan")) {option.setScanSpan((int)arguments.get("scanspan"));
}
// 可选,设置返回经纬度坐标类型,默认 GCJ02
// GCJ02:国测局坐标;// BD09ll:百度经纬度坐标;// BD09:百度墨卡托坐标;// 海内地区定位,无需设置坐标类型,对立返回 WGS84 类型坐标
if (arguments.containsKey("coorType")) {option.setCoorType((String)arguments.get("coorType"));
}
// 设置是否须要返回左近的 poi 列表
if (arguments.containsKey("isNeedLocationPoiList")) {if (((boolean)arguments.get("isNeedLocationPoiList"))) {option.setIsNeedLocationPoiList(true);
} else {option.setIsNeedLocationPoiList(false);
}
}
// 设置是否须要最新版本 rgc 数据
if (arguments.containsKey("isNeedNewVersionRgc")) {if (((boolean)arguments.get("isNeedNewVersionRgc"))) {option.setIsNeedLocationPoiList(true);
} else {option.setIsNeedLocationPoiList(false);
}
}
}
}
编写启动、进行性能
- 开始定位
private void startLocation() {if(null != mLocationClient) {mLocationClient.start();
}
}
- 进行定位
private void stopLocation() {if (null != mLocationClient) {mLocationClient.stop();
mLocationClient = null;
}
}
百度定位回调
- CurrentLocationListener
/**
* 格式化工夫
*
* @param time
* @param strPattern
* @return
*/
private String formatUTC(long time, String strPattern) {if (TextUtils.isEmpty(strPattern)) {strPattern = "yyyy-MM-dd HH:mm:ss";}
SimpleDateFormat sdf = null;
try {sdf = new SimpleDateFormat(strPattern, Locale.CHINA);
sdf.applyPattern(strPattern);
} catch (Throwable e) {e.printStackTrace();
}
return sdf == null ? "NULL" : sdf.format(time);
}
class CurrentLocationListener extends BDAbstractLocationListener {
@Override
public void onReceiveLocation(BDLocation bdLocation) {if (null == mEventSink) {return;}
Map<String, Object> result = new LinkedHashMap<>();
// 判断国内外获取后果
if (isInChina) {if (bdLocation.getLocationWhere() == BDLocation.LOCATION_WHERE_IN_CN) {result.put("isInChina", 1); // 在国内
} else {result.put("isInChina", 0); // 在国外
}
mEventSink.success(result);
return;
}
// 场景定位获取后果
if (isPurporseLoc) {result.put("latitude", bdLocation.getLatitude()); // 纬度
result.put("longitude", bdLocation.getLongitude()); // 经度
mEventSink.success(result);
return;
}
result.put("callbackTime", formatUTC(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"));
if (null != bdLocation) {if (bdLocation.getLocType() == BDLocation.TypeGpsLocation
|| bdLocation.getLocType() == BDLocation.TypeNetWorkLocation
|| bdLocation.getLocType() == BDLocation.TypeOffLineLocation) {result.put("locType", bdLocation.getLocType()); // 定位后果类型
result.put("locTime", bdLocation.getTime()); // 定位胜利工夫
result.put("latitude", bdLocation.getLatitude()); // 纬度
result.put("longitude", bdLocation.getLongitude()); // 经度
if (bdLocation.hasAltitude()) {result.put("altitude", bdLocation.getAltitude()); // 高度
}
result.put("radius", Double.parseDouble(String.valueOf(bdLocation.getRadius()))); // 定位精度
result.put("country", bdLocation.getCountry()); // 国家
result.put("province", bdLocation.getProvince()); // 省份
result.put("city", bdLocation.getCity()); // 城市
result.put("district", bdLocation.getDistrict()); // 区域
result.put("town", bdLocation.getTown()); // 城镇
result.put("street", bdLocation.getStreet()); // 街道
result.put("address", bdLocation.getAddrStr()); // 地址
result.put("locationDetail", bdLocation.getLocationDescribe()); // 地位语义化形容
if (null != bdLocation.getPoiList() && !bdLocation.getPoiList().isEmpty()) {List<Poi> pois = bdLocation.getPoiList();
StringBuilder stringBuilder = new StringBuilder();
if (pois.size() == 1) {stringBuilder.append(pois.get(0).getName()).append(",").append(pois.get(0).getTags())
.append(pois.get(0).getAddr());
} else {for (int i = 0; i < pois.size() - 1; i++) {stringBuilder.append(pois.get(i).getName()).append(",").append(pois.get(i).getTags())
.append(pois.get(i).getAddr()).append("|");
}
stringBuilder.append(pois.get(pois.size()-1).getName()).append(",").append(pois.get(pois.size()-1).getTags())
.append(pois.get(pois.size()-1).getAddr());
}
result.put("poiList",stringBuilder.toString()); // 周边 poi 信息
//
}
if (bdLocation.getFloor() != null) {
// 以后反对高精度室内定位
String buildingID = bdLocation.getBuildingID();// 百度外部建筑物 ID
String buildingName = bdLocation.getBuildingName();// 百度外部建筑物缩写
String floor = bdLocation.getFloor();// 室内定位的楼层信息,如 f1,f2,b1,b2
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(buildingID).append("-").append(buildingName).append("-").append(floor);
result.put("indoor", stringBuilder.toString()); // 室内定位后果信息
mLocationClient.startIndoorMode();// 开启室内定位模式(反复调用也没问题),开启后,定位 SDK 会交融各种定位信息(GPS,WI-FI,蓝牙,传感器等)间断平滑的输入定位后果;} else {mLocationClient.stopIndoorMode(); // 处于室外则敞开室内定位模式
}
} else {result.put("errorCode", bdLocation.getLocType()); // 定位后果错误码
result.put("errorInfo", bdLocation.getLocTypeDescription()); // 定位失败形容信息
}
} else {result.put("errorCode", -1);
result.put("errorInfo", "location is null");
}
mEventSink.success(result); // android 端实时检测地位变动,将地位后果发送到 flutter 端
}
}
地位揭示服务
public class MyNotifyLister extends BDNotifyListener {
// 已达到设置监听地位左近
public void onNotify(BDLocation mlocation, float distance){if (null == mEventSink) {return;}
Map<String, Object> result = new LinkedHashMap<>();
result.put("nearby", "已达到设置监听地位左近"); // 1 为曾经达到 0 为未达到
mEventSink.success(result);
}
}
Example 代码
动静受权
- example/pubspec.yaml
dependencies:
flutter:
sdk: flutter
...
permission_handler: ^5.0.1+1
- example/lib/main.dart
class _MyAppState extends State<MyApp> {
@override
void initState() {super.initState();
_requestPermission(); // 执行权限申请}
// 动静申请定位权限
Future<bool> _requestPermission() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.location,
Permission.storage,
].request();
return statuses[Permission.location].isGranted &&
statuses[Permission.storage].isGranted;
}
}
主界面代码
- example/lib/main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_baidu_plugin_ducafecat/flutter_baidu_plugin_ducafecat.dart';
import 'package:flutter_baidu_plugin_ducafecat_example/views/location-view.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState() {super.initState();
_requestPermission(); // 执行权限申请
if (Platform.isIOS == true) {
FlutterBaiduPluginDucafecat.setApiKeyForIOS("dkYT07blcAj3drBbcN1eGFYqt16HP1pR");
}
}
@override
void dispose() {super.dispose();
}
// 动静申请定位权限
Future<bool> _requestPermission() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.location,
Permission.storage,
].request();
return statuses[Permission.location].isGranted &&
statuses[Permission.storage].isGranted;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {"location_view": (context) => LocationView(),},
home: MyHome(),);
}
}
class MyHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text('地图插件')),
body: SingleChildScrollView(
child: Column(
children: [
ListTile(title: Text('定位信息'),
subtitle: Text('点击开始后,百度地图实时推送经纬度信息'),
leading: Icon(Icons.location_searching),
trailing: Icon(Icons.keyboard_arrow_right),
onTap: () {Navigator.pushNamed(context, "location_view");
},
)
],
),
),
);
}
}
定位服务代码
- example/lib/views/location-view.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_baidu_plugin_ducafecat/entity/flutter_baidu_location.dart';
import 'package:flutter_baidu_plugin_ducafecat/entity/flutter_baidu_location_android_option.dart';
import 'package:flutter_baidu_plugin_ducafecat/entity/flutter_baidu_location_ios_option.dart';
import 'package:flutter_baidu_plugin_ducafecat/flutter_baidu_plugin_ducafecat.dart';
class LocationView extends StatefulWidget {LocationView({Key key}) : super(key: key);
@override
_LocationViewState createState() => _LocationViewState();
}
class _LocationViewState extends State<LocationView> {FlutterBaiduPluginDucafecat _locationPlugin = FlutterBaiduPluginDucafecat();
StreamSubscription<Map<String, Object>> _locationListener; // 事件监听
BaiduLocation _baiduLocation; // 经纬度信息
// Map<String, Object> _loationResult; // 返回格局数据
@override
void dispose() {super.dispose();
// 勾销监听
if (null != _locationListener) {_locationListener.cancel();
}
}
// 返回定位信息
void _setupListener() {if (_locationListener != null) {return;}
_locationListener =
_locationPlugin.onResultCallback().listen((Map<String, Object> result) {setState(() {
// _loationResult = result;
try {_baiduLocation = BaiduLocation.fromMap(result);
print(_baiduLocation);
} catch (e) {print(e);
}
});
});
}
// 设置 android 端和 ios 端定位参数
void _setLocOption() {
// android 端设置定位参数
BaiduLocationAndroidOption androidOption = new BaiduLocationAndroidOption();
androidOption.setCoorType("bd09ll"); // 设置返回的地位坐标系类型
androidOption.setIsNeedAltitude(true); // 设置是否须要返回海拔高度信息
androidOption.setIsNeedAddres(true); // 设置是否须要返回地址信息
androidOption.setIsNeedLocationPoiList(true); // 设置是否须要返回周边 poi 信息
androidOption.setIsNeedNewVersionRgc(true); // 设置是否须要返回最新版本 rgc 信息
androidOption.setIsNeedLocationDescribe(true); // 设置是否须要返回地位形容
androidOption.setOpenGps(true); // 设置是否须要应用 gps
androidOption.setLocationMode(LocationMode.Hight_Accuracy); // 设置定位模式
androidOption.setScanspan(1000); // 设置发动定位申请工夫距离
Map androidMap = androidOption.getMap();
// ios 端设置定位参数
BaiduLocationIOSOption iosOption = new BaiduLocationIOSOption();
iosOption.setIsNeedNewVersionRgc(true); // 设置是否须要返回最新版本 rgc 信息
iosOption.setBMKLocationCoordinateType("BMKLocationCoordinateTypeBMK09LL"); // 设置返回的地位坐标系类型
iosOption.setActivityType("CLActivityTypeAutomotiveNavigation"); // 设置利用地位类型
iosOption.setLocationTimeout(10); // 设置地位获取超时工夫
iosOption.setDesiredAccuracy("kCLLocationAccuracyBest"); // 设置预期精度参数
iosOption.setReGeocodeTimeout(10); // 设置获取地址信息超时工夫
iosOption.setDistanceFilter(100); // 设置定位最小更新间隔
iosOption.setAllowsBackgroundLocationUpdates(true); // 是否容许后盾定位
iosOption.setPauseLocUpdateAutomatically(true); // 定位是否会被零碎主动暂停
Map iosMap = iosOption.getMap();
_locationPlugin.prepareLoc(androidMap, iosMap);
}
// 启动定位
void _handleStartLocation() {if (null != _locationPlugin) {_setupListener();
_setLocOption();
_locationPlugin.startLocation();}
}
// 进行定位
void _handleStopLocation() {if (null != _locationPlugin) {_locationPlugin.stopLocation();
setState(() {_baiduLocation = null;});
}
}
////////////////////////////////////////////////////////////
// 显示地理信息
Widget _buildLocationView() {
return _baiduLocation != null
? Table(
children: [
TableRow(children: [TableCell(child: Text('经度')),
TableCell(child: Text(_baiduLocation.longitude.toString())),
]),
TableRow(children: [TableCell(child: Text('纬度')),
TableCell(child: Text(_baiduLocation.latitude.toString())),
]),
TableRow(children: [TableCell(child: Text('国家')),
TableCell(
child: Text(_baiduLocation.country != null
? _baiduLocation.country
: "")),
]),
TableRow(children: [TableCell(child: Text('省份')),
TableCell(
child: Text(_baiduLocation.province != null
? _baiduLocation.province
: "")),
]),
TableRow(children: [TableCell(child: Text('城市')),
TableCell(
child: Text(_baiduLocation.city != null
? _baiduLocation.city
: "")),
]),
TableRow(children: [TableCell(child: Text('区县')),
TableCell(
child: Text(_baiduLocation.district != null
? _baiduLocation.district
: "")),
]),
TableRow(children: [TableCell(child: Text('街道')),
TableCell(
child: Text(_baiduLocation.street != null
? _baiduLocation.street
: "")),
]),
TableRow(children: [TableCell(child: Text('地址')),
TableCell(
child: Text(_baiduLocation.address != null
? _baiduLocation.address
: "")),
]),
TableRow(children: [TableCell(child: Text('地位语义化形容')),
TableCell(
child: Text(_baiduLocation.locationDetail != null
? _baiduLocation.locationDetail
: "")),
]),
TableRow(children: [TableCell(child: Text('周边 poi 信息')),
TableCell(
child: Text(_baiduLocation.poiList != null
? _baiduLocation.poiList
: "")),
]),
TableRow(children: [TableCell(child: Text('错误码')),
TableCell(
child: Text(_baiduLocation.errorCode != null
? _baiduLocation.errorCode.toString()
: "")),
]),
TableRow(children: [TableCell(child: Text('定位失败形容信息')),
TableCell(
child: Text(_baiduLocation.errorInfo != null
? _baiduLocation.errorInfo
: "")),
]),
],
)
: Container();}
// 控制面板
Widget _buildControlPlan() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
color: Colors.blue,
textColor: Colors.white,
onPressed: _baiduLocation == null ? _handleStartLocation : null,
child: Text('开始定位'),
),
MaterialButton(
color: Colors.blue,
textColor: Colors.white,
onPressed: _baiduLocation != null ? _handleStopLocation : null,
child: Text('暂停定位'),
)
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('定位信息'),
),
body: SingleChildScrollView(
child: Column(
children: [_buildControlPlan(),
Divider(),
_buildLocationView(),],
),
),
);
}
}
参考
© 猫哥
https://ducafecat.tech
https://ducafecat.gitee.io