首页
留言
关于
友链
更多
足迹
Search
1
SpringMVC+Spring+MyBatis整合完整版Web实例(附数据)
2,914 阅读
2
关于在Flutter实现Google地图的方法
1,885 阅读
3
druid报异常 “sql injection violation, part alway true condition not allow”的解决方案
1,381 阅读
4
git删除remote
1,344 阅读
5
MyBatis的TooManyResultsException异常的解决办法
1,151 阅读
发现
技术
生活
户外
登录
Search
标签搜索
Git
JavaScript
Flutter
Oracle
Git学习
Java
MySQL
SQL Server
秦岭户外
IntelliJ IDEA
Spring Boot
Flutter 2.0
对称加密算法
Google地图
Maven
ES6
linux
Tomcat
Redis
Spring
Bai Keyang
累计撰写
288
篇文章
累计收到
277
条评论
首页
栏目
发现
技术
生活
户外
页面
留言
关于
友链
足迹
搜索到
1
篇与
国内使用Google地图
的结果
2019-12-03
关于在Flutter实现Google地图的方法
由于项目需要开发一款国际化APP(多语种)。公司是一家国内的车联网企业,车辆地图监控为项目的基础功能模块。在国内项目用的是高德,高德国际化支持太差劲,在国际化中直接被PASS掉了。为了能达到全球化的位置监控展示,尝试过MapBox,最后考虑种种还是使用Google地图。话说,Google在国内的现状目前是啥样我想大家也都清楚,在9月份的某一天突然发现国内的Google地图网页也被重定向google.cn。看来这是真的打算撤退的节奏啊。偶然间在网上看到说 用电脑访问 http://www.google.cn//maps 可以访问,真的神奇了。虽然Google国内地图被关闭了,但是好在API可以正常使用。项目是开发一款基于IOS/Android的移动端软件,在调研前期选择了Flutter去开发,因为看到Flutter对IOS/Android的支持还是不错的,一套代码编译为两个平台的软件。当然,Google Map 官方也当然提供的有Flutter 的插件 google_maps_flutter,开箱即用很是方便。直接上手。基于google_maps_flutter的开发准备工作:1、首先需要Google地图的API Key2、手机需要Google 服务全家桶(不懂请自行度娘喔~ )添加依赖:dependencies: google_maps_flutter: ^0.5.21+12插件的使用方法在【https://pub.dev/packages/google_maps_flutter】中也介绍的很详细。直接copy代码走起,如下~import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Google Maps Demo', home: MapSample(), ); } } class MapSample extends StatefulWidget { @override State<MapSample> createState() => MapSampleState(); } class MapSampleState extends State<MapSample> { Completer<GoogleMapController> _controller = Completer(); static final CameraPosition _kGooglePlex = CameraPosition( target: LatLng(37.42796133580664, -122.085749655962), zoom: 14.4746, ); static final CameraPosition _kLake = CameraPosition( bearing: 192.8334901395799, target: LatLng(37.43296265331129, -122.08832357078792), tilt: 59.440717697143555, zoom: 19.151926040649414); @override Widget build(BuildContext context) { return new Scaffold( body: GoogleMap( mapType: MapType.hybrid, initialCameraPosition: _kGooglePlex, onMapCreated: (GoogleMapController controller) { _controller.complete(controller); }, ), floatingActionButton: FloatingActionButton.extended( onPressed: _goToTheLake, label: Text('To the lake!'), icon: Icon(Icons.directions_boat), ), ); } Future<void> _goToTheLake() async { final GoogleMapController controller = await _controller.future; controller.animateCamera(CameraUpdate.newCameraPosition(_kLake)); } }在运行代码之前需要配置API Key :Android:在android/app/src/main/AndroidManifest.xml中配置:<application> ... <meta-data android:name="com.google.android.geo.API_KEY" android:value="您的Key"/> </application>IOS :在Info.plist中配置如下:<dict> ...... <key>io.flutter.embedded_views_preview</key> <true/> </dict>两种配置Key的方法:方法一:在ios/Runner/AppDelegate.m中配置Key:#include "AppDelegate.h" #include "GeneratedPluginRegistrant.h" #import "GoogleMaps/GoogleMaps.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [GMSServices provideAPIKey:@"您的Key"]; [GeneratedPluginRegistrant registerWithRegistry:self]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @end方法二:在ios/Runner/AppDelegate.swift中配置Keyimport UIKit import Flutter import GoogleMaps @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? ) -> Bool { GMSServices.provideAPIKey("您的Key") GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }OK,开始运行。 嗯~???? 设备不支持?我的模拟器没有安装Google 服务全家桶。目前国内的Android手机都已经将Google内置服务去掉了。然而Google地图在Android却需要Google服务的支持。这就有些尴尬了。总不可能以后凡是使用该APP的用户都强迫安装这个吧?客户首先就不买单。于是开始第二种尝试,用HTML实现地图并嵌套在APP中。方案实现思路:使用webview嵌套一个页面,在页面中展示Google地图。通过页面JS回调Flutter里面的方法;Flutter通过webview_flutter插件提供的evaluateJavascript函数调用页面上的方法操作页面地图(比如地图缩放、地图绘制、地图查找)准备工作:1、首先需要Google地图的API Key2、依赖dependencies: webview_flutter: ^0.3.15+1代码:class Lnglat { double lng; double lat; Lnglat(this.lng, this.lat); @override String toString() { return 'Lnglat{lng: $lng, lat: $lat}'; } Map<String, dynamic> toJson() { Map<String, dynamic> _m = new Map<String, dynamic>(); _m["lng"] = this.lng; _m["lat"] = this.lat; return _m; } factory Lnglat.fromJson(Map<String, dynamic> json) { return Lnglat( json['lng'], json['lat']); } }class InfoWindow { String content; ///信息的最大宽度,与内容的宽度无关。仅当在调用open之前设置此值时,才会考虑此值。 ///若要在更改内容时更改最大宽度,请调用close、setOptions,然后调用open。 double maxWidth; ///所有InfoWindows都按其zIndex的顺序显示在地图上,较高的值显示在较低值的InfoWindows前面。 ///默认情况下,InfoWindows根据纬度显示,较低纬度的InfoWindows出现在较高纬度的InfoWindows前面。 ///信息窗口始终显示在标记前面。 int zIndex; InfoWindow({this.content, this.maxWidth, this.zIndex}); @override String toString() { return 'InfoWindow{content: $content, maxWidth: $maxWidth, zIndex: $zIndex}'; } Map<String, dynamic> toJson() { Map<String, dynamic> _m = new Map<String, dynamic>(); _m["content"] = this.content; _m["maxWidth"] = this.maxWidth; _m["zIndex"] = this.zIndex; return _m; } factory InfoWindow.fromJson(Map<String, dynamic> json) { return InfoWindow( content: json['content'], maxWidth: json['maxWidth'], zIndex: json['zIndex']); } }import 'InfoWindow.dart'; import 'Lnglat.dart'; class Marker { /// 点标记在地图上显示的位置,默认为地图中心点 Lnglat position; /// 需在点标记中显示的图标 String icon; /// 鼠标滑过点标记时的文字提示,不设置则鼠标滑过点标无文字提示 String title; /// 添加文本标注 String label; /// 点标记是否可点击(默认为true) bool clickable; /// 设置点标记是否可拖拽移动(默认为false) bool crossOnDrag; /// Marker 点击事件 final click; /// Marker点击显示窗体信息 InfoWindow clickInfoWindow; Marker(this.position, { this.title, this.label, this.icon, this.clickable = true, this.crossOnDrag = false, this.clickInfoWindow, this.click}); @override String toString() { return 'Marker{position: $position, icon: $icon, title: $title, label: $label, clickable: $clickable, crossOnDrag: $crossOnDrag, ' 'clickInfoWindow: $clickInfoWindow }'; } Map<String, dynamic> toJson() { Map<String, dynamic> _m = new Map<String, dynamic>(); _m["position"] = this.position.toJson(); _m["icon"] = this.icon; _m["title"] = this.title; _m["label"] = this.label; _m["clickable"] = this.clickable; _m["crossOnDrag"] = this.crossOnDrag; _m["clickInfoWindow"] = (null != this.clickInfoWindow) ? this.clickInfoWindow.toJson():null; return _m; } factory Marker.fromJson(Map<String, dynamic> json) { return Marker( json['position'], title: json['title'], label: json['label'], icon: json['icon'], clickable: json['clickable'], crossOnDrag: json['crossOnDrag']); } }import 'Marker.dart'; import 'Lnglat.dart'; class Line { List<Lnglat> path; // 线路颜色 String color; // 线路宽 double width; // 是否允许播放(默认false) bool isAllowPlay; // 线路端点开启 bool endPointEnable; // 起点(预留参数;当endPointEnable 为true时,该参数生效) Marker start; // 终点(预留参数;当endPointEnable 为true时,该参数生效) Marker end; // 是否自动播放(默认false;当isAllowPlay 为true时,该参数生效) bool isMoveAlong; Line(this.path, {this.color, this.width, this.isAllowPlay, this.start, this.end, this.isMoveAlong = false, this.endPointEnable = false,}); @override String toString() { return 'Line{path: $path, isMoveAlong: $isMoveAlong}'; } Map<String, dynamic> toJson() { Map<String, dynamic> _m = new Map<String, dynamic>(); List<dynamic> _path = []; if(null != this.path && this.path.length > 0){ for(int i = 0;i < this.path.length;i++){ _path.add(this.path[i].toJson()); } } _m["path"] = _path; _m["color"] = this.color; _m["width"] = this.width; _m["isAllowPlay"] = this.isAllowPlay; _m["start"] = this.start.toJson(); _m["end"] = this.end.toJson(); _m["isMoveAlong"] = this.isMoveAlong; _m["endPointEnable"] = this.endPointEnable; return _m; } factory Line.fromJson(Map<String, dynamic> json) { return Line( json['path'], color: json['color'], width: json['width'], isAllowPlay: json['isAllowPlay'], start: json['start'], end: json['end'], isMoveAlong: json['isMoveAlong'], endPointEnable: json["endPointEnable"]); } }enum MapLocale { /// 地图支持语言种类详见:https://developers.google.cn/maps/faq#languagesupport /// English (EN) United States en_us, /// Chinese (ZH) Simplified zh_cn, }import 'dart:ui'; import 'gmap/Line.dart'; import 'gmap/Lnglat.dart'; import 'gmap/MapLocale.dart'; import 'gmap/Marker.dart'; class MapOption { // 地图语言(默认英文) MapLocale locale; // 地图默认中心点 Lnglat center; // 显示控件(默认 开启) bool controlEnable; // 允许拖拽地图(默认 开启) bool dragEnable; // 双击缩放地图(默认 开启) bool doubleClickZoom; // 地图点 List<Marker> markers; // 地图线 List<Line> lines; // 地图缩放级别 num zoom; // 地图加载完成回调 final complete; // Marker点击回调 final markerClick; MapOption({this.locale = MapLocale.en_us, this.center, this.markers, this.lines, this.zoom, this.complete, this.markerClick, this.controlEnable = true, this.dragEnable = true, this.doubleClickZoom = true,}); @override String toString() { return 'GMapOption{center: $center, markers: $markers, lines: $lines, zoom: $zoom, controlEnable: $controlEnable, dragEnable:$dragEnable, doubleClickZoom:$doubleClickZoom }'; } Map<String, dynamic> toJson () { Map<String, dynamic> _m = new Map<String, dynamic>(); _m["center"] = this.center.toJson(); List<dynamic> _markers = []; if(null!= this.markers && this.markers.length > 0){ for(int i = 0;i < this.markers.length;i++){ _markers.add(this.markers[i].toJson()); } } _m["markers"] = _markers; List<dynamic> _lines = []; if(null != this.lines && this.lines.length > 0) { for(int i = 0;i < this.lines.length;i++){ _lines.add(this.lines[i].toJson()); } } _m["lines"] = _lines; _m["zoom"] = this.zoom; _m["dragEnable"] = this.dragEnable; _m["controlEnable"] = this.controlEnable; _m['doubleClickZoom'] = this.doubleClickZoom; return _m; } factory MapOption.fromJson(Map<String, dynamic> json) { return MapOption( center: json['center'], markers: json['markers'], lines: json['lines'], zoom: json['zoom'], dragEnable: json['dragEnable'], controlEnable: json['controlEnable'], doubleClickZoom: json['doubleClickZoom'] ); } }import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'Config.dart'; import 'MapOption.dart'; import 'gmap/Line.dart'; import 'gmap/MapLocale.dart'; import 'gmap/Marker.dart'; import 'package:webview_flutter/webview_flutter.dart'; class MapView extends StatefulWidget { MapOption mapOption; String cityName; AppBar appBar; WebViewController viewController; MapView({this.mapOption, this.cityName, this.appBar}); @override _MapViewState createState() => _MapViewState(); Object exceJavascript(String excute){ if(null != viewController){ viewController.evaluateJavascript('callbackDemo("Demo回调Demo回调Demo回调Demo回调Demo回调");').then((result) { //print('您可以在此处处理JS结果1'); return '您可以在此处处理JS结果1'; }); } } } class _MapViewState extends State<MapView> { bool _loading = true; WebView webViewx; WebViewController _vc; dynamic overlayEncode(dynamic item) { if(item is Marker) { return item.toJson(); }else if(item is Line) { return item.toJson(); } return item; } ///js与flutter交互 JavascriptChannel _alertJavascriptChannel(BuildContext context) { return JavascriptChannel( name: 'Toast',//invoke要和网页协商一致 onMessageReceived: (JavascriptMessage message) { if(null == widget.viewController){ setState(() { widget.viewController = _vc; }); } var msg = json.decode(message.message); //print(msg['isFirstComplete']); if(null != msg['isFirstComplete'] && true == msg['isFirstComplete']){ // 地图首次加载 //print('地图首次加载完成'); if(null != widget.mapOption.markers && widget.mapOption.markers.length > 0){/// 回调页面绘制点的方法 var _markers = json.encode(widget.mapOption.markers, toEncodable: overlayEncode); widget.viewController.evaluateJavascript('setMarkers(\''+_markers+'\');').then((result) { //print('您可以在此处处理绘制Marker的业务逻辑'); if(null != widget.mapOption.zoom && widget.mapOption.zoom > 0 && widget.mapOption.zoom < 22){ widget.viewController.evaluateJavascript('setZoom(\''+widget.mapOption.zoom.toString()+'\');').then((result) { //print('您可以在此处处理地图缩放的业务逻辑'); }); } }); }else if(null != widget.mapOption.zoom && widget.mapOption.zoom > 0 && widget.mapOption.zoom < 22){ widget.viewController.evaluateJavascript('setZoom(\''+widget.mapOption.zoom.toString()+'\');').then((result) { //print('您可以在此处处理地图缩放的业务逻辑'); }); } widget.mapOption.complete(widget.viewController, msg); }else{ // 业务回调 if(null != msg['MARKER_CLICK'] && true == msg['MARKER_CLICK']){// Marker Click 回调 //print('Marker Click 回调'); widget.mapOption.markerClick(widget.viewController, msg); } } }); } String getLanguage(MapLocale _mapLocale) { String _language = ""; switch (_mapLocale) { case MapLocale.en_us: _language = "en"; break; case MapLocale.zh_cn: _language = "zh"; break; default: _language = "en"; } return _language; } @override void initState() { String _serverUrl = "http://map.bkybk.com/gmap_flutter.html?"; var _UrlParam = ""; if(null != widget.mapOption.center){ _UrlParam += "center=" + widget.mapOption.center.lng.toString() + "," + widget.mapOption.center.lat.toString(); } if(null != widget.mapOption.controlEnable && false == widget.mapOption.controlEnable){ if(0 < _UrlParam.length){ _UrlParam += "&"; } _UrlParam += "controlEnable=0"; } if(null != widget.mapOption.dragEnable && false == widget.mapOption.dragEnable){ if(0 < _UrlParam.length){ _UrlParam += "&"; } _UrlParam += "dragEnable=0"; } if(null != widget.mapOption.doubleClickZoom && false == widget.mapOption.doubleClickZoom){ if(0 < _UrlParam.length){ _UrlParam += "&"; } _UrlParam += "doubleClickZoom=0"; } if(Platform.isIOS){ if(0 < _UrlParam.length){ _UrlParam += "&"; } _UrlParam += "platform=IOS"; } else { if(0 < _UrlParam.length) { _UrlParam += "&"; } _UrlParam += "platform=Android"; } /// 地图语言 if(0 < _UrlParam.length){ _UrlParam += "&"; } String _language = getLanguage(widget.mapOption.locale); _UrlParam += "language="+_language; _serverUrl = _serverUrl + _UrlParam; super.initState(); //使用插件 FaiWebViewWidget webViewx = WebView( initialUrl: _serverUrl,///初始化url javascriptMode: JavascriptMode.unrestricted,///JS执行模式), onWebViewCreated: (WebViewController webViewController) {///在WebView创建完成后调用,只会被调用一次 //setState(() { _vc = webViewController; widget.viewController = webViewController; //}); //widget._viewController = webViewController; }, onPageFinished: (String url) {///页面加载完成回调 setState(() { _loading = false; }); // TODO 去掉遮罩(遮罩层未实现) }, javascriptChannels: <JavascriptChannel>[///JS和Flutter通信的Channel; _alertJavascriptChannel(context), ].toSet(), navigationDelegate: (NavigationRequest request) {//路由委托(可以通过在此处拦截url实现JS调用Flutter部分); ///通过拦截url来实现js与flutter交互 if (request.url.startsWith('js://webview')) { // Fluttertoast.showToast(msg:'JS调用了Flutter By navigationDelegate'); print('blocking navigation to $request}'); return NavigationDecision.prevent;///阻止路由替换,不能跳转,因为这是js交互给我们发送的消息 } return NavigationDecision.navigate;///允许路由替换 }, ); } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: widget.appBar!=null?widget.appBar:null, body: buildRefreshHexWidget(), ); } Widget buildRefreshHexWidget() { return RefreshIndicator( //下拉刷新触发方法 onRefresh: () async{ print('refresh'); }, //设置webViewWidget child:webViewx, ); } }import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'common/Config.dart'; import 'common/MapOption.dart'; import 'common/MapView.dart'; import 'common/gmap/Lnglat.dart'; import 'common/gmap/MapLocale.dart'; void main(List<String> args) { App(); } class App extends StatefulWidget { @override AppState createState() => AppState(); } class AppState extends State<App> { static GlobalKey<NavigatorState> navigatorKey = GlobalKey(); @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('Google Maps examples')), backgroundColor: Colors.grey.shade200, body: MapView( appBar: null, mapOption: MapOption( locale: MapLocale.zh_cn, controlEnable: false, dragEnable: true, doubleClickZoom: true, center: Lnglat(116.396663, 39.912321), complete: (_viewController, value) { // 地图加载完成后回调 }, markerClick: (_viewController, res) { // 地图上marker点击回调 }), cityName: "北京", ), ), ); } }以上核心代码是我进行过整理和一定的简单封装,主要是方便使用。开始运行。 搞定!!!嵌套的页面地址是http://map.bkybk.com/gmap_flutter.html,关于这个html的源码我这里就贴了,感兴趣的可以自己去把页面的代码复制出来喔~
2019年12月03日
1,885 阅读
4 评论
0 点赞