停車場找不到自己的車停在哪兒?教你開發一款“Find My Car”App

華為開發者論壇發表於2021-06-07

您會忘記您的車停在哪了嗎?如果會,這款應用是您不錯的一個選擇!

在本指南中,將實現如下功能:

l    使用 華為地圖服務來標記車輛的位置,並在華為地圖上展示前往車輛所在位置的路徑。

l    使用 華為定位服務來獲取使用者的當前位置。

l    使用 Shared Preferences 來儲存車輛停放位置資料。

l    使用 Directions API 來規劃前往車輛所在位置的路徑。

 

首先,您需要註冊一個華為開發者賬戶,並在 AppGallery Connect 中新增一個應用專案。開啟 地圖服務 定位服務 開關,以便在您的應用中使用服務。如果您沒有華為開發者賬戶,不清楚具體步驟,請參考如下連結:

l    註冊華為開發者賬戶

l    配置AGC 資訊

l    整合地圖服務Flutter 外掛

l    整合定位服務Flutter 外掛

重要 :新增應用時,輸入的應用包名應當與您的 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 =
      "
          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    定位服務文件

l     Directions API 文件

l    地圖服務Demo 專案

l    定位服務Demo 專案


原文連結: https://developer.huawei.com/consumer/cn/forum/topic/0201549190057190044?fid=18

原作者:胡椒

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69970551/viewspace-2775835/,如需轉載,請註明出處,否則將追究法律責任。

相關文章