Flutter for OpenHarmony 汽车保养应用开发实战:八大功能模块从零到一

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

作者:maaath

一、前言

随着 OpenHarmony 生态的快速发展,Flutter 作为业界领先的跨平台框架,已经成功适配 OpenHarmony 系统。本文将带领读者使用 Flutter for OpenHarmony 开发一款功能完整的汽车保养应用,涵盖保养里程提醒、保养记录管理、加油油耗统计、维修店推荐、故障码诊断、保险到期提醒、年检提醒设置和行车日志记录八大核心功能模块。

本文假设读者已具备 Flutter 基础开发环境,并已完成 Flutter for OpenHarmony 的开发环境配置。我们将从数据模型设计开始,逐步构建完整的应用功能。

二、数据模型设计

良好的数据模型是应用稳定运行的基础。汽车保养应用涉及多个业务实体,我们需要合理设计它们之间的关系。

2.1 核心数据类

首先定义车辆信息模型,包含品牌、型号、车牌号、当前里程等基础信息:

class Car {
  final String id;
  final String brand;
  final String model;
  final String plateNumber;
  final int currentMileage;
  final String vin;
  final DateTime purchaseDate;
  final String engineType;
  final double fuelTankCapacity;

  Car({
    required this.id,
    required this.brand,
    required this.model,
    required this.plateNumber,
    required this.currentMileage,
    required this.vin,
    required this.purchaseDate,
    required this.engineType,
    required this.fuelTankCapacity,
  });
}

2.2 保养提醒模型

保养提醒是应用的核心功能之一。每个保养项目都有里程间隔和时间间隔两个维度,只要任一维度达到阈值就会触发提醒:

class MaintenanceReminder {
  final String id;
  final String carId;
  final String itemName;
  final int intervalMileage;
  final int intervalDays;
  final int lastMileage;
  final DateTime lastDate;
  final String description;
  final String importance;
  bool isEnabled;

  int get remainingMileage {
    return intervalMileage - (CarService().myCar?.currentMileage ?? 0 - lastMileage);
  }

  int get remainingDays {
    return intervalDays - DateTime.now().difference(lastDate).inDays;
  }

  bool get isOverdue {
    return remainingMileage <= 0 || remainingDays <= 0;
  }
}

这里通过计算属性 remainingMileageremainingDays 实时计算剩余里程和天数,isOverdue 则判断是否已过期。这种设计使得 UI 层无需关心计算逻辑,直接读取状态即可。

2.3 加油记录与油耗统计

加油记录需要记录加油量、单价、总价、里程等信息。油耗统计则基于这些数据进行计算:

class FuelRecord {
  final String id;
  final String carId;
  final DateTime date;
  final int mileage;
  final double liters;
  final double pricePerLiter;
  final double totalCost;
  final String fuelType;
  final String stationName;
  final bool isFullTank;
}

class FuelStatistics {
  final double totalLiters;
  final double totalCost;
  final double averageConsumption;
  final double costPerKm;
  final int totalDistance;
  final int recordCount;
}

2.4 故障码模型

故障码诊断功能内置了常见的 OBD-II 故障码数据,每个故障码包含严重程度、可能原因、解决方案和预估费用:

class FaultCode {
  final String id;
  final String code;
  final String name;
  final String description;
  final String severity;
  final String possibleCause;
  final String solution;
  final List<String> symptoms;
  final double estimatedCost;
  final bool canContinueDriving;
}

三、业务逻辑层设计

我们采用单例模式设计 CarService,统一管理所有业务数据和操作。这种设计模式在 Flutter 应用中非常常见,既保证了数据的全局一致性,又方便在各页面间共享状态。

class CarService {
  static final CarService _instance = CarService._();
  factory CarService() => _instance;
  CarService._();

  Car? _myCar;
  final List<MaintenanceReminder> _reminders = [];
  final List<MaintenanceRecord> _records = [];
  final List<FuelRecord> _fuelRecords = [];
  final List<RepairShop> _shops = [];
  final List<FaultCode> _faultCodes = [];
  final List<Insurance> _insurances = [];
  final List<AnnualInspection> _inspections = [];
  final List<DrivingLog> _drivingLogs = [];

  void initData() {
    if (_myCar != null) return;
    _initCar();
    _initReminders();
    _initRecords();
    _initFuelRecords();
    _initShops();
    _initFaultCodes();
    _initInsurances();
    _initInspections();
    _initDrivingLogs();
  }
  // ... 各初始化方法和业务方法
}

initData() 方法使用防重复加载机制,确保数据只初始化一次。各业务模块的初始化方法内部填充了贴近真实场景的模拟数据,方便读者快速体验完整功能。

四、UI 层实现

4.1 首页概览

首页采用 CustomScrollView + SliverAppBar 的组合,实现可折叠的顶部导航栏。顶部展示车辆基本信息、当前里程和待处理提醒数量,中部是车辆状态概览卡片,下方是八大功能入口网格。

Widget _buildQuickStatusCards() {
  final car = _carService.myCar!;
  final stats = _carService.getFuelStatistics();
  final overdueCount = _carService.reminders
      .where((r) => r.isEnabled && r.isOverdue).length;

  return Padding(
    padding: const EdgeInsets.all(16),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            const Icon(Icons.speed, color: Color(0xFF1565C0), size: 20),
            const SizedBox(width: 8),
            const Text(
              '车辆状态概览',
              style: TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.bold,
                color: Color(0xFF1565C0),
              ),
            ),
          ],
        ),
        const SizedBox(height: 12),
        Row(
          children: [
            Expanded(
              child: _buildStatusCard(
                '当前里程',
                '${car.currentMileage} km',
                Icons.speed,
                const Color(0xFF1565C0),
              ),
            ),
            const SizedBox(width: 10),
            Expanded(
              child: _buildStatusCard(
                '平均油耗',
                '${stats.averageConsumption.toStringAsFixed(1)} L/100km',
                Icons.local_gas_station,
                const Color(0xFFE65100),
              ),
            ),
          ],
        ),
      ],
    ),
  );
}

4.2 保养里程提醒页面

该页面将保养项目按"已过期"、“即将到期”、"已关闭"三个状态分类展示。每个卡片清晰展示保养项目名称、重要程度、里程间隔和时间间隔,以及剩余里程和天数。

Widget _buildReminderCard(MaintenanceReminder reminder, bool isOverdue) {
  final car = _carService.myCar!;
  final remainingMileage = reminder.intervalMileage -
      (car.currentMileage - reminder.lastMileage);
  final remainingDays = reminder.intervalDays -
      DateTime.now().difference(reminder.lastDate).inDays;

  return Container(
    margin: const EdgeInsets.only(bottom: 10),
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: isOverdue ? Colors.red.shade50 : Colors.white,
      borderRadius: BorderRadius.circular(12),
      border: Border.all(
        color: isOverdue ? Colors.red.shade200 : Colors.grey.shade200,
      ),
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Container(
              padding: const EdgeInsets.symmetric(
                horizontal: 8, vertical: 3,
              ),
              decoration: BoxDecoration(
                color: _getImportanceColor(reminder.importance)
                    .withValues(alpha: 0.15),
                borderRadius: BorderRadius.circular(6),
              ),
              child: Text(
                reminder.importance,
                style: TextStyle(
                  fontSize: 11,
                  fontWeight: FontWeight.bold,
                  color: _getImportanceColor(reminder.importance),
                ),
              ),
            ),
            const SizedBox(width: 8),
            Expanded(
              child: Text(
                reminder.itemName,
                style: const TextStyle(
                  fontSize: 15,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            Switch(
              value: reminder.isEnabled,
              onChanged: (_) {
                setState(() {
                  _carService.toggleReminder(reminder.id);
                });
              },
              activeColor: const Color(0xFFE53935),
            ),
          ],
        ),
        // ... 更多详情
      ],
    ),
  );
}

4.3 油耗统计与趋势图表

油耗统计页面使用 CustomPainter 绘制油耗趋势图,直观展示油耗变化趋势。同时支持添加加油记录,自动计算平均油耗和每公里费用。

class _ChartPainter extends CustomPainter {
  final List<FuelRecord> records;
  final double maxPrice;
  final double priceRange;

  
  void paint(Canvas canvas, Size size) {
    final consumptionPaint = Paint()
      ..color = const Color(0xFFE65100)
      ..strokeWidth = 2.5
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    final pricePaint = Paint()
      ..color = const Color(0xFF1565C0)
      ..strokeWidth = 2.5
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;

    // 绘制折线
    final consumptionPath = Path();
    final pricePath = Path();

    for (int i = 0; i < records.length; i++) {
      final x = i * stepX;
      final consumptionY = size.height * (1 - (
          records[i].liters * 100 /
          (records[i].mileage > 0 ? records[i].mileage : 1) /
          maxConsumption).clamp(0.0, 1.0));
      // ... 绘制逻辑
    }

    canvas.drawPath(consumptionPath, consumptionPaint);
    canvas.drawPath(pricePath, pricePaint);
  }
}

4.4 故障码诊断页面

故障码诊断支持按故障码编号或症状关键词搜索,可按严重程度筛选。每个故障码卡片展示编号、名称、描述、可能原因、解决方案和预估费用。

4.5 维修店推荐页面

维修店推荐支持按距离和评分排序,可按服务类型筛选。每个店铺卡片展示评分、距离、地址、电话、营业时间和服务项目,并提供拨打电话和导航按钮。

4.6 保险与年检提醒

保险提醒页面将保单按"已过期"、“即将到期”、"有效"三个状态分类。年检提醒页面支持设置提前提醒天数(7/15/30/60/90天),并附有年检规则小贴士。

4.7 行车日志记录

行车日志记录每次出行的起终点、时间、里程、油耗、过路费等信息,并统计累计里程和出行次数。

五、在首页集成汽车保养入口

在应用首页的快捷操作栏和更多服务网格中添加汽车保养入口:

final actions = [
  // ... 其他入口
  {'icon': Icons.directions_car, 'label': '汽车保养',
   'color': const Color(0xFF1565C0), 'page': const CarHomePage()},
];

六、运行效果截图

以下为应用在鸿蒙设备上的运行效果截图:

截图一:汽车保养首页
展示车辆状态概览、功能服务网格和最近动态,顶部 SliverAppBar 显示车辆信息和待处理提醒数量。
在这里插入图片描述

截图二:保养里程提醒页面
展示已过期和即将到期的保养项目列表,每个项目清晰显示剩余里程和天数。
在这里插入图片描述

截图三:加油油耗统计页面
展示油耗统计概览卡片和油耗趋势折线图,以及历史加油记录列表。
在这里插入图片描述

截图四:故障码诊断页面
展示故障码搜索和筛选功能,以及故障码详情卡片。
在这里插入图片描述

截图五:维修店推荐页面
展示附近维修店列表,支持按距离和评分排序。
在这里插入图片描述

截图六:行车日志记录页面
展示行车统计概览和出行记录列表。
在这里插入图片描述

七、总结

本文详细介绍了使用 Flutter for OpenHarmony 开发汽车保养应用的完整过程。通过合理的数据模型设计、单例模式的服务层封装以及 Material Design 3 风格的 UI 实现,我们成功构建了一个功能完整的汽车保养应用。

八大功能模块涵盖了汽车日常使用的核心需求:保养里程提醒帮助用户及时保养车辆;加油油耗统计让用户清晰了解油耗情况;维修店推荐方便用户找到优质服务商;故障码诊断帮助用户初步判断车辆故障;保险和年检提醒避免用户遗忘重要日期;行车日志记录则完整保存每次出行信息。

所有代码均已在 OpenHarmony 设备上验证通过,完整源码请访问 AtomGit(https://atomgit.com)获取。欢迎加入开源鸿蒙跨平台社区(https://openharmonycrossplatform.csdn.net)交流讨论,共同推动 Flutter for OpenHarmony 生态发展。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐