Flutter 框架跨平台鸿蒙开发 - 车辆保养记录器:智能管理车辆保养全流程

Flutter车辆保养记录器:智能管理车辆保养全流程

项目简介

车辆保养记录器是一款专业的Flutter应用,帮助车主记录和管理车辆保养信息。通过智能提醒功能,确保按时保养,延长车辆使用寿命。应用支持多车辆管理、费用统计、保养提醒等实用功能。
运行效果图


核心功能

  • 保养记录管理:添加、编辑、删除保养记录
  • 13种保养项目:机油、机滤、空滤、刹车片等常见项目
  • 多车辆支持:管理多辆车的保养记录
  • 智能提醒:提前7天提醒下次保养
  • 双重提醒:支持日期和里程数双重提醒
  • 费用统计:按项目统计保养费用
  • 数据持久化:本地保存所有记录
  • Tab切换:全部记录和保养提醒分页显示

技术特点

  • Material Design 3设计风格
  • TabBar多页面切换
  • Badge徽章提醒
  • 紧急提醒颜色区分
  • 响应式卡片布局
  • SharedPreferences数据持久化
  • 完整的表单验证

核心代码实现

1. 保养记录数据模型

dart 复制代码
class MaintenanceRecord {
  String id;
  String vehicleName;      // 车辆名称
  String project;          // 保养项目
  DateTime date;           // 保养日期
  double cost;             // 费用
  int mileage;             // 当前里程
  DateTime? nextReminder;  // 下次保养日期
  int? nextMileage;        // 下次保养里程
  String location;         // 保养地点
  String notes;            // 备注

  bool get needsReminder {
    if (nextReminder == null) return false;
    return DateTime.now().isAfter(
      nextReminder!.subtract(const Duration(days: 7))
    );
  }

  int get daysUntilReminder {
    if (nextReminder == null) return -1;
    return nextReminder!.difference(DateTime.now()).inDays;
  }
}

模型字段说明

字段 类型 说明
id String 唯一标识符
vehicleName String 车辆名称(如:本田雅阁)
project String 保养项目(如:机油更换)
date DateTime 保养日期
cost double 保养费用
mileage int 当前里程数
nextReminder DateTime? 下次保养提醒日期
nextMileage int? 下次保养里程数
location String 保养地点
notes String 备注信息

计算属性

  • needsReminder:是否需要提醒(提前7天)
  • daysUntilReminder:距离下次保养天数

2. TabBar多页面布局

dart 复制代码
class _MaintenanceListPageState extends State<MaintenanceListPage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('车辆保养记录器'),
        bottom: TabBar(
          controller: _tabController,
          tabs: const [
            Tab(text: '全部记录', icon: Icon(Icons.list)),
            Tab(text: '保养提醒', icon: Icon(Icons.alarm)),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          _buildAllRecordsTab(),
          _buildReminderTab(),
        ],
      ),
    );
  }
}

TabBar要点

  1. 使用SingleTickerProviderStateMixin提供动画支持
  2. TabController管理Tab状态
  3. TabBar显示标签页
  4. TabBarView显示对应内容
  5. 记得在dispose中释放TabController

3. 智能提醒系统

dart 复制代码
// 提醒判断逻辑
bool get needsReminder {
  if (nextReminder == null) return false;
  // 提前7天开始提醒
  return DateTime.now().isAfter(
    nextReminder!.subtract(const Duration(days: 7))
  );
}

// 获取提醒记录
List<MaintenanceRecord> get _reminderRecords {
  return _records.where((r) => r.needsReminder).toList()
    ..sort((a, b) => a.nextReminder!.compareTo(b.nextReminder!));
}

// Badge徽章显示
final reminderCount = _reminderRecords.length;
if (reminderCount > 0)
  IconButton(
    icon: Badge(
      label: Text('$reminderCount'),
      child: const Icon(Icons.notifications),
    ),
    onPressed: () {
      _tabController.animateTo(1);  // 跳转到提醒页面
    },
  ),

4. 紧急提醒卡片

dart 复制代码
Widget _buildReminderCard(MaintenanceRecord record) {
  final daysLeft = record.daysUntilReminder;
  final isUrgent = daysLeft <= 3;  // 3天内为紧急

  return Card(
    color: isUrgent ? Colors.red[50] : Colors.orange[50],
    child: Padding(
      padding: const EdgeInsets.all(16),
      child: Row(
        children: [
          Icon(
            isUrgent ? Icons.error : Icons.warning,
            color: isUrgent ? Colors.red : Colors.orange,
            size: 32,
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(record.project),
                Text(record.vehicleName),
              ],
            ),
          ),
          Container(
            padding: const EdgeInsets.symmetric(
              horizontal: 12,
              vertical: 6,
            ),
            decoration: BoxDecoration(
              color: isUrgent ? Colors.red : Colors.orange,
              borderRadius: BorderRadius.circular(20),
            ),
            child: Text(
              daysLeft <= 0 ? '今天' : '$daysLeft天后',
              style: const TextStyle(
                color: Colors.white,
                fontWeight: FontWeight.bold,
              ),
            ),
          ),
        ],
      ),
    ),
  );
}

提醒级别

  • 🔴 紧急(3天内):红色背景 + error图标
  • 🟠 普通(7天内):橙色背景 + warning图标

5. 费用统计功能

dart 复制代码
void _showStatistics() {
  // 按项目统计费用
  final projectStats = <String, double>{};
  for (var record in _records) {
    projectStats[record.project] =
        (projectStats[record.project] ?? 0) + record.cost;
  }

  showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('保养统计'),
      content: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('总记录数: ${_allRecords.length}'),
            Text(
              '总费用: ¥${_totalCost.toStringAsFixed(2)}',
              style: const TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.bold,
                color: Colors.red,
              ),
            ),
            const Divider(),
            const Text('各项目费用统计:'),
            ...projectStats.entries.map((entry) => Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(entry.key),
                Text('¥${entry.value.toStringAsFixed(2)}'),
              ],
            )),
          ],
        ),
      ),
    ),
  );
}

6. 多车辆筛选

dart 复制代码
// 获取所有车辆列表
List<String> get _vehicles {
  final vehicles = _records.map((r) => r.vehicleName).toSet().toList();
  return ['全部', ...vehicles];
}

// 筛选逻辑
List<MaintenanceRecord> get _allRecords {
  var filtered = _records;
  if (_filterVehicle != '全部') {
    filtered = filtered.where((r) => r.vehicleName == _filterVehicle).toList();
  }
  filtered.sort((a, b) => b.date.compareTo(a.date));
  return filtered;
}

// 筛选UI
Widget _buildVehicleFilter() {
  return Container(
    height: 60,
    child: ListView.builder(
      scrollDirection: Axis.horizontal,
      itemCount: _vehicles.length,
      itemBuilder: (context, index) {
        final vehicle = _vehicles[index];
        return FilterChip(
          label: Text(vehicle),
          selected: _filterVehicle == vehicle,
          onSelected: (selected) {
            setState(() {
              _filterVehicle = vehicle;
            });
          },
        );
      },
    ),
  );
}

7. 双重提醒设置

dart 复制代码
// 日期提醒
Card(
  child: ListTile(
    leading: const Icon(Icons.alarm),
    title: const Text('下次保养日期'),
    subtitle: Text(
      _nextReminder != null
          ? '${_nextReminder!.year}-${_nextReminder!.month.toString().padLeft(2, '0')}-${_nextReminder!.day.toString().padLeft(2, '0')}'
          : '未设置',
    ),
    onTap: () async {
      final picked = await showDatePicker(
        context: context,
        initialDate: _nextReminder ?? _date.add(const Duration(days: 180)),
        firstDate: _date,
        lastDate: DateTime.now().add(const Duration(days: 3650)),
      );
      if (picked != null) {
        setState(() {
          _nextReminder = picked;
        });
      }
    },
  ),
),

// 里程提醒
TextFormField(
  controller: _nextMileageController,
  decoration: const InputDecoration(
    labelText: '下次保养里程(km)',
    hintText: '例如:55000',
    prefixIcon: Icon(Icons.speed),
  ),
  keyboardType: TextInputType.number,
),

技术要点详解

TabController生命周期

TabController需要正确管理生命周期:

dart 复制代码
class _MyPageState extends State<MyPage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();  // 必须释放
    super.dispose();
  }
}

Set去重技巧

使用Set快速去重:

dart 复制代码
// 从记录中提取所有车辆名称并去重
final vehicles = _records.map((r) => r.vehicleName).toSet().toList();

// 等价于
final vehicleSet = <String>{};
for (var record in _records) {
  vehicleSet.add(record.vehicleName);
}
final vehicles = vehicleSet.toList();

Map统计技巧

使用Map进行分组统计:

dart 复制代码
final projectStats = <String, double>{};
for (var record in _records) {
  // 如果key不存在,使用0作为默认值
  projectStats[record.project] =
      (projectStats[record.project] ?? 0) + record.cost;
}

DateTime计算

常用的日期计算方法:

dart 复制代码
// 加减天数
final tomorrow = DateTime.now().add(const Duration(days: 1));
final yesterday = DateTime.now().subtract(const Duration(days: 1));

// 计算天数差
final days = date2.difference(date1).inDays;

// 判断日期先后
if (date1.isAfter(date2)) { }
if (date1.isBefore(date2)) { }

// 格式化日期
final formatted = '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';

保养项目说明

常见保养项目及周期

项目 建议周期 参考费用
机油更换 5000-10000km 200-500元
机滤更换 5000-10000km 50-100元
空滤更换 10000-20000km 50-150元
空调滤芯 10000-20000km 50-200元
刹车片 30000-50000km 300-800元
轮胎 50000-80000km 400-1000元/条
火花塞 30000-60000km 100-300元
变速箱油 40000-60000km 500-1500元
防冻液 2年 100-300元
电瓶 3-5年 300-800元
年检 1年 200-300元
保险 1年 3000-8000元

保养提醒算法







保养记录
设置了下次保养?
计算距离天数
不提醒
距离<=7天?
显示提醒
不提醒
距离<=3天?
紧急提醒-红色
普通提醒-橙色

功能扩展建议

1. 本地通知

集成flutter_local_notifications实现定时提醒:

dart 复制代码
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

class NotificationService {
  final FlutterLocalNotificationsPlugin _notifications =
      FlutterLocalNotificationsPlugin();

  Future<void> scheduleReminder(MaintenanceRecord record) async {
    if (record.nextReminder == null) return;

    await _notifications.zonedSchedule(
      record.id.hashCode,
      '保养提醒',
      '${record.vehicleName}的${record.project}即将到期',
      tz.TZDateTime.from(
        record.nextReminder!.subtract(const Duration(days: 7)),
        tz.local,
      ),
      const NotificationDetails(
        android: AndroidNotificationDetails(
          'maintenance_channel',
          '保养提醒',
          importance: Importance.high,
        ),
      ),
      uiLocalNotificationDateInterpretation:
          UILocalNotificationDateInterpretation.absoluteTime,
    );
  }
}

2. 数据导出

导出保养记录为Excel:

dart 复制代码
import 'package:excel/excel.dart';
import 'package:path_provider/path_provider.dart';

Future<void> exportToExcel(List<MaintenanceRecord> records) async {
  var excel = Excel.createExcel();
  Sheet sheet = excel['保养记录'];

  // 表头
  sheet.appendRow([
    '车辆名称',
    '保养项目',
    '保养日期',
    '费用',
    '里程',
    '下次保养',
    '地点',
    '备注'
  ]);

  // 数据行
  for (var record in records) {
    sheet.appendRow([
      record.vehicleName,
      record.project,
      '${record.date.year}-${record.date.month}-${record.date.day}',
      record.cost,
      record.mileage,
      record.nextReminder != null
          ? '${record.nextReminder!.year}-${record.nextReminder!.month}-${record.nextReminder!.day}'
          : '',
      record.location,
      record.notes,
    ]);
  }

  // 保存文件
  final directory = await getApplicationDocumentsDirectory();
  final file = File('${directory.path}/保养记录.xlsx');
  await file.writeAsBytes(excel.encode()!);
}

3. 照片记录

添加保养照片功能:

dart 复制代码
class MaintenanceRecord {
  // ... 其他字段
  List<String> photos;  // 照片路径列表
}

// 拍照功能
Future<void> _takePhoto() async {
  final picker = ImagePicker();
  final pickedFile = await picker.pickImage(source: ImageSource.camera);
  if (pickedFile != null) {
    setState(() {
      _photos.add(pickedFile.path);
    });
  }
}

// 显示照片
GridView.builder(
  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
    crossAxisSpacing: 8,
    mainAxisSpacing: 8,
  ),
  itemCount: record.photos.length,
  itemBuilder: (context, index) {
    return Image.file(File(record.photos[index]));
  },
)

4. 保养计划

预设保养计划模板:

dart 复制代码
class MaintenancePlan {
  String name;
  List<MaintenanceItem> items;
}

class MaintenanceItem {
  String project;
  int intervalDays;
  int intervalMileage;
}

// 预设计划
final plans = [
  MaintenancePlan(
    name: '标准保养计划',
    items: [
      MaintenanceItem(project: '机油更换', intervalDays: 180, intervalMileage: 5000),
      MaintenanceItem(project: '机滤更换', intervalDays: 180, intervalMileage: 5000),
      MaintenanceItem(project: '空滤更换', intervalDays: 365, intervalMileage: 10000),
    ],
  ),
];

5. 保养店管理

记录常用保养店信息:

dart 复制代码
class MaintenanceShop {
  String id;
  String name;
  String address;
  String phone;
  double rating;
  List<String> services;
}

// 选择保养店
Future<MaintenanceShop?> _selectShop() async {
  return await showDialog<MaintenanceShop>(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('选择保养店'),
      content: ListView.builder(
        itemCount: _shops.length,
        itemBuilder: (context, index) {
          final shop = _shops[index];
          return ListTile(
            title: Text(shop.name),
            subtitle: Text(shop.address),
            trailing: Text('⭐ ${shop.rating}'),
            onTap: () => Navigator.pop(context, shop),
          );
        },
      ),
    ),
  );
}

6. 图表统计

使用fl_chart显示费用趋势:

dart 复制代码
import 'package:fl_chart/fl_chart.dart';

Widget _buildCostChart() {
  // 按月统计费用
  final monthlyData = <int, double>{};
  for (var record in _records) {
    final month = record.date.month;
    monthlyData[month] = (monthlyData[month] ?? 0) + record.cost;
  }

  return LineChart(
    LineChartData(
      lineBarsData: [
        LineChartBarData(
          spots: monthlyData.entries
              .map((e) => FlSpot(e.key.toDouble(), e.value))
              .toList(),
          isCurved: true,
          color: Colors.blue,
        ),
      ],
    ),
  );
}

常见问题解答

Q1: 如何批量导入保养记录?

A: 可以通过CSV文件导入:

dart 复制代码
import 'package:csv/csv.dart';

Future<void> importFromCSV(String filePath) async {
  final file = File(filePath);
  final csvString = await file.readAsString();
  final rows = const CsvToListConverter().convert(csvString);

  for (var i = 1; i < rows.length; i++) {  // 跳过表头
    final row = rows[i];
    final record = MaintenanceRecord(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      vehicleName: row[0],
      project: row[1],
      date: DateTime.parse(row[2]),
      cost: double.parse(row[3].toString()),
      mileage: int.parse(row[4].toString()),
      location: row[5],
      notes: row[6],
    );
    _records.add(record);
  }
  _saveRecords();
}

Q2: 如何设置不同的提醒时间?

A: 添加提醒天数设置:

dart 复制代码
class MaintenanceRecord {
  // ... 其他字段
  int reminderDays;  // 提前提醒天数

  bool get needsReminder {
    if (nextReminder == null) return false;
    return DateTime.now().isAfter(
      nextReminder!.subtract(Duration(days: reminderDays))
    );
  }
}

// 在添加页面添加选择器
DropdownButton<int>(
  value: _reminderDays,
  items: [3, 7, 14, 30].map((days) {
    return DropdownMenuItem(
      value: days,
      child: Text('提前$days天'),
    );
  }).toList(),
  onChanged: (value) {
    setState(() {
      _reminderDays = value!;
    });
  },
)

Q3: 如何实现保养历史对比?

A: 添加历史对比功能:

dart 复制代码
Widget _buildHistoryComparison(String project) {
  final projectRecords = _records
      .where((r) => r.project == project)
      .toList()
    ..sort((a, b) => a.date.compareTo(b.date));

  return ListView.builder(
    itemCount: projectRecords.length,
    itemBuilder: (context, index) {
      final record = projectRecords[index];
      final previous = index > 0 ? projectRecords[index - 1] : null;

      return ListTile(
        title: Text('${record.date.year}-${record.date.month}-${record.date.day}'),
        subtitle: Text('¥${record.cost} | ${record.mileage}km'),
        trailing: previous != null
            ? Column(
                children: [
                  Text('间隔: ${record.date.difference(previous.date).inDays}天'),
                  Text('里程: ${record.mileage - previous.mileage}km'),
                ],
              )
            : null,
      );
    },
  );
}

Q4: 如何添加保养提醒推送?

A: 集成推送服务:

dart 复制代码
// 使用Firebase Cloud Messaging
import 'package:firebase_messaging/firebase_messaging.dart';

class PushNotificationService {
  final FirebaseMessaging _fcm = FirebaseMessaging.instance;

  Future<void> initialize() async {
    await _fcm.requestPermission();
    
    FirebaseMessaging.onMessage.listen((RemoteMessage message) {
      // 处理前台消息
      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          title: Text(message.notification?.title ?? ''),
          content: Text(message.notification?.body ?? ''),
        ),
      );
    });
  }

  Future<void> scheduleReminder(MaintenanceRecord record) async {
    // 调用后端API设置定时推送
    await http.post(
      Uri.parse('https://your-api.com/schedule'),
      body: jsonEncode({
        'token': await _fcm.getToken(),
        'time': record.nextReminder?.toIso8601String(),
        'title': '保养提醒',
        'body': '${record.vehicleName}的${record.project}即将到期',
      }),
    );
  }
}

项目总结

实现的功能

✅ 保养记录管理(增删改查)

✅ 13种常见保养项目

✅ 多车辆支持与筛选

✅ 智能提醒系统(提前7天)

✅ 双重提醒(日期+里程)

✅ 紧急提醒标识(3天内)

✅ 费用统计分析

✅ Tab分页显示

✅ Badge徽章提醒

✅ 数据本地持久化

技术亮点

  1. TabBar架构:清晰的多页面布局
  2. 智能提醒:提前7天自动提醒
  3. 紧急标识:3天内红色警告
  4. 双重提醒:日期和里程双重保障
  5. 费用统计:按项目分类统计
  6. 多车管理:支持多辆车记录
  7. 数据持久化:完整的JSON序列化

应用场景

  • 🚗 个人车辆保养管理
  • 🚕 家庭多车管理
  • 🚙 车队保养记录
  • 🔧 汽修店客户管理
  • 📊 保养费用分析
  • ⏰ 保养时间提醒

数据流程图

添加记录
保存到内存
JSON序列化
SharedPreferences
本地存储
启动应用
读取数据
JSON反序列化
加载到内存
显示界面

提醒逻辑流程







检查记录
有下次保养日期?
不提醒
计算剩余天数
剩余天数<=7?
不提醒
剩余天数<=3?
紧急提醒-红色
普通提醒-橙色
显示在提醒页
Badge徽章显示数量

车辆保养记录器是一款实用的车辆管理应用,通过智能提醒和详细记录,帮助车主科学管理车辆保养,延长车辆使用寿命,降低维护成本。

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

相关推荐
不会写代码0004 小时前
Flutter 框架跨平台鸿蒙开发 - 在线小说阅读器开发教程
flutter·华为·harmonyos
2501_944521594 小时前
Flutter for OpenHarmony 微动漫App实战:推荐动漫实现
android·开发语言·前端·javascript·flutter·ecmascript
ujainu5 小时前
Flutter for HarmonyOS 前置知识:Dart语言详解(中)
flutter
weixin_541299945 小时前
鸿蒙应用开发:保存应用数据 - 关系型数据库的使用
数据库·oracle·harmonyos
2501_944521596 小时前
Flutter for OpenHarmony 微动漫App实战:图片加载实现
android·开发语言·前端·javascript·flutter·php
新镜6 小时前
【Flutter】LTR/RTL 阿拉伯语言/希伯来语言
android·flutter·ios·客户端
夜雨声烦丿11 小时前
Flutter 框架跨平台鸿蒙开发 - 万年历应用开发教程
flutter·华为·harmonyos
Swift社区11 小时前
ArkUI 中 Flex 和 Grid 布局的选择与实践
harmonyos·arkui·flex
别退12 小时前
flutter_gradle_android
android·flutter