1. 项目概述
最近在OpenHarmony平台上用Flutter开发了一款家具购买记录App,其中实现保修功能时踩了不少坑。这个功能看似简单,但涉及到数据模型设计、本地存储、时间计算等多个技术点。下面我就把整个实现过程拆解分享,特别适合刚接触Flutter和OpenHarmony开发的同行参考。
这款App的核心价值在于帮助用户管理家具购买后的保修信息。很多人在购买家具后,保修卡随手一放就找不到了,等需要维修时才发现已经过了保修期。我们的App就是要解决这个痛点,让用户能随时查看每件家具的保修状态。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与准备
2.1 Flutter与OpenHarmony的适配
选择Flutter开发OpenHarmony应用主要考虑两点:一是Flutter的跨平台特性可以复用已有代码,二是OpenHarmony对Flutter的支持已经比较成熟。目前Flutter 3.7+版本已经能很好地运行在OpenHarmony 3.2及以上系统上。
环境搭建时需要注意:
- 必须使用OpenHarmony 3.2+版本
- Flutter SDK需要3.7以上
- 开发工具推荐DevEco Studio 3.1+
提示:OpenHarmony的Flutter插件安装后,记得在build.gradle中添加openharmony依赖:
gradle复制dependencies { implementation 'org.openharmony.tpc.thirdparty:flutter_release:1.0.0' }
2.2 数据存储方案
考虑到家具购买记录需要长期保存,我们选择了Hive作为本地数据库。相比SQLite,Hive有这些优势:
- 零配置,开箱即用
- 高性能,特别适合频繁读写
- 支持复杂对象直接存储
安装Hive时需要添加这些依赖:
yaml复制dependencies:
hive: ^2.2.3
hive_flutter: ^1.1.0
path_provider: ^2.0.11
3. 核心功能实现
3.1 数据模型设计
家具保修功能需要记录这些关键信息:
- 家具基本信息(名称、品牌、型号)
- 购买信息(日期、价格、商家)
- 保修信息(期限、开始日期、条款)
对应的Dart模型类如下:
dart复制@HiveType(typeId: 0)
class FurnitureItem {
@HiveField(0)
final String name;
@HiveField(1)
final String brand;
@HiveField(2)
final DateTime purchaseDate;
@HiveField(3)
final int warrantyMonths;
// 计算保修到期日
DateTime get warrantyExpiry => purchaseDate.add(Duration(days: warrantyMonths * 30));
// 计算剩余保修天数
int get remainingWarrantyDays => warrantyExpiry.difference(DateTime.now()).inDays;
}
3.2 保修状态计算逻辑
保修状态分为三种:
- 在保(剩余天数>30)
- 即将到期(0<剩余天数≤30)
- 已过期(剩余天数≤0)
实现代码:
dart复制WarrantyStatus get warrantyStatus {
final remaining = remainingWarrantyDays;
if(remaining > 30) return WarrantyStatus.valid;
if(remaining > 0) return WarrantyStatus.expiring;
return WarrantyStatus.expired;
}
enum WarrantyStatus { valid, expiring, expired }
3.3 用户界面实现
主界面采用ListView展示所有家具,每个Item显示:
- 家具缩略图
- 名称和品牌
- 保修状态标签(不同颜色区分)
- 剩余天数/已过期提示
关键UI代码:
dart复制ListTile(
leading: Image.network(item.imageUrl),
title: Text(item.name),
subtitle: Text('${item.brand} · 购买于${DateFormat('yyyy-MM-dd').format(item.purchaseDate)}'),
trailing: _buildWarrantyBadge(item),
);
Widget _buildWarrantyBadge(FurnitureItem item) {
final status = item.warrantyStatus;
return Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: status == WarrantyStatus.valid
? Colors.green[100]
: status == WarrantyStatus.expiring
? Colors.orange[100]
: Colors.red[100],
borderRadius: BorderRadius.circular(12),
),
child: Text(
status == WarrantyStatus.valid
? '剩余${item.remainingWarrantyDays}天'
: status == WarrantyStatus.expiring
? '即将到期'
: '已过期',
style: TextStyle(color: Colors.black87),
),
);
}
4. 高级功能实现
4.1 保修到期提醒
使用flutter_local_notifications插件实现到期提醒:
dart复制Future<void> scheduleWarrantyNotification(FurnitureItem item) async {
if(item.remainingWarrantyDays > 30) return;
final notificationTime = item.warrantyExpiry.subtract(Duration(days: 1));
await flutterLocalNotificationsPlugin.schedule(
item.hashCode,
'保修即将到期提醒',
'您的${item.name}保修将在${item.remainingWarrantyDays}天后到期',
notificationTime,
NotificationDetails(
android: AndroidNotificationDetails(
'warranty_channel',
'保修提醒',
importance: Importance.high,
),
),
);
}
4.2 保修文件上传
允许用户上传保修卡照片,使用image_picker选择图片,然后通过dio上传到服务器:
dart复制Future<void> uploadWarrantyCard(FurnitureItem item) async {
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
if(image == null) return;
final formData = FormData.fromMap({
'furniture_id': item.id,
'file': await MultipartFile.fromFile(image.path),
});
try {
final response = await Dio().post(
'https://api.example.com/upload',
data: formData,
);
// 处理响应
} catch (e) {
// 错误处理
}
}
5. 性能优化技巧
5.1 列表性能优化
家具列表可能包含大量条目,需要优化滚动性能:
- 使用ListView.builder而非ListView
- 为列表项添加const构造函数
- 对图片使用cached_network_image
优化后的列表代码:
dart复制ListView.builder(
itemCount: furnitureList.length,
itemBuilder: (context, index) {
final item = furnitureList[index];
return _FurnitureListItem(
item: item,
onTap: () => _showDetail(item),
);
},
);
class _FurnitureListItem extends StatelessWidget {
const _FurnitureListItem({required this.item, this.onTap});
final FurnitureItem item;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return ListTile(
// ...同前
);
}
}
5.2 本地存储优化
Hive默认将所有数据加载到内存,对于大数据集需要分页加载:
dart复制Future<List<FurnitureItem>> getFurniturePage(int page, int pageSize) async {
final box = await Hive.openBox<FurnitureItem>('furniture');
final keys = box.keys;
final start = page * pageSize;
final end = min((page + 1) * pageSize, keys.length);
return keys
.skip(start)
.take(end - start)
.map((key) => box.get(key))
.whereType<FurnitureItem>()
.toList();
}
6. 常见问题与解决方案
6.1 日期计算错误
问题:跨年计算保修期时出现错误
解决:使用date_utils包处理月份计算
dart复制DateTime addMonths(DateTime date, int months) {
return DateTime(
date.year + months ~/ 12,
date.month + months % 12,
date.day,
);
}
6.2 OpenHarmony兼容性问题
问题:某些Flutter插件在OpenHarmony上无法使用
解决方案:
- 优先使用官方维护的插件
- 检查插件是否包含Android实现(大多数能在OpenHarmony运行)
- 必要时自行实现功能
6.3 图片加载慢
解决方案:
- 使用cached_network_image缓存图片
- 预先压缩图片再上传
- 对列表使用缩略图,详情页再加载原图
实现代码:
dart复制CachedNetworkImage(
imageUrl: item.thumbnailUrl,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
width: 60,
height: 60,
fit: BoxFit.cover,
);
7. 扩展功能思路
7.1 多设备同步
通过华为云服务实现数据跨设备同步:
- 集成AGC云数据库
- 实现冲突解决策略
- 添加同步状态提示
7.2 智能识别保修卡
使用ML Kit实现:
- 拍照识别保修卡上的关键信息
- 自动填充购买日期和保修期限
- 提取商家联系方式
7.3 维修记录管理
扩展功能:
- 记录每次维修情况
- 关联维修费用
- 统计家具维修频率
实现代码框架:
dart复制class MaintenanceRecord {
final DateTime date;
final double cost;
final String description;
final String serviceProvider;
// ...
}
在开发过程中,最大的体会是OpenHarmony平台对Flutter的支持已经相当完善,绝大多数功能都能顺利实现。唯一需要注意的是部分插件的兼容性,建议在开发初期就进行核心功能验证。对于家具保修这种需要精确日期计算的功能,一定要考虑闰年、不同月份天数等边界情况,最好编写单元测试覆盖这些场景。
