【乔悟空】Flutter百度地图插件开源实现(一)定位功能

    第一次写这个东西!!有点激动和小紧张……

    废话不多说了,开始进入正题,这篇文章的正题是实现Flutter百度地图的定位功能,因为目前(2019.7.16)还没有一篇文章可以很详细的说明白这个功能到底是怎么实现的,所以希望这篇文章可以帮到大家!

前提:

1、已经有Flutter开发环境

2、有想学习的心思~

开始操作:

一、百度地图官网下载定位功能API (⁎⁍̴̛ᴗ⁍̴̛⁎)传送门

image

小伙伴们可以自行选择下载示例代码学习!不过我在教程里就会跟大家分享有关知识的(◐‿◑)

二、创建一个FlutterPlugin文件

进行Flutter插件制作推荐使用AndroidStudio,VSCode做这个还是差点事~

三、将第一步下载得到的API解压后文件夹中的libs文件放到新建的插件文件夹根目录下android文件夹下

1、移动libs文件到新建的插件文件夹根目录下android文件夹下

image
image
image

2、将libs文件夹下jar包添加到库,具体操作如下:

在AndroidStudio中android/libs文件目录下BaiduLBS_Android.jar文件右键单击,点击弹出菜单中的Add As Library,在新弹出窗口直接点OK就OK🥳具体操作截图如下⬇️

image

四、将AndroidStudio从FLutter项目切换到专业Android模式

1、在插件项目名右键点击,按照下图依次点击进入专业Android模式

image

2、进入专业模式后,点击左上角将Android模式转换为Project模式

image

3、转换后文件结构如下:

image

五、整个项目的文件结构如下

image

六、文件代码及详解
1、FlutterPlugin


**package com.qwk.flutter_plugin;** 

import android.app.Activity;

import com.baidu.location.BDAbstractLocationListener;

import com.baidu.location.BDLocation;

import com.baidu.location.LocationClient;

import java.util.HashMap;

import java.util.Map;

import io.flutter.plugin.common.MethodCall;

import io.flutter.plugin.common.MethodChannel;

import io.flutter.plugin.common.MethodChannel.MethodCallHandler;

import io.flutter.plugin.common.MethodChannel.Result;

import io.flutter.plugin.common.PluginRegistry.Registrar;

/** FlutterBaiduMapPlugin */

public class FlutterPluginimplements MethodCallHandler {

/** Plugin registration. */

  public static void registerWith(Registrar registrar) {

final MethodChannel channel =new MethodChannel(registrar.messenger(), "baidumap");

    channel.setMethodCallHandler(new FlutterPlugin(registrar.activity(),channel));

  }

private Activityactivity;

  private MethodChannelchannel;

  private LocationClientmLocationClient =null;

  private BDAbstractLocationListenermListener;

  public LocationServicelocationService ;

  public LocationActivitylocationActivity ;

  public FlutterPlugin(Activity activity, MethodChannel channel) {

this.activity = activity;

    this.channel = channel;

  }

@Override

  public void onMethodCall(MethodCall call, Result result) {

locationService =new LocationService(activity.getApplicationContext());

    locationActivity=new LocationActivity(locationService,new CurrentLocationListener(result));

    if (call.method.equals("getLocal")){

locationActivity.onStart();

    }else {

result.notImplemented();

    }

}

Maplocation2map(BDLocation location){

Map json =new HashMap<>();

    json.put("latitude",location.getLatitude());    //获取纬度信息

    json.put("longitude",location.getLongitude());    //获取经度信息

    json.put("country",location.getCountry());    //获取国家

    json.put("countryCode", location.getCountryCode());

    json.put("province",location.getProvince());    //获取省份

    json.put("city",location.getCity());    //获取城市

    json.put("cityCode", location.getCityCode());

    json.put("district",location.getDistrict());    //获取区县

    json.put("street",location.getStreet());    //获取街道信息

    json.put("locationDescribe",location.getLocationDescribe());    //获取位置描述信息

    json.put("adCode",location.getAdCode());    //获取城市adcode

    json.put("isInChina",location.getLocationWhere() == BDLocation.LOCATION_WHERE_IN_CN);

    json.put("errorCode",location.getLocType());

    //获取定位类型、定位错误返回码,具体信息可参照类参考中BDLocation类中的说明

    return json;

  }

/**

* 实现定位回调

*/

  class CurrentLocationListenerextends BDAbstractLocationListener {

Resultresult;

    CurrentLocationListener(Result result) {

this.result = result;

    }

@Override

    public synchronized void onReceiveLocation(BDLocation location) {

if (location ==null) {

return;

      }

try {

if(result!=null){

result.success((location2map(location)));

        }

}finally {

locationActivity.onStop();

        result =null;

      }

}

}

} 

2、LocationActivity

package com.qwk.flutter_plugin;

import com.baidu.location.BDAbstractLocationListener;

/***
 * 单点定位示例,用来展示基本的定位结果,配置在LocationService.java中
 * 默认配置也可以在LocationService中修改
 * 默认配置的内容自于开发者论坛中对开发者长期提出的疑问内容
 *
 * @author baidu
 *
 */
public class LocationActivity {
    private LocationService locationService;
    private BDAbstractLocationListener mListener;

    public LocationActivity(LocationService locationService, FlutterPlugin.CurrentLocationListener mListener) {
        this.locationService = locationService;
        this.mListener = mListener;
    }


    /***
     * Stop location service
     */

    public void onStop() {
        locationService.unregisterListener(mListener); //注销掉监听
        locationService.stop(); //停止定位服务
    }


    public void onStart() {
        // -----------location config ------------
        //获取locationservice实例,建议应用中只初始化1个location实例,然后使用,可以参考其他示例的activity,都是通过此种方式获取locationservice实例的
        locationService.registerListener(mListener);
        //注册监听

        locationService.setLocationOption(locationService.getDefaultLocationClientOption());
        locationService.start();
    }

}

3、LocationService

package com.qwk.flutter_plugin;

import android.content.Context;

import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.location.LocationClientOption.LocationMode;

/**
 * 
 * @author baidu
 *
 */
public class LocationService {
    private LocationClient client = null;
    private LocationClientOption mOption,DIYoption;
    private Object  objLock = new Object();

    /***
     * 
     * @param locationContext
     */
    public LocationService(Context locationContext){
        synchronized (objLock) {
            if(client == null){
                client = new LocationClient(locationContext);
                client.setLocOption(getDefaultLocationClientOption());
            }
        }
    }
    
    /***
     * 
     * @param listener
     * @return
     */
    
    public boolean registerListener(BDAbstractLocationListener listener){
        boolean isSuccess = false;
        if(listener != null){
            client.registerLocationListener(listener);
            isSuccess = true;
        }
        return  isSuccess;
    }
    
    public void unregisterListener(BDAbstractLocationListener listener){
        if(listener != null){
            client.unRegisterLocationListener(listener);
        }
    }
    
    /***
     * 
     * @param option
     * @return isSuccessSetOption
     */
    public boolean setLocationOption(LocationClientOption option){
        boolean isSuccess = false;
        if(option != null){
            if(client.isStarted())
                client.stop();
            DIYoption = option;
            client.setLocOption(option);
        }
        return isSuccess;
    }

    /***
     *
     * @return DefaultLocationClientOption  默认O设置
     */
    public LocationClientOption getDefaultLocationClientOption(){
        if(mOption == null){
            mOption = new LocationClientOption();
            mOption.setLocationMode(LocationMode.Hight_Accuracy);//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备
            mOption.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系,如果配合百度地图使用,建议设置为bd09ll;
            mOption.setScanSpan(3000);//可选,默认0,即仅定位一次,设置发起连续定位请求的间隔需要大于等于1000ms才是有效的
            mOption.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要
            mOption.setIsNeedLocationDescribe(true);//可选,设置是否需要地址描述
            mOption.setNeedDeviceDirect(false);//可选,设置是否需要设备方向结果
            mOption.setLocationNotify(false);//可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
            mOption.setIgnoreKillProcess(true);//可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死   
            mOption.setIsNeedLocationDescribe(true);//可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近”
            mOption.setIsNeedLocationPoiList(true);//可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到
            mOption.SetIgnoreCacheException(false);//可选,默认false,设置是否收集CRASH信息,默认收集
            mOption.setOpenGps(true);//可选,默认false,设置是否开启Gps定位
            mOption.setIsNeedAltitude(false);//可选,默认false,设置定位时是否需要海拔信息,默认不需要,除基础定位版本都可用
         
        }
        return mOption;
    }


    /**
     *
     * @return DIYOption 自定义Option设置
     */
    public LocationClientOption getOption(){
        if(DIYoption == null) {
            DIYoption = new LocationClientOption();
        }
        return DIYoption;
    }

    public void start(){
        synchronized (objLock) {
            if(client != null && !client.isStarted()){
                client.start();
            }
        }
    }
    public void stop(){
        synchronized (objLock) {
            if(client != null && client.isStarted()){
                client.stop();
            }
        }
    }

    public boolean isStart() {
        return client.isStarted();
    }

    public boolean requestHotSpotState(){
        return client.requestHotSpotState();
    }
    
} 

七、插件根目录下lib文件夹下文件结构

dart端文件结构.png

八、插件根目录下lib文件夹下文件代码示例
1、BaiduLocation.dart

class BaiduLocation{
  final double latitude;
  final double longitude;
  final String country;
  final String countryCode;
  final String province;
  final String cityCode;
  final String city;
  final String district;
  final String street;
  final String locationDescribe;
  final String adCode;
  final int errorCode;
  final bool isInChina;


  BaiduLocation({this.latitude, this.longitude,
    this.country, this.countryCode, this.province,
    this.cityCode, this.city, this.district, this.street,
    this.locationDescribe,this.adCode,this.errorCode,this.isInChina});

  factory BaiduLocation.fromMap(dynamic value){
    return new BaiduLocation(
        latitude: value['latitude'],
        longitude:value['longitude'],

        country:value['country'],
        countryCode:value['countryCode'],
        province: value['province'],
        cityCode: value['cityCode'],
        city: value['city'],
        district : value['district'],
        street:value['street'],
        locationDescribe:value['locationDescribe'],
        adCode:value['adCode'],
        errorCode:value['errorCode'],
        isInChina:value['isInChina']
    );
  }

  bool isSuccess() {
    return errorCode == 161;
  }

  Map getMap() {
    return {
      "latitude": latitude,
      "longitude":longitude,

      "country":country,
      "countryCode":countryCode,
      "province": province,
      "cityCode": cityCode,
      "city": city,
      "district" : district,
      "street":street,
      "locationDescribe":locationDescribe,
      "adCode":adCode,
      "errorCode":errorCode,
      "isInChina":isInChina
    };
  }
}

2 、flutter_plugin.dart
我的插件名为flutter_plugin,如果你建立的插件名不一样的话那就用你自己的名字就可以。

import 'dart:async';

import 'package:flutter/services.dart';

import 'baiduLocation.dart';

class QwkBaidumap {
  static const MethodChannel _channel = const MethodChannel('baidumap');

  static Future<BaiduLocation> get local async {
    final BaiduLocation version =
    BaiduLocation.fromMap(await _channel.invokeMethod('getLocal'));

//    return "X:${version.latitude.toString()} , Y:${version.longitude.toString()}";
    return version;
  }
}

九、在根目录下example文件夹下lib目录下main.dart中修改示例代码

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_plugin/flutter_plugin.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    String platformVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      platformVersion = await FlutterPlugin.platformVersion;
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Text('Running on: $_platformVersion\n'),
        ),
      ),
    );
  }
}

十、修改AndroidManifest.xml
修改根目录flutter_plugin/android/src/main/AndroidManifest.xml文件,修改后代码如下,需要注意的是将代码倒数第三行中间内容修改为你自己的百度AK。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.qwk.flutter_plugin">
    <!-- 这个权限用于进行网络定位-->
    <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>
        <!-- AK鉴权 -->
        <!-- meta-data需要写在application中 -->
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="修改为你的百度AK" />
    </application>
</manifest>

十一、总结
整体的代码就是以上这些,希望能够帮助到大家,有不足的地方请大神即使指正,请大家随意喷我哈,🤒第一次写经验不是很丰富,如果大家有什么不懂的地方大家可以➕我扣扣(1608066610)私聊我就行,希望能够多多交流哦~

两句废话👇

在Flutter学习过程中我们会遇到各种各样的问题👅
多个朋友多条路,让我们愉快的一起玩耍吧!👀
有什么问题欢迎跟我讨论哦~
喜欢交朋友的我的二维码奉上🙆‍♂️


乔悟空
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容