Andorid/Flutter的Dictionary也叫Map
使用Flutter版Google Maps 跟 location
(定位還有一套geolocator好像也很厲害, 不過兩套都是Flutter Favorites)
要先去GCP生一把Google Maps API key
選擇Maps SDK for iOS並啟用
然後因為已經啟用iOS了, 所以畫面會變成這樣
iOS Runner的AppDelegate
一樣要import GoogleMaps
然後GMSServices.provideAPIKey
import UIKit
import Flutter
import GoogleMaps
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("你鑰匙掉了")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Android這邊請
<manifest ...
<application ...
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="YOUR KEY HERE"/>
如果需要不同平台用不同的key
可以申請兩隻
然後點進去設定
如果切換帳號會看到這個報錯
重新選取專案就好了
iOS:
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
Android:
如果是Flutter1.12之前才需要特別處理,看這裡
就是一個叫做GoogleMap的Widget
可以設定圖層類型與起始點
根據Fullter慣例
一樣是使用controller去控制widget
不過這邊用了一個沒見過的東西Completer
暫時沒研究
Completer<GoogleMapController> _controller = Completer();
GoogleMap(
mapType: MapType.terrain,
initialCameraPosition: _kGooglePlex,
myLocationEnabled: _enableMyLocation,
myLocationButtonEnabled: true,
markers: Set<Marker>.of(_markerMap.values),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
)
可以看到在init裡面有兩個參數都是跟my location有關的
myLocationButtonEnabled就是右下角的定位按鈕(預設為ture)
點了之後的邏輯套件已經處理好
iOS版的Google Maps應該沒有這麼方便⚠️⚠️⚠️
myLocationEnabled就是在地圖上要不要顯示藍點點(預設false)
若為true, 會要求權限
但是要求的時機也太醜了吧...就卡在這邊
用了addPostFrameCallback
好啦是有好一點...
抱歉小弟學藝不精
暫時先用delayed
initState兩秒後再要求(經實測XD, 1秒不要求, 3秒太久)
剛剛配對的controller就可以在這時使用
static final CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(_kGooglePlex));
因為是要請聖獸守護在自身四方(這人寫扣寫到頭殼壞掉)
所以要先取得目前位置
void _getCurrentLocation() async {
var location = new Location();
try {
_currentLocation = await location.getLocation();
print(_currentLocation);
} on Exception {
_currentLocation = null;
}
_addMarker(MarkerType.top);
_addMarker(MarkerType.bottom);
_addMarker(MarkerType.left);
_addMarker(MarkerType.right);
}
然後建立Marker, 加進GoogleMap的markers參數裡
注意兩點:
enum MarkerType {top, bottom, left, right}
Map<MarkerId, Marker> _markerMap = <MarkerId, Marker>{};
void _addMarker(MarkerType type) {
final MarkerId markerId = MarkerId("IDLF_$type");
final offset = 0.002;
LatLng latLng;
String snippet;
switch (type){
case MarkerType.top:
latLng = LatLng(_currentLocation.latitude + 0.001, _currentLocation.longitude);
snippet = "北~玄武";
break;
case MarkerType.bottom:
latLng = LatLng(_currentLocation.latitude - 0.003, _currentLocation.longitude);
snippet = "南~朱雀";
break;
case MarkerType.left:
latLng = LatLng(_currentLocation.latitude, _currentLocation.longitude - offset);
snippet = "左~青龍";
break;
case MarkerType.right:
latLng = LatLng(_currentLocation.latitude, _currentLocation.longitude + offset);
snippet = "右~白虎";
break;
}
final Marker marker = Marker(
markerId: markerId,
infoWindow: InfoWindow(title: "Hello~", snippet: snippet),
position: latLng,
);
if (!mounted) return;
setState(() {
_markerMap[markerId] = marker;
});
}
pub上面的範例就這樣...有點崩潰
後來發現是在這邊
然後套件裡面也有@@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:icofont_flutter/icofont_flutter.dart';
import 'package:location/location.dart';
enum MarkerType {top, bottom, left, right}
class LessonPageMap extends StatefulWidget {
@override
_LessonPageMapState createState() => _LessonPageMapState();
}
class _LessonPageMapState extends State<LessonPageMap> {
static final CameraPosition _kGooglePlex = CameraPosition(
target: LatLng(37.42796133580664, -122.085749655962),
zoom: 14.4746,
);
Completer<GoogleMapController> _controller = Completer();
bool _enableMyLocation = false;
Map<MarkerId, Marker> _markerMap = <MarkerId, Marker>{};
LocationData _currentLocation;
void _getCurrentLocation() async {
var location = new Location();
try {
_currentLocation = await location.getLocation();
print(_currentLocation);
} on Exception {
_currentLocation = null;
}
_addMarker(MarkerType.top);
_addMarker(MarkerType.bottom);
_addMarker(MarkerType.left);
_addMarker(MarkerType.right);
}
void _addMarker(MarkerType type) {
final MarkerId markerId = MarkerId("IDLF_$type");
final offset = 0.002;
LatLng latLng;
String snippet;
switch (type){
case MarkerType.top:
latLng = LatLng(_currentLocation.latitude + 0.001, _currentLocation.longitude);
snippet = "北~玄武";
break;
case MarkerType.bottom:
latLng = LatLng(_currentLocation.latitude - 0.003, _currentLocation.longitude);
snippet = "南~朱雀";
break;
case MarkerType.left:
latLng = LatLng(_currentLocation.latitude, _currentLocation.longitude - offset);
snippet = "左~青龍";
break;
case MarkerType.right:
latLng = LatLng(_currentLocation.latitude, _currentLocation.longitude + offset);
snippet = "右~白虎";
break;
}
final Marker marker = Marker(
markerId: markerId,
infoWindow: InfoWindow(title: "Hello~", snippet: snippet),
position: latLng,
);
if (!mounted) return;
setState(() {
_markerMap[markerId] = marker;
});
}
@override
void initState() {
super.initState();
print("init");
print(DateTime.now());
Future.delayed(Duration(seconds: 2)).then((value) {
print("delayed");
print(DateTime.now());
setState(() {
_enableMyLocation = true;
_getCurrentLocation();
});
});
}
Future<void> _goToGoogle() async {
final GoogleMapController controller = await _controller.future;
controller.animateCamera(CameraUpdate.newCameraPosition(_kGooglePlex));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Map")),
body: GoogleMap(
mapType: MapType.terrain,
initialCameraPosition: _kGooglePlex,
myLocationEnabled: _enableMyLocation,
myLocationButtonEnabled: true,
markers: Set<Marker>.of(_markerMap.values),
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.startFloat,
floatingActionButton: FloatingActionButton(
child: Icon(IcoFontIcons.brandGoogle),
onPressed: _goToGoogle,
),
);
}
}
本集內容Android版請見:iOS Developer Learning Android. Lesson 22
下集預告:打包上架