关于flutter:Flutter-混合开发-02-百度地图定位功能-android-篇

7次阅读

共计 31636 个字符,预计需要花费 80 分钟才能阅读完成。

本节指标

  • 百度地图业务
  • 百度组件初始
  • 编写定位代码 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

正文完
 0