flutter实现调用原生安卓的高德地图导航功能(插件化)

查看了高德地图flutter插件的文档,都没有能支持导航的功能,并且flutter的高德插件支持的功能特别少,没办法,只能使用安卓原生的导航,flutter去调用了,具体实现方式如下:

  1. 创建 Flutter 插件

使用--template=plugin 声明创建的是同时包含了 iOS 和 Android 代码的 plugin;

使用--org 选项指定组织,一般采用反向域名表示法;

使用-i 选项指定 iOS 平台开发语言,objc 或者 swift;

使用-a 选项指定 Android 平台开发语言,java 或者 kotlin。

flutter create --template=plugin --org com.tencent.game -i objc -a java flutter_amap_nav
  1. 项目结构

我们可以看到 Plugin 多出了一些目录,android 目录用于 Android 平台的代码实现,ios 目录用于 iOS 平台的代码实现,example 目录用于该组件的调试。

  1. 在androidStudio下,右键android目录,选择flutter,以安卓工程打开,具体如下:

  1. 此时,我们在打开的这个项目就可以进行远程的安卓开发了,关于安卓导航部分的SDK,高德的官方文档上面有示例,附上官网链接https://lbs.amap.com/api/android-navi-sdk/summary/

高德地图APIkey申请可参考https://lbs.amap.com/api/android-navi-sdk/guide/create-project/get-key,获取调试版本和发布版本的SHA1(在申请key的时候,这两个SHA1可填写一致),需要注意的是包名在AndroidManifest.xml的package获取

获取好Key之后,在AndroidManifest.xml文件中,添加如下权限

<!--允许访问网络,必选权限-->
<uses-permission android:name="android.permission.INTERNET" />

<!--允许获取精确位置,实时导航为必选-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!--允许获取粗略位置,实时导航为必选-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!--允许获取设备和运营商信息,用于问题排查和网络定位(无gps情况下的定位),若需网络定位功能则必选-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

<!--允许获取网络状态,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!--允许获取wifi网络信息,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

<!--允许获取wifi状态改变,用于网络定位(无gps情况下的定位),若需网络定位功能则必选-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

<!--后台获取位置信息,若需后台定位或持续导航则必选-->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

<!--用于申请调用A-GPS模块,卫星定位加速-->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />

<!--允许写入扩展存储,用于写入缓存定位数据-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!--用于用户链接蓝牙时,在导航组件页面的蓝牙连接提醒,建立链接后开发者可选用蓝牙通道进行tts播报-->
<uses-permission android:name="android.permission.BLUETOOTH" />

<!--用与导航状态中保持屏幕常亮-->
<uses-permission android:name="android.permission.WAKE_LOCK"/>

<!--允许读设备等信息,用于问题排查-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

并且,需要添加地图定位的服务

  <application>
        <service android:name="com.amap.api.location.APSService"></service>
        <activity
            android:name="com.amap.api.navi.AmapRouteActivity"
            android:theme="@android:style/Theme.NoTitleBar"
            android:configChanges="orientation|keyboardHidden|screenSize|navigation" />


    </application>

在build.gradle文件中添加导航SDK的依赖,需要需要build.gradle在安卓工程中有两个文件,不要写错位置了,我使用的方式是自动获取最新的依赖包,当然也可以采用手动引入jar包的方式,可参考官方文档的安卓工程手动部署https://lbs.amap.com/api/android-navi-sdk/guide/create-project/manual-configuration

implementation 'com.amap.api:navi-3dmap:latest.integration'

接着,我们在app目录下的AndroidManifest.xml添加apiKey

现在, 我们就可以写安卓代码了,在java目录下,新建mapView和MyViewFactory类,在mapView类下面代码实现如下:

package com.example.amap_nav;

import android.content.Context;
import android.view.View;
import android.webkit.WebView;
import android.widget.TextView;

import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Poi;
import com.amap.api.navi.AmapNaviPage;
import com.amap.api.navi.AmapNaviParams;
import com.amap.api.navi.AmapNaviType;
import com.amap.api.navi.AmapPageType;
import com.amap.api.navi.NaviSetting;
import com.amap.api.navi.INaviInfoCallback;
import com.amap.api.navi.model.AMapNaviLocation;

import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.platform.PlatformView;

public class mapView implements PlatformView {

    private final View Nav;
    private  final  AmapNaviParams naviParams;


    public mapView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params){
        Poi start;
        Poi end;
        TextView textView = new TextView(context);
//        隐私合规检查
        NaviSetting.updatePrivacyShow(context, true, true);
        NaviSetting.updatePrivacyAgree(context, true);
        if (params!=null&&!params.isEmpty()&&params.containsKey("start")) {
            String start1 = (String) params.get("start");
            String end1 = (String) params.get("end");
            textView.setText(start1+end1);
            double a  = Double.parseDouble(start1.split(",")[0]) ;
            double b  = Double.parseDouble(start1.split(",")[1]) ;
            double c  = Double.parseDouble(end1.split(",")[0]) ;
            double d  = Double.parseDouble(end1.split(",")[1]) ;
            start = new Poi(null, new LatLng(a,b), null);
            end = new Poi(null, new LatLng(c,d), null);
            // 组件参数配置
            //构建导航组件配置类,没有传入起点,所以起点默认为 “我的位置”
            naviParams = new AmapNaviParams(start,null,end, AmapNaviType.DRIVER, AmapPageType.ROUTE);
            naviParams.setUseInnerVoice(true);
            naviParams.setMultipleRouteNaviMode(true);
            naviParams.setNeedDestroyDriveManagerInstanceWhenNaviExit(true);
            //启动导航组件
            AmapNaviPage.getInstance().showRouteActivity(context.getApplicationContext(), naviParams,new INaviInfoCallback() {

                @Override
                public void onInitNaviFailure() {

                }

                @Override
                public void onGetNavigationText(String s) {

                }

                @Override
                public void onLocationChange(AMapNaviLocation aMapNaviLocation) {

                }

                @Override
                public void onArriveDestination(boolean b) {

                }

                @Override
                public void onStartNavi(int i) {

                }

                @Override
                public void onCalculateRouteSuccess(int[] ints) {

                }

                @Override
                public void onCalculateRouteFailure(int i) {

                }

                @Override
                public void onStopSpeaking() {

                }

                @Override
                public void onReCalculateRoute(int i) {

                }

                @Override
                public void onExitPage(int i) {

                }

                @Override
                public void onStrategyChanged(int i) {

                }

                @Override
                public void onArrivedWayPoint(int i) {

                }

                @Override
                public void onMapTypeChanged(int i) {

                }

                @Override
                public void onNaviDirectionChanged(int i) {

                }

                @Override
                public void onDayAndNightModeChanged(int i) {

                }

                @Override
                public void onBroadcastModeChanged(int i) {

                }

                @Override
                public void onScaleAutoChanged(boolean b) {

                }

                @Override
                public View getCustomMiddleView() {
                   return null;
                }

                @Override
                public View getCustomNaviView() {

                    return null ;
                }

                @Override
                public View getCustomNaviBottomView() {

                    TextView textView1 = new TextView(context);
                    String a = "123123213123123";
                    textView1.setText(a);
                    return textView1 ;
                }

            });




//         拿到flutter传递过来的参数

            this.Nav = textView;

        } else {

            textView.setText(null);
            this.Nav = textView;
            naviParams = null;
        }


    }

    @Override
    public View getView() {
        return Nav;
    }

    @Override
    public void dispose() {

    }

}


在MyViewFactory类下面代码实现如下:

package com.example.amap_nav;

import android.content.Context;

import com.example.amap_nav.mapView;

import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

public class MyViewFactory extends PlatformViewFactory {

    private final BinaryMessenger messenger;

    public MyViewFactory(BinaryMessenger messenger) {
        super(StandardMessageCodec.INSTANCE);
        this.messenger = messenger;
    }

    @Override
    @SuppressWarnings("unchecked")
    public PlatformView create(Context context, int id, Object o) {
        Map<String, Object> params = (Map<String, Object>) o;
        return new mapView(context, messenger, id, params);
    }
}

接着,我们去flutter自动创建plugin

的代码实现如下:

package com.example.amap_nav;

import android.util.Log;

import androidx.annotation.NonNull;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BasicMessageChannel;
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.StringCodec;

import com.example.amap_nav.MyViewFactory;

/** AmapNavPlugin */
public class AmapNavPlugin implements FlutterPlugin, MethodCallHandler {

  private MethodChannel channel;



  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
//    需要注册的视图的唯一标识
    final String key = "karl_info";
//    创建MethodChannel通道,amap_nav与yaml的name是需要对应的
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "amap_nav");

    channel.setMethodCallHandler(this);
//    注册原生view,通过注册视图工厂(viewFactory),需要传入唯一标识和ViewFactory类
    flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(key,new MyViewFactory(flutterPluginBinding.getBinaryMessenger()));



  }


  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {

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

      result.success("Android " + android.os.Build.VERSION.RELEASE +call.arguments);
    }
    else if (call.method.equals("startEnd")) {

      result.success("Android " + android.os.Build.VERSION.RELEASE);

    }
    else {
      result.notImplemented();
    }
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
  }
}

然后在MainActivity.java里面注册一下视图

package com.example.amap_nav_example;

import androidx.annotation.NonNull;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    @Override
        public  void  configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);
        }
}

到这里,安卓部分的代码已经完成了,然后我们回到flutter目录,在lib目录下添加load.dart文件,代码如下:

import 'dart:io';

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



class navWidget extends StatelessWidget {
  const navWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    String viewType = 'karl_info'; // 唯一标识符
    var creationParams = {
      'start':'30.501258, 114.414684',
      'end': '30.511258,114.434684'
    };// 视图创建参数,可以被插件用来传递构造函数参数到嵌入式Android视图
    // 视图创建完毕的回调
    PlatformViewCreatedCallback callback = (id) {};
    // 判断设备类型,也可用:defaultTargetPlatform == TargetPlatform.android
    if (Platform.isAndroid) {
      return AndroidView(
          viewType: viewType,
        onPlatformViewCreated: callback,
        creationParams: creationParams,
        //参数的编码方式
        creationParamsCodec: const StandardMessageCodec(),
      );
    } else if (Platform.isIOS) {
      return UiKitView(
          viewType: viewType,
        onPlatformViewCreated: callback,

      );
    } else {
      return Text('您的设备暂不支持此软件');
    }

  }
}

在flutter自动创建lib目录下的dart文件中实现方式如下:


import 'dart:async';

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


class AmapNav {
  static const MethodChannel _channel = MethodChannel('amap_nav');

  static Future<String?> get platformVersion async {
    //invokeMethod传参方式
    final String? version = await _channel.invokeMethod('getPlatformVersion',666);
    return version;
  }
  static Future<String?> get startEnd async {

  }
}

大功告成,项目跑起来的页面如下,后续各种导航的功能可参考官网实现定制化开发,github地址https://github.com/shenlan42/flutter_amap_nav

猜你喜欢

转载自blog.csdn.net/m0_60546228/article/details/129464642