您會忘記您的車停在哪了嗎?如果會,這款應用是您不錯的一個選擇!
在本指南中,將實現如下功能:
l 使用華為地圖服務來標記車輛的位置,並在華為地圖上展示前往車輛所在位置的路徑。
l 使用華為定位服務來獲取用戶的當前位置。
l 使用Shared Preferences來存儲車輛停放位置數據。
l 使用Directions API來規划前往車輛所在位置的路徑。
首先,您需要注冊一個華為開發者賬戶,並在AppGallery Connect中添加一個應用項目。開啟“地圖服務”和“定位服務”開關,以便在您的應用中使用服務。如果您沒有華為開發者賬戶,不清楚具體步驟,請參考如下鏈接:
l 配置AGC信息
重要:添加應用時,輸入的應用包名應當與您的Flutter項目包名一致。
注意:下載agconnect-services.json文件前,請確保已開通所需的HMS服務。
權限
為正常使用HMS服務,您需要在AndroidManifest.xml文件中添加如下權限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
添加依賴
完成上述步驟后,需在pubspec.yaml文件中添加對所需HMS服務對應的Flutter插件的依賴。您可以在pub.dev中找到最新版本的插件。
dependencies:
flutter:
sdk: flutter
huawei_map: ^5.0.3+302
huawei_location: ^5.0.0+301
shared_preferences: ^0.5.12+4
http: ^0.12.2
添加插件依賴后,運行flutter pub get命令。
至此,所有添加的插件均已准備就緒。
申請定位權限並獲取位置
PermissionHandler _permissionHandler = PermissionHandler();
FusedLocationProviderClient _locationService = FusedLocationProviderClient();
Location _myLocation;
LatLng _center;
@override
void initState() {
requestPermission();
super.initState();
}
requestPermission() async {
bool hasPermission = await _permissionHandler.hasLocationPermission();
if (!hasPermission)
hasPermission = await _permissionHandler.requestLocationPermission();
if (hasPermission) getLastLocation();
}
getLastLocation() async {
_myLocation = await _locationService.getLastLocation();
setState(() {
_center = LocationUtils.locationToLatLng(_myLocation);
});
}
Location數據類型來自華為定位服務。LatLng數據類型來自華為地圖服務。調用getLastLocation方法時會獲取到一個Location取值,您需要將其轉換為LatLng取值,以便在HuaweiMap控件中使用。
class LocationUtils {
static LatLng locationToLatLng(Location location) =>
LatLng(location.latitude, location.longitude);
}
添加HuaweiMap控件和按鈕
如果_myLocation變量取值不是null,表示已獲取到用戶位置,且應用可以啟動並將該位置賦值給HuaweiMap控件中的目標屬性。
Stack(
children: [
HuaweiMap(
initialCameraPosition: CameraPosition(
target: _center,
zoom: _zoom,
),
markers: _markers,
polylines: _polylines,
mapType: MapType.normal,
tiltGesturesEnabled: true,
buildingsEnabled: true,
compassEnabled: true,
zoomControlsEnabled: true,
rotateGesturesEnabled: true,
myLocationButtonEnabled: true,
myLocationEnabled: true,
trafficEnabled: false,
),
Positioned(
left: 20,
top: 20,
child: _isCarParked
? CustomButton(
text: "Go to My Car",
onPressed: goToMyCar,
)
: CustomButton(
text: "Set Location",
onPressed: parkMyCar,
),
),
],
),
使用Stack封裝HuaweiMap控件,並添加按鈕。按鈕名稱和功能會隨車輛狀態的變化而改變。
停車並設置位置
void parkMyCar() {
getLastLocation();
Prefs.setCarLocation(_myLocation);
Prefs.setIsCarParked(true);
getCarStatus();
}
getLastLocation() async {
_myLocation = await _locationService.getLastLocation();
setState(() {
_center = LocationUtils.locationToLatLng(_myLocation);
});
}
getCarStatus() async {
_isCarParked = await Prefs.getIsCarParked();
setState(() {});
addMarker();
}
addMarker() async {
if (_isCarParked && _markers.isEmpty) {
LatLng carLocation = await Prefs.getCarLocation();
setState(() {
_markers.add(Marker(
markerId: MarkerId("myCar"),
position: carLocation,
));
});
}
}
添加位置時,獲取用戶的最后位置,更新_myLocation和_center,在使用SharedPreferences存儲數據的Prefs類中設置位置,然后添加一個標記來展示車輛的位置。
如下示例代碼中,創建了一個名為Prefs的helper類,並使用SharedPreferences將方法分開。
class Prefs {
static const String _latitude = "car_location_latitude";
static const String _longitude = "car_location_longitude";
static const String _isLocationSet = "is_location_set";
static void setCarLocation(Location location) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setDouble(_latitude, location.latitude);
prefs.setDouble(_longitude, location.longitude);
print("Car's location has been set to (${location.latitude}, ${location.longitude})");
}
static Future<LatLng> getCarLocation() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
double lat = prefs.getDouble(_latitude);
double lng = prefs.getDouble(_longitude);
return LatLng(lat, lng);
}
static void setIsCarParked(bool value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool(_isLocationSet, value);
}
static Future<bool> getIsCarParked() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool(_isLocationSet)?? false;
}
}
點擊Set Location按鈕后,將設置當前位置並在應用內存中使用SharedPreferences存儲位置。此外,該按鈕的名稱和功能也將改變,以便通過改變后的按鈕返回到停車位置。
查找車輛
在返回時,點擊Go to My Car按鈕。Directions API會查找一條從當前位置到停車位置的路線,然后應用會在華為地圖上通過折線來展示該路線。
void goToMyCar() async {
getLastLocation();
addMarker();
LatLng carLocation = await Prefs.getCarLocation();
DirectionRequest request = DirectionRequest(
origin: Destination(
lat: _myLocation.latitude,
lng: _myLocation.longitude,
),
destination: Destination(
lat: carLocation.lat,
lng: carLocation.lng,
),
);
DirectionResponse response = await DirectionUtils.getDirections(request);
drawRoute(response);
}
drawRoute(DirectionResponse response) {
if (_polylines.isNotEmpty) _polylines.clear();
var steps = response.routes[0].paths[0].steps;
for (int i = 0; i < steps.length; i++) {
for (int j = 0; j < steps[i].polyline.length; j++) {
_points.add(steps[i].polyline[j].toLatLng());
}
}
setState(() {
_polylines.add(
Polyline(
polylineId: PolylineId("route"),
points: _points,
color: Colors.redAccent),
);
});
}
使用Directions API時需特別注意,您需要在HTTP posting前將您編碼后的API key添加到URL地址末尾。可以通過encodeComponent方法來實現,如下代碼所示。
class ApplicationUtils {
static String encodeComponent(String component) => Uri.encodeComponent(component);
static const String API_KEY = "YOUR_API_KEY";
// HTTPS POST
static String url =
"https://mapapi.cloud.huawei.com/mapApi/v1/routeService/walking?key=" +
encodeComponent(API_KEY);
}
class DirectionUtils {
static Future<DirectionResponse> getDirections(DirectionRequest request) async {
var headers = <String, String>{
"Content-type": "application/json",
};
var response = await http.post(ApplicationUtils.url,
headers: headers, body: jsonEncode(request.toJson()));
if (response.statusCode == 200) {
DirectionResponse directionResponse =
DirectionResponse.fromJson(jsonDecode(response.body));
return directionResponse;
} else
throw Exception('Failed to load direction response');
}
}
例如,如果原始API key是ABC/DFG+*,則轉換結果為* ABC%2FDFG%2B*.*。
至此,我們實現了2大功能:位置存儲以及回到存儲數據所代表的位置。此外,還添加了一個floatingActionButton(浮動按鈕),用來重置位置數據和清屏。
clearScreen() {
Prefs.setIsCarParked(false);
Prefs.setCarLocation(null);
_markers.clear();
_polylines.clear();
getCarStatus();
}
Stack(
children: [
/*
* Other widgets
*/
Positioned(
left: 20,
bottom: 20,
child: FloatingActionButton(
backgroundColor: Colors.blueGrey,
child: Icon(Icons.clear),
onPressed: clearScreen,
),
),
],
),
您可以在GitHub頁面查看完整的代碼。頁面鏈接:GitHub
溫馨提示
l Directions API支持三種路徑規划:步行、騎行以及駕車。每種路徑規划對應的URL都不同。
l 添加API key到URL末尾前,請先對進行編碼。否則,您將無法獲取響應。
l 您可以在agconnect-services.json文件中查看您的API key。
更多詳情請點擊:
l 地圖服務文檔
l 定位服務文檔
原文鏈接:https://developer.huawei.com/...
原作者:胡椒