Browse Source

add: 添加MapView example 完成地图锚点添加,导航安卓端开发

hwh97 5 years ago
parent
commit
e6ed0b97c1

+ 4 - 0
.gitignore

@@ -3,5 +3,9 @@
 
 .packages
 .pub/
+*.iml
+*.ipr
+*.iws
+.idea/
 
 build/

+ 1 - 0
android/build.gradle

@@ -42,4 +42,5 @@ android {
 dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
     implementation 'com.amap.api:location:4.8.0'
+    implementation 'com.amap.api:3dmap:7.3.0'
 }

+ 61 - 23
android/src/main/kotlin/com/i2edu/amap_location/AMapHandler.kt

@@ -8,34 +8,72 @@ import com.amap.api.location.AMapLocationClientOption
 import com.amap.api.location.AMapLocationListener
 import io.flutter.plugin.common.MethodCall
 import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.StandardMessageCodec
+import io.flutter.plugin.platform.PlatformView
+import io.flutter.plugin.platform.PlatformViewFactory
 
-class AMapHandler(private val context: Context, channel: MethodChannel) :
+class AMapHandler(private val flutterState: FlutterState) :
         MethodChannel.MethodCallHandler, AMapLocationListener {
-    private var client: AMapLocationClient? = null;
-    private var channel: MethodChannel? = channel
-
+    private var client: AMapLocationClient? = null
+    private var mapView: AMapView? = null
 
     override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
-        if (call.method == "getPlatformVersion") {
-            result.success("Android ${android.os.Build.VERSION.RELEASE}")
-        } else if (call.method == "startLocation") {
-            if (client == null) {
-                client = AMapLocationClient(context)
+        when (call.method) {
+            "getPlatformVersion" -> {
+                result.success("Android ${android.os.Build.VERSION.RELEASE}")
+            }
+            "registerView" -> {
+                mapView = AMapView(flutterState.context, flutterState.methodChannel)
+                flutterState.platformViewRegistry.registerViewFactory("com.i2edu.mapView", object : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
+                    override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
+                        return mapView!!
+                    }
+                })
+                result.success(true)
+            }
+            "startLocation" -> {
+                if (client == null) {
+                    client = AMapLocationClient(flutterState.context)
+                }
+                try {
+                    val options = getFromMethodCall(call)
+                    client?.setLocationOption(options)
+                    client?.setLocationListener(this)
+                    client?.startLocation()
+                } catch (e: Exception) {
+                    result.error("1000", e.message, null)
+                }
+                result.success("success");
+            }
+            "closeLocation" -> {
+                stopLocation()
+                result.success("success")
+            }
+            // map view
+            "onCreate" -> {
+                mapView?.onCreate()
+                result.success(true)
+            }
+            "onPause" -> {
+                mapView?.onPause()
+                result.success(true)
+            }
+            "onResume" -> {
+                mapView?.onResume()
+                result.success(true)
+            }
+            "setMarkers" -> {
+                val data = call.argument<List<Map<String, Any>>>("markers")!!
+                mapView?.setMarkers(data)
+                result.success(true)
+            }
+            "onDestroy" -> {
+                mapView?.dispose()
+                result.success(true)
             }
-            try {
-                val options = getFromMethodCall(call)
-                client?.setLocationOption(options)
-                client?.setLocationListener(this)
-                client?.startLocation()
-            } catch (e: Exception) {
-                result.error("1000", e.message, null)
+            else -> {
+                result.notImplemented()
             }
-            result.success("success");
-        } else  if (call.method == "closeLocation") {
-            stopLocation()
-            result.success("success")
-        } else {
-            result.notImplemented()
         }
     }
 
@@ -44,7 +82,7 @@ class AMapHandler(private val context: Context, channel: MethodChannel) :
         if (location != null) {
             if (location.getErrorCode() == 0) {
                 //可在其中解析amapLocation获取相应内容。
-                channel?.invokeMethod("location", location.toJson(1).toString())
+                flutterState.methodChannel.invokeMethod("location", location.toJson(1).toString())
             } else {
                 Log.e("AmapError", "location Error, ErrCode:"
                         + location.getErrorCode() + ", errInfo:"

+ 142 - 0
android/src/main/kotlin/com/i2edu/amap_location/AMapView.kt

@@ -0,0 +1,142 @@
+package com.i2edu.amap_location
+
+import android.content.Context
+import android.graphics.Paint
+import android.location.Location
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import com.amap.api.maps.AMap
+import com.amap.api.maps.CameraUpdateFactory
+import com.amap.api.maps.MapView
+import com.amap.api.maps.model.*
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.platform.PlatformView
+import java.util.*
+
+
+class AMapView(private val context: Context, private val channel: MethodChannel) : PlatformView,
+        AMap.OnMyLocationChangeListener, AMap.InfoWindowAdapter {
+    private val TAG = "AMapView"
+    var frameLayout: FrameLayout? = null
+    var mapView: MapView? = null
+    var aMap: AMap? = null
+    var infoWindow: View? = null
+    var mMarkers: List<Map<String, Any>>? = null
+    private var firstMove: Boolean = false
+
+    private fun setUpView(context: Context) {
+        if (frameLayout == null) {
+            frameLayout = FrameLayout(context)
+            frameLayout?.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+            mapView = MapView(context)
+            mapView?.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+            frameLayout?.addView(mapView)
+        }
+    }
+
+    fun onCreate() {
+        // TODO SaveInstance restore
+        mapView?.onCreate(null)
+        if (aMap == null) {
+            aMap = mapView?.map
+        }
+        val locationStyle = MyLocationStyle()
+        locationStyle.showMyLocation(true)
+        locationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.dog))
+        locationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER)
+        aMap?.myLocationStyle = locationStyle
+        aMap?.isMyLocationEnabled = true
+        aMap?.uiSettings?.isMyLocationButtonEnabled = true
+        aMap?.setOnMyLocationChangeListener(this)
+        aMap?.setInfoWindowAdapter(this)
+    }
+
+    fun onPause() {
+        mapView?.onPause()
+    }
+
+    fun onResume() {
+        mapView?.onResume()
+    }
+
+    fun setMarkers(markers: List<Map<String, Any>>) {
+        this.mMarkers = markers
+
+        aMap?.addMarkers(mMarkers?.map {
+            MarkerOptions().position(LatLng(it["lat"].toString().toDouble(), it["lon"].toString().toDouble()))
+                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.i2)).title(it["title"].toString())
+        }?.toList() as ArrayList<MarkerOptions>, false)
+    }
+
+    override fun getView(): View {
+        setUpView(context)
+        return frameLayout!!
+    }
+
+    override fun dispose() {
+        firstMove = false
+        mMarkers = null
+        if (frameLayout != null) {
+            frameLayout?.removeView(mapView)
+            frameLayout = null
+        }
+        if (mapView != null) {
+            mapView?.removeAllViews()
+            infoWindow = null
+            mapView = null
+        }
+        aMap?.setOnMyLocationChangeListener(null)
+        if (aMap != null) {
+            aMap = null
+        }
+        mapView?.onDestroy()
+    }
+
+    override fun onMyLocationChange(p0: Location) {
+        if (!firstMove) {
+            aMap?.moveCamera(CameraUpdateFactory.newCameraPosition(CameraPosition(LatLng(p0.latitude, p0.longitude), 14f, 0f, 0f)))
+            firstMove = true
+        }
+    }
+
+    override fun getInfoContents(p0: Marker?): View? {
+        return null
+    }
+
+    override fun getInfoWindow(p0: Marker?): View {
+        if (infoWindow == null) {
+            infoWindow = LayoutInflater.from(context).inflate(
+                    R.layout.custom_info_window, null)
+        }
+        render(p0, infoWindow);
+        return infoWindow!!
+    }
+
+    private fun render(marker: Marker?, view: View?) {
+        val tvTitle = view?.findViewById<TextView>(R.id.tv_title)
+        val tvContent = view?.findViewById<TextView>(R.id.tv_content)
+        val tvTel = view?.findViewById<TextView>(R.id.tv_tel)
+        val closeImg = view?.findViewById<ImageView>(R.id.img_close)
+        closeImg?.setOnClickListener {
+            marker?.hideInfoWindow()
+        }
+        val tvGuide = view?.findViewById<TextView>(R.id.tv_guide)
+        tvGuide?.paint?.flags = Paint.UNDERLINE_TEXT_FLAG;
+
+        val map: Map<String, Any>? = mMarkers?.first { it["title"].toString() == marker?.title }
+        if (map != null) {
+            //TODO 中英文
+            tvTitle?.text = map["title"].toString()
+            tvContent?.text = "地址:${map["content"].toString()}"
+            tvTel?.text = "电话:${map["tel"].toString()}"
+            tvGuide?.setOnClickListener {
+                // return data to flutter
+                channel.invokeMethod("guide", map)
+            }
+        }
+    }
+}

+ 15 - 4
android/src/main/kotlin/com/i2edu/amap_location/AmapLocationPlugin.kt

@@ -10,9 +10,14 @@ import io.flutter.plugin.common.PluginRegistry.Registrar
 
 /** AmapLocationPlugin */
 public class AmapLocationPlugin: FlutterPlugin {
+
+
   override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
-    val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "amap_location")
-    channel.setMethodCallHandler(AMapHandler(flutterPluginBinding.applicationContext, channel));
+    flutterState = FlutterState(
+            flutterPluginBinding.applicationContext,
+            flutterPluginBinding.binaryMessenger,
+            flutterPluginBinding.platformViewRegistry)
+    flutterState?.startListening(AMapHandler(flutterState!!))
   }
 
   // This static function is optional and equivalent to onAttachedToEngine. It supports the old
@@ -25,13 +30,19 @@ public class AmapLocationPlugin: FlutterPlugin {
   // depending on the user's project. onAttachedToEngine or registerWith must both be defined
   // in the same class.
   companion object {
+    private var flutterState: FlutterState? = null
+
     @JvmStatic
     fun registerWith(registrar: Registrar) {
-      val channel = MethodChannel(registrar.messenger(), "amap_location")
-      channel.setMethodCallHandler(AMapHandler(registrar.context(), channel))
+      flutterState = FlutterState(
+              registrar.context(),
+              registrar.messenger(),
+              registrar.platformViewRegistry())
+      flutterState?.startListening(AMapHandler(flutterState!!))
     }
   }
 
   override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
+    flutterState?.stopListening()
   }
 }

+ 22 - 0
android/src/main/kotlin/com/i2edu/amap_location/FlutterState.kt

@@ -0,0 +1,22 @@
+package com.i2edu.amap_location
+
+import android.content.Context
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.platform.PlatformViewRegistry
+
+class FlutterState(
+        val context: Context,
+        binaryMessenger: BinaryMessenger,
+        val platformViewRegistry: PlatformViewRegistry) {
+    
+    val methodChannel: MethodChannel = MethodChannel(binaryMessenger, "amap_location")
+
+    fun startListening(methodCallHandler: AMapHandler) {
+        methodChannel.setMethodCallHandler(methodCallHandler)
+    }
+
+    fun stopListening() {
+        methodChannel.setMethodCallHandler(null)
+    }
+}

BIN
android/src/main/res/drawable/close.png


BIN
android/src/main/res/drawable/dog.png


BIN
android/src/main/res/drawable/i2.png


+ 21 - 0
android/src/main/res/drawable/info_window_bg.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+
+            <!-- 填充的颜色 -->
+            <solid android:color="#ffffff" />
+
+            <!-- 边框的颜色和粗细 -->
+            <!--    <stroke-->
+            <!--        android:width="1dp"-->
+            <!--        android:color="@color/blue"-->
+            <!--        />-->
+
+            <!-- android:radius 圆角的半径 -->
+            <corners
+                android:radius="4dp"
+                />
+        </shape>
+    </item>
+</layer-list>

+ 16 - 0
android/src/main/res/drawable/triangle.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <rotate
+            android:fromDegrees="45"
+            android:pivotX="135%"
+            android:pivotY="15%">
+            <shape android:shape="rectangle">
+                <size
+                    android:width="16dp"
+                    android:height="16dp" />
+                <solid android:color="#FFFFFF" />
+            </shape>
+        </rotate>
+    </item>
+</layer-list>

BIN
android/src/main/res/drawable/white.png


+ 67 - 0
android/src/main/res/layout/custom_info_window.xml

@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:background="@android:color/transparent"
+    android:layout_width="240dp"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:background="@drawable/info_window_bg"
+        android:layout_width="240dp"
+        android:layout_height="wrap_content">
+        <LinearLayout
+            android:orientation="vertical"
+            android:paddingVertical="8dp"
+            android:paddingHorizontal="8dp"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent">
+            <TextView
+                android:id="@+id/tv_title"
+                android:textSize="10sp"
+                android:textStyle="bold"
+                android:textColor="@android:color/black"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/tv_content"
+                android:layout_marginTop="2dp"
+                android:maxLines="3"
+                android:textSize="10sp"
+                android:textColor="@android:color/black"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/tv_tel"
+                android:layout_marginTop="2dp"
+                android:textSize="10sp"
+                android:textColor="@android:color/black"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+            <TextView
+                android:id="@+id/tv_guide"
+                android:layout_marginTop="2dp"
+                android:text="到这里去"
+                android:textSize="13sp"
+                android:textColor="#0000cc"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/img_close"
+            android:src="@drawable/close"
+            android:layout_gravity="end"
+            android:layout_marginTop="2dp"
+            android:background="@android:color/transparent"
+            android:layout_width="18dp"
+            android:layout_height="18dp"/>
+
+    </FrameLayout>
+
+    <ImageView
+        android:src="@drawable/triangle"
+        android:layout_gravity="center|bottom"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+</LinearLayout>

+ 58 - 29
example/lib/main.dart

@@ -1,17 +1,34 @@
 import 'dart:async';
 
 import 'package:amap_location/amap_export.dart';
+import 'package:amap_location_example/map_view_page.dart';
+import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
-void main() => runApp(MyApp());
+void main() {
+  WidgetsFlutterBinding.ensureInitialized();
+  AmapLocation.registerView();
+  runApp(MyApp());
+}
 
-class MyApp extends StatefulWidget {
+class MyApp extends StatelessWidget {
   @override
-  _MyAppState createState() => _MyAppState();
+  Widget build(BuildContext context) {
+    return MaterialApp(
+        home: HomePage()
+    );
+  }
 }
 
-class _MyAppState extends State<MyApp> {
+class HomePage extends StatefulWidget {
+  @override
+  State<StatefulWidget> createState() {
+    return HomePageState();
+  }
+}
+
+class HomePageState extends State<HomePage> {
   String _platformVersion = 'Unknown';
 
   @override
@@ -42,33 +59,45 @@ class _MyAppState extends State<MyApp> {
 
   @override
   Widget build(BuildContext context) {
-    return MaterialApp(
-      home: Scaffold(
-        appBar: AppBar(
-          title: const Text('Plugin example app'),
-        ),
-        body: Center(
-          child: RawMaterialButton(
-            onPressed: () async {
-              await AmapLocation.instance.startLocation(
-                  options: LocationOption(
-                isOnceLocation: false,
-                locationInterval: 5000,
-                locationTimeOut: 10,
-                locationMode: LocationMode.Battery_Saving,
-              ));
-              AmapLocation.instance.locationStream.listen((d) {
-                print(d.city);
-                // stop location when isOnceLocation is true
-                 AmapLocation.instance.disposed();
-              });
-              await Future.delayed(Duration(seconds: 60));
-              AmapLocation.instance.disposed();
-            },
-            child: Text("get lcoation"),
-          ),
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Plugin example app'),
+      ),
+      body: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            RawMaterialButton(
+              onPressed: () async {
+                await AmapLocation.instance.startLocation(
+                    options: LocationOption(
+                      isOnceLocation: false,
+                      locationInterval: 5000,
+                      locationTimeOut: 10,
+                      locationMode: LocationMode.Battery_Saving,));
+                AmapLocation.instance.locationStream.listen((d) {
+                  print(d.city);
+                  // stop location when isOnceLocation is true
+//                     AmapLocation.instance.disposed();
+                });
+                await Future.delayed(Duration(seconds: 60));
+                AmapLocation.instance.disposed();
+              },
+              child: Text("get lcoation"),
+            ),
+            SizedBox(height: 10,),
+            RawMaterialButton(
+              onPressed: () async {
+                Navigator.of(context).push(MaterialPageRoute(builder: (ctx){
+                  return MapViewPage();
+                }));
+              },
+              child: Text("jump to mapview"),
+            ),
+          ],
         ),
       ),
     );
   }
+
 }

+ 39 - 0
example/lib/map_util.dart

@@ -0,0 +1,39 @@
+import 'dart:io';
+import 'package:url_launcher/url_launcher.dart';
+
+/// call third Map App
+class MapUtil {
+
+  // 高德地图
+  static Future<bool> goAMap(double lon, double lat) async {
+    var url = '${Platform.isAndroid ? 'android' : 'ios'}amap://navi?sourceApplication=amap&lat=$lat&lon=$lon&dev=1&style=2';
+    bool canLaunchUrl = await canLaunch(url);
+    if (!canLaunchUrl) {
+      return false;
+    }
+    await launch(url);
+    return true;
+  }
+
+  // 百度地图
+  static Future<bool> goBaiduMap(double lon, double lat) async {
+    var url = 'baidumap://map/direction?destination=$lat,$lon&coord_type=gcj02&mode=driving';
+    bool canLaunchUrl = await canLaunch(url);
+    if (!canLaunchUrl) {
+      return false;
+    }
+    await launch(url);
+    return true;
+  }
+
+  // apple地图
+  static Future<bool> goAppleMap(double lon, double lat) async {
+    var url = 'https://maps.apple.com/?sll=${lon.toString() + "," + lat.toString()}&t=s';
+    bool canLaunchUrl = await canLaunch(url);
+    if (!canLaunchUrl) {
+      return false;
+    }
+    await launch(url);
+    return true;
+  }
+}

+ 199 - 0
example/lib/map_view_page.dart

@@ -0,0 +1,199 @@
+import 'dart:convert';
+
+import 'package:amap_location_example/map_util.dart';
+import 'package:flutter/material.dart';
+import 'package:amap_location/amap_export.dart';
+import 'dart:io';
+
+class MapViewPage extends StatefulWidget {
+  @override
+  State<StatefulWidget> createState() {
+    return _MapViewPageState();
+  }
+}
+
+class _MapViewPageState extends State<MapViewPage> with WidgetsBindingObserver {
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(title: Text("Map View")),
+      body: SizedBox(
+        height: double.maxFinite,
+        width: double.maxFinite,
+        child: AmapLocation.instance.buildMapView(_onPlatformCreate),
+      ),
+    );
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    WidgetsBinding.instance.addObserver(this);
+  }
+
+  @override
+  void dispose() {
+    WidgetsBinding.instance.removeObserver(this);
+    AmapLocation.instance.disposedMapView();
+    super.dispose();
+  }
+
+  @override
+  void didChangeAppLifecycleState(AppLifecycleState state) {
+    super.didChangeAppLifecycleState(state);
+    if (state == AppLifecycleState.paused) {
+      AmapLocation.instance.onPauseMapView();
+    } else if (state == AppLifecycleState.resumed) {
+      AmapLocation.instance.onResumeMapView();
+    }
+  }
+
+  void initCityData() async {
+    // kingWay 的环境
+    var httpClient = new HttpClient();
+    HttpClientRequest request = await httpClient
+        .getUrl(Uri.http("172.16.11.77:8086", "/api/v1/school_map/all_addr"));
+    var response = await request.close();
+    var responseBody = await response.transform(Utf8Decoder()).join();
+    List<Map> data = jsonDecode(responseBody)['data'].cast<Map>();
+    print(data.toString());
+
+    AmapLocation.instance.setMapMarkers(data.map((f) {
+      var coordinate = f['coordinate'].toString().split(",");
+      return AmapMarker(
+          double.parse(coordinate[1]),
+          double.parse(coordinate[0]),
+          {"title": f["schoolname"], "content": f["address"], "tel": f["tel"]});
+    }).toList());
+  }
+
+  void _onPlatformCreate() {
+    print("on map view create");
+    AmapLocation.instance.onCreateMapView();
+    initCityData();
+    AmapLocation.instance.guideStream.listen((m) {
+      showModalBottomSheet(
+          context: context,
+          builder: (context) {
+            return Container(
+              color: Colors.white,
+              height: Platform.isIOS ? 158 + 50 : 158,
+              margin: EdgeInsets.only(
+                bottom: MediaQuery.of(context).padding.bottom,
+              ),
+              child: Column(
+                mainAxisSize: MainAxisSize.max,
+                mainAxisAlignment: MainAxisAlignment.center,
+                crossAxisAlignment: CrossAxisAlignment.center,
+                children: <Widget>[
+                  Offstage(
+                    offstage: !Platform.isIOS,
+                    child: Column(
+                      children: <Widget>[
+                        Container(
+                          height: 50,
+                          child: Material(
+                            color: Colors.white,
+                            child: InkWell(
+                              child: Center(
+                                child: Text(
+                                  "苹果地图",
+                                  style: TextStyle(
+                                    fontSize: 15,
+                                  ),
+                                ),
+                              ),
+                              onTap: () => _goThirdPartMapApp(context, MapType.Apple, m),
+                            ),
+                          ),
+                        ),
+                        Divider(
+                          height: 1,
+                        ),
+                      ],
+                    ),
+                  ),
+                  Container(
+                    height: 50,
+                    child: Material(
+                      color: Colors.white,
+                      child: InkWell(
+                        child: Center(
+                          child: Text(
+                            "高德地图",
+                            style: TextStyle(
+                              fontSize: 15,
+                            ),
+                          ),
+                        ),
+                        onTap: () => _goThirdPartMapApp(context, MapType.AMap, m),
+                      ),
+                    ),
+                  ),
+                  Divider(
+                    height: 1,
+                  ),
+                  Container(
+                    height: 50,
+                    child: Material(
+                      color: Colors.white,
+                      child: InkWell(
+                        child: Center(
+                          child: Text(
+                            "百度地图",
+                            style: TextStyle(
+                              fontSize: 15,
+                            ),
+                          ),
+                        ),
+                        onTap: () => _goThirdPartMapApp(context, MapType.Baidu, m),
+                      ),
+                    ),
+                  ),
+                  Divider(
+                    height: 1,
+                  ),
+                  Container(
+                    height: 50,
+                    child: Material(
+                      color: Colors.white,
+                      child: InkWell(
+                        child: Center(
+                          child: Text(
+                            "取消",
+                            style: TextStyle(
+                              fontSize: 15,
+                            ),
+                          ),
+                        ),
+                        onTap: () {
+                          Navigator.of(context).pop();
+                        },
+                      ),
+                    ),
+                  ),
+                ],
+              ),
+            );
+          });
+    });
+  }
+
+  void _goThirdPartMapApp(BuildContext context, MapType type, dynamic data) async {
+    // close bottom sheet
+    Navigator.of(context).pop();
+    bool success;
+    if (type == MapType.AMap) {
+      success = await MapUtil.goAMap(data['lon'], data['lat']);
+    } else if (type == MapType.Baidu) {
+      success = await MapUtil.goBaiduMap(data['lon'], data['lat']);
+    } else if (type == MapType.Apple) {
+      success = await MapUtil.goAppleMap(data['lon'], data['lat']);
+    }
+    print("jump ${success ? "successful" : "failed"}");
+  }
+}
+
+enum MapType {
+  Apple, Baidu, AMap
+}

+ 66 - 25
example/pubspec.lock

@@ -12,63 +12,63 @@ packages:
     dependency: transitive
     description:
       name: archive
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.11"
   args:
     dependency: transitive
     description:
       name: args
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.5.2"
   async:
     dependency: transitive
     description:
       name: async
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.4.0"
   boolean_selector:
     dependency: transitive
     description:
       name: boolean_selector
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.5"
   charcode:
     dependency: transitive
     description:
       name: charcode
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.2"
   collection:
     dependency: transitive
     description:
       name: collection
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.14.11"
   convert:
     dependency: transitive
     description:
       name: convert
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.1"
   crypto:
     dependency: transitive
     description:
       name: crypto
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.3"
   cupertino_icons:
     dependency: "direct main"
     description:
       name: cupertino_icons
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.1.3"
   flutter:
@@ -81,53 +81,65 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  flutter_web_plugins:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.0"
   image:
     dependency: transitive
     description:
       name: image
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.1.4"
   matcher:
     dependency: transitive
     description:
       name: matcher
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.12.6"
   meta:
     dependency: transitive
     description:
       name: meta
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.8"
   path:
     dependency: transitive
     description:
       name: path
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.6.4"
   pedantic:
     dependency: transitive
     description:
       name: pedantic
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.8.0+1"
   petitparser:
     dependency: transitive
     description:
       name: petitparser
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.4.0"
+  plugin_platform_interface:
+    dependency: transitive
+    description:
+      name: plugin_platform_interface
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.0.2"
   quiver:
     dependency: transitive
     description:
       name: quiver
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.5"
   sky_engine:
@@ -139,64 +151,93 @@ packages:
     dependency: transitive
     description:
       name: source_span
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.5.5"
   stack_trace:
     dependency: transitive
     description:
       name: stack_trace
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.9.3"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.0"
   string_scanner:
     dependency: transitive
     description:
       name: string_scanner
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.0.5"
   term_glyph:
     dependency: transitive
     description:
       name: term_glyph
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.0"
   test_api:
     dependency: transitive
     description:
       name: test_api
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "0.2.11"
   typed_data:
     dependency: transitive
     description:
       name: typed_data
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "1.1.6"
+  url_launcher:
+    dependency: "direct dev"
+    description:
+      name: url_launcher
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "5.4.2"
+  url_launcher_macos:
+    dependency: transitive
+    description:
+      name: url_launcher_macos
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.0.1+4"
+  url_launcher_platform_interface:
+    dependency: transitive
+    description:
+      name: url_launcher_platform_interface
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "1.0.6"
+  url_launcher_web:
+    dependency: transitive
+    description:
+      name: url_launcher_web
+      url: "https://pub.flutter-io.cn"
+    source: hosted
+    version: "0.1.1+1"
   vector_math:
     dependency: transitive
     description:
       name: vector_math
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "2.0.8"
   xml:
     dependency: transitive
     description:
       name: xml
-      url: "https://pub.dev"
+      url: "https://pub.flutter-io.cn"
     source: hosted
     version: "3.5.0"
 sdks:
   dart: ">=2.4.0 <3.0.0"
+  flutter: ">=1.12.8 <2.0.0"

+ 1 - 1
example/pubspec.yaml

@@ -16,7 +16,7 @@ dependencies:
 dev_dependencies:
   flutter_test:
     sdk: flutter
-
+  url_launcher: 5.4.2
   amap_location:
     path: ../
 

+ 1 - 0
lib/amap_export.dart

@@ -3,3 +3,4 @@ library amap_location;
 export 'amap_location.dart';
 export 'location_option.dart';
 export 'location_result_entity.dart';
+export 'amap_marker.dart';

+ 67 - 14
lib/amap_location.dart

@@ -1,23 +1,33 @@
 import 'dart:async';
 import 'dart:convert';
+import 'dart:io';
 
-import 'package:amap_location/location_option.dart';
-import 'package:amap_location/location_result_entity.dart';
 import 'package:flutter/services.dart';
+import 'package:flutter/material.dart';
+
+import 'amap_marker.dart';
+import 'location_option.dart';
+import 'location_result_entity.dart';
 
 // IOS:
 // add info.plist GaoDeAppKey
 class AmapLocation {
   static const MethodChannel _channel = const MethodChannel('amap_location');
   static final AmapLocation _instance = AmapLocation();
+  static const String viewType = "com.i2edu.mapView";
+
   static AmapLocation get instance => _instance;
 
-  StreamController _streamController;
-  Stream<LocationResultEntity> get locationStream => _streamController.stream;
+  StreamController _locationStreamController;
+  StreamController _guideStreamController;
+
+  Stream<LocationResultEntity> get locationStream => _locationStreamController.stream;
+  Stream<Map> get guideStream => _guideStreamController.stream;
 
-  AmapLocation(){
+  AmapLocation() {
     _channel.setMethodCallHandler(platformCallHandler);
-    _streamController = StreamController<LocationResultEntity>.broadcast();
+    _locationStreamController = StreamController<LocationResultEntity>.broadcast();
+    _guideStreamController = StreamController<Map>.broadcast();
   }
 
   static Future<String> get platformVersion async {
@@ -25,33 +35,76 @@ class AmapLocation {
     return version;
   }
 
+  static Future<void> registerView() async {
+    return await _channel.invokeMethod('registerView');
+  }
+
+  Widget buildMapView(VoidCallback onPlatformViewCreated) =>
+      Platform.isAndroid
+          ? AndroidView(
+        viewType: viewType,
+        creationParams: {},
+        creationParamsCodec: const StandardMessageCodec(),
+        onPlatformViewCreated: (id) => onPlatformViewCreated(),
+      ) : UiKitView(
+        viewType: viewType,
+        creationParams: {},
+        creationParamsCodec: const StandardMessageCodec(),
+        onPlatformViewCreated: (id) => onPlatformViewCreated(),
+      );
+
+  Future<void> setMapMarkers(List<AmapMarker> markers) async {
+    return await _channel.invokeMethod("setMarkers", {"markers":  markers.map((f) => f.toMap()).toList()});
+  }
+
+  Future<void> onCreateMapView() async {
+    return await _channel.invokeMethod('onCreate');
+  }
+
+  Future<void> onPauseMapView() async {
+    return await _channel.invokeMethod('onPause');
+  }
+
+  Future<void> onResumeMapView() async {
+    return await _channel.invokeMethod('onResume');
+  }
+
   Future<void> startLocation({LocationOption options}) async {
-    if (_streamController == null) {
-      _streamController = StreamController<LocationResultEntity>.broadcast();
+    if (_locationStreamController == null) {
+      _locationStreamController = StreamController<LocationResultEntity>.broadcast();
     }
     return await _channel.invokeMethod(
         'startLocation', options?.toMap() ?? LocationOption().toMap());
   }
 
   Future<void> disposed() async {
-    if (!_streamController.isClosed) {
-      await _streamController.close();
+    if (!_locationStreamController.isClosed) {
+      await _locationStreamController.close();
     }
-    _streamController = null;
+    _locationStreamController = null;
     return await _channel.invokeMethod("closeLocation");
   }
 
+  Future<void> disposedMapView() async {
+    if (!_guideStreamController.isClosed) {
+      await _guideStreamController.close();
+    }
+    _guideStreamController = null;
+    return;
+  }
+
   Future<void> platformCallHandler(MethodCall call) async {
     try {
       if (call.method == "location") {
         LocationResultEntity entity =
-            LocationResultEntity().fromJson(jsonDecode(call.arguments));
-        _streamController.add(entity);
+        LocationResultEntity().fromJson(jsonDecode(call.arguments));
+        _locationStreamController.add(entity);
+      } else if (call.method == "guide") {
+        _guideStreamController.add(call.arguments);
       }
     } catch (ex) {
       print('Unexpected error: $ex');
     }
     return null;
-
   }
 }