1. 项目概述:Flutter家庭账单共享本开发
作为一名有5年Flutter开发经验的工程师,我最近完成了一个面向HarmonyOS平台的"家庭账单共享本"应用开发。这个项目让我深刻体会到Flutter在跨平台开发中的优势,特别是在华为生态中的适配表现。这个应用的核心目标是解决家庭成员间账单记录和费用分摊的痛点,相比市面上复杂的财务管理软件,我们更注重简洁性和共享功能的实现。
技术选型方面,我们采用了Flutter 3.13版本配合HarmonyOS适配层,确保应用能在华为设备上流畅运行。UI方面完全遵循Material Design 3规范,同时针对HarmonyOS的设计语言做了适配调整。数据层目前采用内存存储实现核心逻辑,但架构上预留了无缝切换持久化存储的接口。
提示:虽然当前版本使用内存存储,但我在代码结构中已经为后续接入SQLite或华为HiChain做好了准备,这种前瞻性设计可以避免后期大规模重构。
2. 技术架构设计
2.1 整体架构方案
项目采用典型的MVVM架构,但针对Flutter特性做了优化调整。整个应用分为四个主要层次:
-
表现层:包含所有Widget实现,细分为:
- 页面组件(Page)
- 对话框组件(Dialog)
- 通用UI组件(Widgets)
-
业务逻辑层:处理核心业务规则,包括:
- 账单管理服务
- 成员管理服务
- 统计计算服务
-
数据模型层:定义核心数据结构:
dart复制class Bill { final String id; // UUID格式的唯一标识 final String title; // 账单标题(长度限制32字符) final double amount; // 金额(保留2位小数) // 其他字段... } -
数据访问层:当前为内存实现,但抽象了存储接口:
dart复制abstract class BillRepository { Future<List<Bill>> getBills(); Future<void> addBill(Bill bill); // 其他CRUD操作... }
2.2 HarmonyOS适配要点
在华为设备上运行时,我们特别注意了以下几点适配:
-
深色模式适配:通过
MediaQuery.of(context).platformBrightness检测系统主题,动态调整颜色方案。 -
鸿蒙特性集成:
dart复制void _initHarmonyOSFeatures() { if (Platform.isHarmonyOS) { // 启用鸿蒙动效引擎 HarmonyApp.setAnimationScale(1.0); // 配置分布式能力 _initDistributedData(); } } -
性能优化:
- 使用
RepaintBoundary隔离高频更新组件 - 对长列表采用
ListView.builder懒加载 - 避免在build方法中执行耗时操作
- 使用
3. 核心功能实现细节
3.1 账单管理模块
3.1.1 账单CRUD实现
添加账单时采用了表单验证组合策略:
dart复制final _formKey = GlobalKey<FormState>();
void _submitForm() {
if (_formKey.currentState!.validate() &&
_selectedCategory != null &&
_selectedPayer != null) {
// 验证通过,保存账单
}
}
验证规则包括:
- 标题:非空,长度≤32字符
- 金额:正数,保留2位小数
- 分类和付款人:必须选择
3.1.2 共享逻辑实现
账单分摊算法是核心创新点:
dart复制List<BillShare> calculateShares(Bill bill) {
final shareAmount = bill.amount / bill.sharedWith.length;
return bill.sharedWith.map((member) => BillShare(
member: member,
amount: shareAmount,
settled: false
)).toList();
}
我们特别处理了除不尽的情况:
dart复制// 处理金额除不尽的情况
final baseShare = bill.amount ~/ shares;
final remainder = (bill.amount % shares).toInt();
// 将余数平均分配到前几位成员
List<double> amounts = List.filled(shares, baseShare.toDouble());
for (int i = 0; i < remainder; i++) {
amounts[i] += 1;
}
3.2 统计展示模块
3.2.1 分类统计实现
分类统计采用聚合计算+可视化展示:
dart复制Map<String, double> getCategoryStats() {
return _bills.fold({}, (map, bill) {
if (bill.type == BillType.expense) {
map[bill.category] = (map[bill.category] ?? 0) + bill.amount;
}
return map;
});
}
可视化使用LinearProgressIndicator实现比例展示:
dart复制LinearProgressIndicator(
value: categoryAmount / totalExpense,
backgroundColor: Colors.grey[200],
valueColor: AlwaysStoppedAnimation(category.color),
)
3.2.2 时间维度统计
我们实现了灵活的时段统计:
dart复制enum StatPeriod { day, week, month, year }
List<Bill> getBillsByPeriod(StatPeriod period, DateTime date) {
switch (period) {
case StatPeriod.day:
return _bills.where((b) => isSameDay(b.date, date)).toList();
case StatPeriod.week:
return _bills.where((b) => isSameWeek(b.date, date)).toList();
// 其他case...
}
}
4. 关键UI组件实现
4.1 账单卡片组件
账单卡片采用了组合式设计:
dart复制class BillCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
child: InkWell(
onTap: () => _showDetails(context),
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
_buildCategoryIcon(),
SizedBox(width: 16),
_buildBillInfo(),
Spacer(),
_buildAmountDisplay(),
],
),
),
),
);
}
}
特别优化了点击反馈效果:
dart复制InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () {...},
splashColor: Theme.of(context).primaryColor.withOpacity(0.1),
highlightColor: Colors.transparent,
child: ...
)
4.2 统计图表组件
我们实现了自定义的圆环统计图:
dart复制CustomPaint(
painter: _RingChartPainter(
categories: _categories,
stats: _stats,
strokeWidth: 12,
),
size: Size(200, 200),
)
绘制逻辑核心代码:
dart复制void paint(Canvas canvas, Size size) {
double startAngle = -pi / 2;
final total = _stats.values.fold(0.0, (sum, amount) => sum + amount);
_stats.forEach((category, amount) {
final sweepAngle = (amount / total) * 2 * pi;
final paint = Paint()
..color = _getCategoryColor(category)
..style = PaintingStyle.stroke
..strokeWidth = strokeWidth;
canvas.drawArc(
Rect.fromCircle(center: size.center(Offset.zero), radius: size.width/2 - strokeWidth/2),
startAngle,
sweepAngle,
false,
paint,
);
startAngle += sweepAngle;
});
}
5. 性能优化实践
5.1 列表渲染优化
账单列表采用多项优化措施:
dart复制ListView.builder(
itemCount: _bills.length,
itemBuilder: (ctx, index) {
final bill = _bills[index];
return BillCard(
key: ValueKey(bill.id), // 使用唯一key
bill: bill,
// 其他参数...
);
},
)
我们还添加了动画效果优化:
dart复制AnimatedList(
initialItemCount: _bills.length,
itemBuilder: (context, index, animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
child: BillCard(...),
),
);
},
)
5.2 状态管理优化
采用Riverpod实现细粒度状态管理:
dart复制final billProvider = StateNotifierProvider<BillNotifier, List<Bill>>((ref) {
return BillNotifier();
});
class BillNotifier extends StateNotifier<List<Bill>> {
BillNotifier() : super([]);
void addBill(Bill bill) {
state = [...state, bill]; // 不可变更新
}
}
6. 常见问题与解决方案
6.1 数据同步问题
在多设备场景下,我们遇到了数据同步冲突。解决方案是:
dart复制class Bill {
final int version; // 版本号
final DateTime updatedAt; // 最后更新时间
// 合并策略:保留最新版本
static Bill merge(Bill local, Bill remote) {
return local.updatedAt.isAfter(remote.updatedAt) ? local : remote;
}
}
6.2 金额计算精度问题
处理浮点数精度采用了decimal库:
dart复制import 'package:decimal/decimal.dart';
Decimal calculateShare(Decimal total, int people) {
return total / Decimal.fromInt(people);
}
6.3 性能问题排查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 列表滚动卡顿 | 构建函数中有耗时操作 | 使用compute隔离耗时任务 |
| 页面切换慢 | 图片资源过大 | 使用cached_network_image并限制缓存大小 |
| 动画不流畅 | 没有使用RepaintBoundary |
隔离动画组件并启用硬件加速 |
7. 项目扩展方向
7.1 数据持久化方案
我们预留了多种存储方案接口:
dart复制abstract class StorageService {
Future<void> init();
Future<List<Bill>> loadBills();
Future<void> saveBill(Bill bill);
}
// 内存实现
class MemoryStorage implements StorageService {...}
// SQLite实现
class SqliteStorage implements StorageService {...}
// 华为HiChain实现
class HiChainStorage implements StorageService {...}
7.2 多平台适配计划
下一步将完善以下平台适配:
- 鸿蒙原子化服务
- 华为手表版本
- Web管理后台
7.3 高级功能路线图
-
智能分类:通过机器学习自动识别账单类别
dart复制Future<String> predictCategory(String description) async { final result = await MLKit.nlp.classify(description); return result.bestCategory; } -
预算管理:设置月度预算并提醒
dart复制class BudgetTracker { final ValueNotifier<double> remaining; void updateSpending(double amount) { remaining.value -= amount; if (remaining.value < 0) { _showOverBudgetAlert(); } } } -
语音记账:集成华为语音识别
dart复制void startVoiceInput() { final recognizer = HuaweiSpeechRecognizer(); recognizer.listen((text) { _parseVoiceCommand(text); }); }
在实际开发过程中,我发现Flutter在HarmonyOS上的表现相当出色,特别是热重载功能大大提高了UI调试效率。不过也遇到了一些鸿蒙特有API的集成问题,通过华为提供的Flutter插件库基本都能找到解决方案。对于想要尝试Flutter+鸿蒙开发的开发者,我的建议是从小项目开始,逐步熟悉华为生态的特殊性,同时充分利用Flutter的跨平台优势。