狠狠撸

狠狠撸Share a Scribd company logo
怎樣在 Flutter App 中整合
Google Maps
Weizhong Yang a.k.a zonble
zonble@gmail.com
關於我
Weizhong Yang a.k.a zonble
? 最近?年都在做 App 開發
? Flutter GDE (2019-)
? Developer Manager at Cerence Inc (2020-)
? iOS Developer Lead at KKBOX (2011-2020)
? 今年做的事:講了?場 Flutter Desktop plugin 開發,下半年常跑??,開始重
寫??年前的《防區狀況三?效》的新章…
? Twitter @zonble
還記得我去年的題??
總之,今年我們把去年題?裡頭的那個 App 商品化了
Cerence
Link
這個 App 有哪些功能?
? 顯示 ODB 傳來的讀數(速度、溫度等)
? 在地圖上顯示?前??與?機的位置
? 顯示 Journey(每次??上?/熄?之間的駕駛紀錄)
? 路径规划:
? 怎樣?到我的???
? 怎樣去加油站/維修站/?訂地點?
? Geo Fence 規劃(在某個區域開?時發出警告)
? Curfew 規劃(在某個時間開?發出警告)
? 各種警告—急彎、急停、撞擊、進? Geo Fence
這個 App ?量需要顯示地圖
Agenda
? 放置地圖 Widget
? 新增 Marker
? 新增 Polyline
? 計算 zoom level
? 顯示 information window
? 移動地圖、設定樣式
? 其他…
會?到哪些 package?
? google_maps_
fl
utter: The o
ffi
cial package
? custom_info_window: Show custom window in the map
? google_place: Google Place API
? google_maps_utils: 地圖相關計算功能
?
fl
utter_polyline_points: encode/decode Google polyline string
基本導?
? 更新 pubspec.yaml
? 加? google_maps_
fl
utter
?
fl
utter pub get
? 需要?些平台權限,例如使? GPS 等,需要在 iOS 的 Info Plist 以及
Android 的 Manifest 裡頭加上對應設定
? Android 上需要安裝 Google Maps App
基本?法
GoogleMap(
minMaxZoomPreference: MinMaxZoomPreference(0, 16),
initialCameraPosition: LatLng(…..),
mapType: MapType.normal,
mapToolbarEnabled: false,
zoomControlsEnabled: false,
rotateGesturesEnabled: false,
scrollGesturesEnabled: false,
zoomGesturesEnabled: false,
myLocationButtonEnabled: false,
)
Platform View
? Google Maps Widget 是?個 Platform View
? 將 Native View 包進 Flutter 中
? 現在我們也可以在 Platform View 上重疊任意的 Flutter Widget
加上 Marker
fi
nal startPoint = await BitmapDescriptor.fromAssetImage(
ImageCon
fi
guration(size: Size(100, 100)), ‘asset/image.png’);
var markers = [];
fi
nal startPoint = Marker(
markerId: MarkerId('pin-start'),
icon: _startPoint,
anchor: O
ff
set(0.5, 0.5),
position: LatLng(….)),
zIndex: 10,
);
markers.add(startPoint);
GoogleMap(markers:markers); Image 的載?可以使? Future Builder
需要注意載?的解析度(@2x、@3x)
放置 SVG Marker
import 'dart:ui' as ui;
import ‘package:
fl
utter_svg/
fl
utter_svg.dart';
static Future<BitmapDescriptor?>
bitmapDescriptorFromSvgAsset(
BuildContext context, String assetName, Size
size) async {
// Read SVG
fi
le as String
String svgString =
await
DefaultAssetBundle.of(context).loadString(assetNa
me);
// Create DrawableRoot from SVG String
DrawableRoot svgDrawableRoot = await
svg.fromSvgString(svgString, 'a');
// toPicture() and toImage() don't seem to be
pixel ratio aware, so we calculate the actual sizes
here
MediaQueryData queryData =
MediaQuery.of(context);
double devicePixelRatio =
queryData.devicePixelRatio;
double width =
size.width * devicePixelRatio; // where 32 is
your SVG's original width
double height = size.height * devicePixelRatio; //
same thing
// Convert to ui.Picture
ui.Picture picture =
svgDrawableRoot.toPicture(size: Size(width,
height));
ui.Image image = await
picture.toImage(width.toInt(), height.toInt());
ByteData? bytes = await
image.toByteData(format:
ui.ImageByteFormat.png);
fi
nal bu
ff
er = bytes?.bu
ff
er;
if (bu
ff
er != null) {
return
BitmapDescriptor.fromBytes(bu
ff
er.asUint8List());
}
return null;
}
加上 Polyline
fi
nal points = <LatLng>[……];
var lines = <Polyline>{};
fi
nal line = Polyline(
polylineId: PolylineId('planning_route'),
color: planningRouteColor,
width: 6,
points: points,
);
lines.add(line);
GoogleMap(polylines: polylines);
根據 route 計算 zoom level
num latRad(lat) {
var sin = math.sin(lat * math.pi / 180);
var radX2 = math.log((1 + sin) / (1 - sin)) / 2;
return math.max(math.min(radX2, math.pi),
-math.pi) / 2;
}
num zoom(mapPx, worldPx, fraction) {
if (fraction == 0) {
return 21;
}
fi
nal left = math.log(mapPx / worldPx / fraction);
fi
nal result = (left.
fl
oor() / math.ln2);
return result;
}
List<num> _getRegion(num screenWidth, num screenHeight) {
num zoomMax = 16.0;
num? minLong = ….;
num? maxLong = ….;
num? minLat = ….;
num? maxLat = ….;
fi
nal latFraction = (latRad(maxLat) - latRad(minLat)) / math.pi;
fi
nal lngDi
ff
= maxLong - minLong;
fi
nal lngFraction = ((lngDi
ff
< 0) ? (lngDi
ff
+ 360) : lngDi
ff
) / 360;
num latZoom = zoom(screenHeight, 256, latFraction);
num lngZoom = zoom(screenWidth, 256, lngFraction);
fi
nal zoomLevel = math.min(math.min(latZoom, lngZoom) *
0.99, zoomMax);
fi
nal centerLat = (maxLat + minLat) / 2;
fi
nal centerLong = (maxLong + minLong) / 2;
return [centerLat, centerLong, zoomLevel];
}
Custom Info Window
custom_info_window
https://pub.dev/packages/custom_info_window
Custom Info Window
Stack(
children: [
PreloadMapWrapper(
child: GoogleMap(
onCameraMove: (position) {
_customInfoWindowController.onCameraMove?.call();
},
onMapCreated: (GoogleMapController controller) {
_customInfoWindowController.googleMapController = controller;
_mapController = controller;
}, ),
),
CustomInfoWindow(
controller: _customInfoWindowController,
width: 274,
height: 189,
o
ff
set: 30,
), ],
);
var _customInfoWindowController = CustomInfoWindowController();
_customInfoWindowController.addInfoWindow?.call(window, LatLng(…));
_customInfoWindowController.hideInfoWindow?.call();
Circle
fi
nal pin = Circle(
circleId: CircleId('circle'),
radius: radius.toDouble(),
fi
llColor: ….,
strokeColor: ….,
center: location,
);
fi
nal circles = <Circle>{};
circles.add(pin);
GoogleMap(
circles: circles
)
如何更新地圖內容
? 可以透過 setState() 更新包含 GoogleMap Widget 的 Widget
? 如果使? Bloc,可以將 GoogleMap Widget 放在 BlocBuilder 中
? 以上?式可以修改有哪些 Marker、Polyline 與 Circle,不會改變地圖的
zoom level 與中央位置
移動地圖
Future<void> goTo(num latitude, num longitude) async {
fi
nal position = CameraPosition(
target: LatLng(latitude.toDouble(), longitude.toDouble()),
zoom: zoomLevel,
);
_mapController?.animateCamera(CameraUpdate.newCameraPosition(position));
}
GoogleMapController? _mapController
GoogleMap(
onMapCreated: (GoogleMapController controller) {
_mapController = controller;
},
),
進階設置
? liteModeEnabled:?在完全靜態的地圖上
? indoorViewEnabled:顯示室內導航
? tra
ffi
cEnabled:顯示交通狀況
? buildingsEnabled:顯示建築物模型
設定地圖樣式
? GoogleMap Widget 本身
沒有 light/dark mode
? ?是設置整個 Map 樣式
的 JSON
? https://
console.cloud.google.co
m/projectselector2/
google/maps-apis/studio/
styles?pli=1
設定地圖樣式
rootBundle.loadString('assets/map_style.txt').then((string) {
_mapStyle = string;
});
GoogleMap(
onMapCreated: (GoogleMapController controller) {
mapController = controller;
mapController.setMapStyle(_mapStyle);
}
);
這個 Future 也可以考慮放在 FutureBuilder 裡頭
疑難雜症
? Android 上,如果 App 放在背景再回到前景,之後 GoogleMap Widget 可
能畫?花掉,或是有奇怪的狀況
? 可以? WidgetsBindingObserver 偵測回到前景重繪
(didChangeAppLifecycleState)
? 重新 Build GoogleMap Widget 也不?得會重繪
? 但是呼叫 setMapStyle ?定可以重繪
地点搜寻
地点搜寻
fi
nal _googlePlace = GooglePlace(kGooglePlacesApikey);
Future<TextSearchResponse?> _callGooglePlaceAPI(String keyword, {
required double lat,
required double lng
}) async {
return await _googlePlace.search.getTextSearch(keyword,
location: Location(lat: lat, lng: lng),
);
}
路径规划
路径规划
PolylineResult result = await PolylinePoints().getRouteBetweenCoordinates(
kGoogleDirectionsApiKey,
PointLatLng(lat1, lng1),
PointLatLng(lat2, lng2),
travelMode: TravelMode.driving,
);
import 'package:
fl
utter_polyline_points/
fl
utter_polyline_points.dart';
路徑偏移(Route deviation)
? 判斷?前的 GPS 位置是否偏移規劃的路徑
? 可以使? google_map_polyutil
? 呼叫 PolyUtils.isLocationOnEdgeTolerance,判斷座標是否在路徑上
? 還有其他?具
Recap
? 放置地圖 Widget
? 新增 Marker
? 新增 Polyline
? 計算 zoom level
? 顯示 information window
? 移動地圖、設定樣式
? 其他…
That’s all
謝謝?家

More Related Content

What's hot (20)

PDF
20210119 AWS Black Belt Online Seminar AWS CloudTrail
Amazon Web Services Japan
?
PDF
Amazon VPC? ELB/Direct Connect/VPN ???? - ???, AWS ???? ????
Amazon Web Services Korea
?
PDF
Amazon s3へのテ?ータ転送における課題とその対処法を一挙紹介
Tetsunori Nishizawa
?
PDF
[2019] ???, ???! Reactive? ?? Spring Kafka
NHN FORWARD
?
PDF
The Apollo and GraphQL Stack
Sashko Stubailo
?
PPTX
What is AWS Cloud Watch
jeetendra mandal
?
PDF
??????(Observability) ??? ???? ??????? ????? - ??? AWS ???? ???? :: AWS Summi...
Amazon Web Services Korea
?
PPTX
Spring integration
Dominik Strzy?ewski
?
PDF
AWS Summit Seoul 2023 | ??? ?? BI, QuickSight
Amazon Web Services Korea
?
PPTX
IBM JVM ?? - Oracle JVM ? ??
JungWoon Lee
?
PDF
クララオンラインが狈别迟蝉办辞辫别を选んだ理由
Kyohei Komatsu
?
PDF
?? ?? ???
jieunsys
?
PDF
AWS DirectConnect ?? ??? (???) - ??? ??? ???
Amazon Web Services Korea
?
PPTX
失败から学ぶ础奥厂の监视
株式会社オプト 仙台ラボラトリ
?
PDF
[MeetUp][2nd] ?????_?????_????_v1.2
InfraEngineer
?
PDF
AWS Black Belt Techシリーズ Elastic Load Balancing (ELB)
Amazon Web Services Japan
?
PPTX
Vmware vSphere Api Best Practices
Pablo Roesch
?
PPTX
Dependency injection using Google guice
Aman Verma
?
PDF
Angular 2 observables
Geoffrey Filippi
?
PDF
AWS Systems manager 入門
Serverworks Co.,Ltd.
?
20210119 AWS Black Belt Online Seminar AWS CloudTrail
Amazon Web Services Japan
?
Amazon VPC? ELB/Direct Connect/VPN ???? - ???, AWS ???? ????
Amazon Web Services Korea
?
Amazon s3へのテ?ータ転送における課題とその対処法を一挙紹介
Tetsunori Nishizawa
?
[2019] ???, ???! Reactive? ?? Spring Kafka
NHN FORWARD
?
The Apollo and GraphQL Stack
Sashko Stubailo
?
What is AWS Cloud Watch
jeetendra mandal
?
??????(Observability) ??? ???? ??????? ????? - ??? AWS ???? ???? :: AWS Summi...
Amazon Web Services Korea
?
Spring integration
Dominik Strzy?ewski
?
AWS Summit Seoul 2023 | ??? ?? BI, QuickSight
Amazon Web Services Korea
?
IBM JVM ?? - Oracle JVM ? ??
JungWoon Lee
?
クララオンラインが狈别迟蝉办辞辫别を选んだ理由
Kyohei Komatsu
?
?? ?? ???
jieunsys
?
AWS DirectConnect ?? ??? (???) - ??? ??? ???
Amazon Web Services Korea
?
失败から学ぶ础奥厂の监视
株式会社オプト 仙台ラボラトリ
?
[MeetUp][2nd] ?????_?????_????_v1.2
InfraEngineer
?
AWS Black Belt Techシリーズ Elastic Load Balancing (ELB)
Amazon Web Services Japan
?
Vmware vSphere Api Best Practices
Pablo Roesch
?
Dependency injection using Google guice
Aman Verma
?
Angular 2 observables
Geoffrey Filippi
?
AWS Systems manager 入門
Serverworks Co.,Ltd.
?

Similar to 怎樣在 Flutter app 中使用 Google Maps (9)

DOC
Google map api接口整理
lileinba
?
PPTX
2016輕鬆開發自有網路地圖工作坊 進階班 0701
family
?
PPTX
Android基礎課程2 - google map android API
Duran Hsieh
?
PPTX
Study mapapi v0.1
Paul Yang
?
PDF
iOS Map and Location
Ryan Chung
?
PDF
Google Maps 開始收費了該怎麼辦?
Mu Chun Wang
?
PPTX
再接再勵學 Swift 程式設計
政斌 楊
?
PDF
hiddenGem Attraction Recording Platform : 狠狠撸
柏軒 鄒
?
ODP
行动装置奥别产地图
c34031328
?
Google map api接口整理
lileinba
?
2016輕鬆開發自有網路地圖工作坊 進階班 0701
family
?
Android基礎課程2 - google map android API
Duran Hsieh
?
Study mapapi v0.1
Paul Yang
?
iOS Map and Location
Ryan Chung
?
Google Maps 開始收費了該怎麼辦?
Mu Chun Wang
?
再接再勵學 Swift 程式設計
政斌 楊
?
hiddenGem Attraction Recording Platform : 狠狠撸
柏軒 鄒
?
行动装置奥别产地图
c34031328
?
Ad

More from Weizhong Yang (20)

PDF
Flutter BLE
Weizhong Yang
?
PDF
关於延长役期这件事情
Weizhong Yang
?
PDF
Dart null safety
Weizhong Yang
?
PDF
導入 Flutter 前你應該知道的事
Weizhong Yang
?
PDF
Github Actions
Weizhong Yang
?
PDF
iPlayground: CarPlay and MFI Hearing Aids
Weizhong Yang
?
PDF
CocoaPods private repo
Weizhong Yang
?
PDF
Flutter 踩雷心得
Weizhong Yang
?
PDF
那些年被蘋果 Ban 掉的 API
Weizhong Yang
?
PDF
給 iOS 工程師的 Vue.js 開發
Weizhong Yang
?
PDF
苦集滅道:透過開發客製 Sketch Plug-in 改善產品設計流程
Weizhong Yang
?
PDF
使用 switch/case 重構程式碼
Weizhong Yang
?
PDF
怎樣寫出比較沒有問題的 Code
Weizhong Yang
?
PDF
贪食蛇
Weizhong Yang
?
PDF
Aspect Oriented Programming
Weizhong Yang
?
PDF
Mac OS X 與 iOS 的 Audio API
Weizhong Yang
?
KEY
Html 5 native drag
Weizhong Yang
?
KEY
Retina mac
Weizhong Yang
?
KEY
Python 的文件系統
Weizhong Yang
?
KEY
Input Method Kit
Weizhong Yang
?
Flutter BLE
Weizhong Yang
?
关於延长役期这件事情
Weizhong Yang
?
Dart null safety
Weizhong Yang
?
導入 Flutter 前你應該知道的事
Weizhong Yang
?
Github Actions
Weizhong Yang
?
iPlayground: CarPlay and MFI Hearing Aids
Weizhong Yang
?
CocoaPods private repo
Weizhong Yang
?
Flutter 踩雷心得
Weizhong Yang
?
那些年被蘋果 Ban 掉的 API
Weizhong Yang
?
給 iOS 工程師的 Vue.js 開發
Weizhong Yang
?
苦集滅道:透過開發客製 Sketch Plug-in 改善產品設計流程
Weizhong Yang
?
使用 switch/case 重構程式碼
Weizhong Yang
?
怎樣寫出比較沒有問題的 Code
Weizhong Yang
?
贪食蛇
Weizhong Yang
?
Aspect Oriented Programming
Weizhong Yang
?
Mac OS X 與 iOS 的 Audio API
Weizhong Yang
?
Html 5 native drag
Weizhong Yang
?
Retina mac
Weizhong Yang
?
Python 的文件系統
Weizhong Yang
?
Input Method Kit
Weizhong Yang
?
Ad

怎樣在 Flutter app 中使用 Google Maps

  • 1. 怎樣在 Flutter App 中整合 Google Maps Weizhong Yang a.k.a zonble zonble@gmail.com
  • 2. 關於我 Weizhong Yang a.k.a zonble ? 最近?年都在做 App 開發 ? Flutter GDE (2019-) ? Developer Manager at Cerence Inc (2020-) ? iOS Developer Lead at KKBOX (2011-2020) ? 今年做的事:講了?場 Flutter Desktop plugin 開發,下半年常跑??,開始重 寫??年前的《防區狀況三?效》的新章… ? Twitter @zonble
  • 5. 這個 App 有哪些功能? ? 顯示 ODB 傳來的讀數(速度、溫度等) ? 在地圖上顯示?前??與?機的位置 ? 顯示 Journey(每次??上?/熄?之間的駕駛紀錄) ? 路径规划: ? 怎樣?到我的??? ? 怎樣去加油站/維修站/?訂地點? ? Geo Fence 規劃(在某個區域開?時發出警告) ? Curfew 規劃(在某個時間開?發出警告) ? 各種警告—急彎、急停、撞擊、進? Geo Fence
  • 7. Agenda ? 放置地圖 Widget ? 新增 Marker ? 新增 Polyline ? 計算 zoom level ? 顯示 information window ? 移動地圖、設定樣式 ? 其他…
  • 8. 會?到哪些 package? ? google_maps_ fl utter: The o ffi cial package ? custom_info_window: Show custom window in the map ? google_place: Google Place API ? google_maps_utils: 地圖相關計算功能 ? fl utter_polyline_points: encode/decode Google polyline string
  • 9. 基本導? ? 更新 pubspec.yaml ? 加? google_maps_ fl utter ? fl utter pub get ? 需要?些平台權限,例如使? GPS 等,需要在 iOS 的 Info Plist 以及 Android 的 Manifest 裡頭加上對應設定 ? Android 上需要安裝 Google Maps App
  • 10. 基本?法 GoogleMap( minMaxZoomPreference: MinMaxZoomPreference(0, 16), initialCameraPosition: LatLng(…..), mapType: MapType.normal, mapToolbarEnabled: false, zoomControlsEnabled: false, rotateGesturesEnabled: false, scrollGesturesEnabled: false, zoomGesturesEnabled: false, myLocationButtonEnabled: false, )
  • 11. Platform View ? Google Maps Widget 是?個 Platform View ? 將 Native View 包進 Flutter 中 ? 現在我們也可以在 Platform View 上重疊任意的 Flutter Widget
  • 12. 加上 Marker fi nal startPoint = await BitmapDescriptor.fromAssetImage( ImageCon fi guration(size: Size(100, 100)), ‘asset/image.png’); var markers = []; fi nal startPoint = Marker( markerId: MarkerId('pin-start'), icon: _startPoint, anchor: O ff set(0.5, 0.5), position: LatLng(….)), zIndex: 10, ); markers.add(startPoint); GoogleMap(markers:markers); Image 的載?可以使? Future Builder 需要注意載?的解析度(@2x、@3x)
  • 13. 放置 SVG Marker import 'dart:ui' as ui; import ‘package: fl utter_svg/ fl utter_svg.dart'; static Future<BitmapDescriptor?> bitmapDescriptorFromSvgAsset( BuildContext context, String assetName, Size size) async { // Read SVG fi le as String String svgString = await DefaultAssetBundle.of(context).loadString(assetNa me); // Create DrawableRoot from SVG String DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, 'a'); // toPicture() and toImage() don't seem to be pixel ratio aware, so we calculate the actual sizes here MediaQueryData queryData = MediaQuery.of(context); double devicePixelRatio = queryData.devicePixelRatio; double width = size.width * devicePixelRatio; // where 32 is your SVG's original width double height = size.height * devicePixelRatio; // same thing // Convert to ui.Picture ui.Picture picture = svgDrawableRoot.toPicture(size: Size(width, height)); ui.Image image = await picture.toImage(width.toInt(), height.toInt()); ByteData? bytes = await image.toByteData(format: ui.ImageByteFormat.png); fi nal bu ff er = bytes?.bu ff er; if (bu ff er != null) { return BitmapDescriptor.fromBytes(bu ff er.asUint8List()); } return null; }
  • 14. 加上 Polyline fi nal points = <LatLng>[……]; var lines = <Polyline>{}; fi nal line = Polyline( polylineId: PolylineId('planning_route'), color: planningRouteColor, width: 6, points: points, ); lines.add(line); GoogleMap(polylines: polylines);
  • 15. 根據 route 計算 zoom level num latRad(lat) { var sin = math.sin(lat * math.pi / 180); var radX2 = math.log((1 + sin) / (1 - sin)) / 2; return math.max(math.min(radX2, math.pi), -math.pi) / 2; } num zoom(mapPx, worldPx, fraction) { if (fraction == 0) { return 21; } fi nal left = math.log(mapPx / worldPx / fraction); fi nal result = (left. fl oor() / math.ln2); return result; } List<num> _getRegion(num screenWidth, num screenHeight) { num zoomMax = 16.0; num? minLong = ….; num? maxLong = ….; num? minLat = ….; num? maxLat = ….; fi nal latFraction = (latRad(maxLat) - latRad(minLat)) / math.pi; fi nal lngDi ff = maxLong - minLong; fi nal lngFraction = ((lngDi ff < 0) ? (lngDi ff + 360) : lngDi ff ) / 360; num latZoom = zoom(screenHeight, 256, latFraction); num lngZoom = zoom(screenWidth, 256, lngFraction); fi nal zoomLevel = math.min(math.min(latZoom, lngZoom) * 0.99, zoomMax); fi nal centerLat = (maxLat + minLat) / 2; fi nal centerLong = (maxLong + minLong) / 2; return [centerLat, centerLong, zoomLevel]; }
  • 17. Custom Info Window Stack( children: [ PreloadMapWrapper( child: GoogleMap( onCameraMove: (position) { _customInfoWindowController.onCameraMove?.call(); }, onMapCreated: (GoogleMapController controller) { _customInfoWindowController.googleMapController = controller; _mapController = controller; }, ), ), CustomInfoWindow( controller: _customInfoWindowController, width: 274, height: 189, o ff set: 30, ), ], ); var _customInfoWindowController = CustomInfoWindowController(); _customInfoWindowController.addInfoWindow?.call(window, LatLng(…)); _customInfoWindowController.hideInfoWindow?.call();
  • 18. Circle fi nal pin = Circle( circleId: CircleId('circle'), radius: radius.toDouble(), fi llColor: …., strokeColor: …., center: location, ); fi nal circles = <Circle>{}; circles.add(pin); GoogleMap( circles: circles )
  • 19. 如何更新地圖內容 ? 可以透過 setState() 更新包含 GoogleMap Widget 的 Widget ? 如果使? Bloc,可以將 GoogleMap Widget 放在 BlocBuilder 中 ? 以上?式可以修改有哪些 Marker、Polyline 與 Circle,不會改變地圖的 zoom level 與中央位置
  • 20. 移動地圖 Future<void> goTo(num latitude, num longitude) async { fi nal position = CameraPosition( target: LatLng(latitude.toDouble(), longitude.toDouble()), zoom: zoomLevel, ); _mapController?.animateCamera(CameraUpdate.newCameraPosition(position)); } GoogleMapController? _mapController GoogleMap( onMapCreated: (GoogleMapController controller) { _mapController = controller; }, ),
  • 21. 進階設置 ? liteModeEnabled:?在完全靜態的地圖上 ? indoorViewEnabled:顯示室內導航 ? tra ffi cEnabled:顯示交通狀況 ? buildingsEnabled:顯示建築物模型
  • 22. 設定地圖樣式 ? GoogleMap Widget 本身 沒有 light/dark mode ? ?是設置整個 Map 樣式 的 JSON ? https:// console.cloud.google.co m/projectselector2/ google/maps-apis/studio/ styles?pli=1
  • 23. 設定地圖樣式 rootBundle.loadString('assets/map_style.txt').then((string) { _mapStyle = string; }); GoogleMap( onMapCreated: (GoogleMapController controller) { mapController = controller; mapController.setMapStyle(_mapStyle); } ); 這個 Future 也可以考慮放在 FutureBuilder 裡頭
  • 24. 疑難雜症 ? Android 上,如果 App 放在背景再回到前景,之後 GoogleMap Widget 可 能畫?花掉,或是有奇怪的狀況 ? 可以? WidgetsBindingObserver 偵測回到前景重繪 (didChangeAppLifecycleState) ? 重新 Build GoogleMap Widget 也不?得會重繪 ? 但是呼叫 setMapStyle ?定可以重繪
  • 26. 地点搜寻 fi nal _googlePlace = GooglePlace(kGooglePlacesApikey); Future<TextSearchResponse?> _callGooglePlaceAPI(String keyword, { required double lat, required double lng }) async { return await _googlePlace.search.getTextSearch(keyword, location: Location(lat: lat, lng: lng), ); }
  • 28. 路径规划 PolylineResult result = await PolylinePoints().getRouteBetweenCoordinates( kGoogleDirectionsApiKey, PointLatLng(lat1, lng1), PointLatLng(lat2, lng2), travelMode: TravelMode.driving, ); import 'package: fl utter_polyline_points/ fl utter_polyline_points.dart';
  • 29. 路徑偏移(Route deviation) ? 判斷?前的 GPS 位置是否偏移規劃的路徑 ? 可以使? google_map_polyutil ? 呼叫 PolyUtils.isLocationOnEdgeTolerance,判斷座標是否在路徑上 ? 還有其他?具
  • 30. Recap ? 放置地圖 Widget ? 新增 Marker ? 新增 Polyline ? 計算 zoom level ? 顯示 information window ? 移動地圖、設定樣式 ? 其他…