zoukankan      html  css  js  c++  java
  • 停车场找不到自己的车停在哪儿?教你开发一款“Find My Car”App

    您会忘记您的车停在哪了吗?如果会,这款应用是您不错的一个选择!

    在本指南中,将实现如下功能:

    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 =
          "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  定位服务文档

     Directions API文档

    l  地图服务Demo项目

    l  定位服务Demo项目

    原文链接:https://developer.huawei.com/...
    原作者:胡椒

  • 相关阅读:
    JS判断页面是否加载完成
    简单的前端验证码
    如何让旧浏览器支持HTML5新标签
    JSON使用(4)
    JSON语法(3)
    JSON简介(2)
    JSON教程(1)
    jQuery-noConflict()
    jQuery
    jQuery
  • 原文地址:https://www.cnblogs.com/developer-huawei/p/14859654.html
Copyright © 2011-2022 走看看