HarmonyOS鸿蒙Next中教你开发一款“Find My Car”App,解决停车场找不到车的问题

HarmonyOS鸿蒙Next中教你开发一款“Find My Car”App,解决停车场找不到车的问题 您会忘记您的车停在哪了吗?如果会,这款应用是您不错的一个选择!

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

  • 使用华为地图服务来标记车辆的位置,并在华为地图上展示前往车辆所在位置的路径。
  • 使用华为定位服务来获取用户的当前位置。
  • 使用 Shared Preferences 来存储车辆停放位置数据。
  • 使用 Directions API 来规划前往车辆所在位置的路径。

重要:添加应用时,输入的应用包名应当与您的 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 类中设置位置,然后添加一个标记来展示车辆的位置。

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 按钮。

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.*

至此,我们实现了两大功能:位置存储以及回到存储数据所代表的位置。此外,还添加了一个 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,
      ),
    ),
  ],
)

更多关于HarmonyOS鸿蒙Next中教你开发一款“Find My Car”App,解决停车场找不到车的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中教你开发一款“Find My Car”App,解决停车场找不到车的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中开发“Find My Car”App,首先利用设备的GPS模块记录停车位置,并将坐标数据存储在本地或云端。通过鸿蒙的分布式能力,用户可以在不同设备上同步查看停车位置。结合地图API,实现导航功能,引导用户找到车辆。此外,可集成蓝牙或NFC技术,实现近距离车辆定位。最后,优化UI设计,确保用户体验流畅。

回到顶部