Flutter for OpenHarmony 实战:打造功能完整的记账助手应用
Flutter for OpenHarmony 记账助手应用开发实战 本文详细介绍了使用Flutter for OpenHarmony框架开发一款功能完整的记账助手应用的全过程。内容涵盖: 项目背景与功能规划:分析记账应用的核心需求,阐述Flutter在鸿蒙平台的优势 技术架构设计:采用MVVM分层架构,使用shared_preferences实现数据持久化 数据模型实现:Transaction交
Flutter for OpenHarmony 实战:打造功能完整的记账助手应用
文章目录
摘要
记账应用是移动应用中最实用的工具类应用之一。本文将详细介绍如何使用Flutter for OpenHarmony框架开发一款功能完整的记账助手应用。文章涵盖了数据模型设计、数据持久化方案、UI界面设计、收支统计等核心技术点。通过本文学习,读者将掌握Flutter在鸿蒙平台上开发工具类应用的完整流程,了解数据持久化和状态管理的最佳实践。
一、项目背景与功能概述
1.1 记账应用的需求分析
个人财务管理是现代生活中的重要组成部分,一款优秀的记账应用应该满足以下核心需求:
基础功能模块
- 收入/支出记录添加
- 交易历史查看
- 余额实时统计
- 分类管理
- 数据持久化存储
用户体验要求
- 简洁直观的操作界面
- 快速添加记录
- 清晰的数据展示
- 数据安全保障
1.2 为什么选择Flutter for OpenHarmony
Flutter在开发工具类应用时具有独特优势:
开发效率高
- 热重载功能加速UI调试
- 丰富的Widget组件库
- 声明式UI简化开发
跨平台一致性
- 一套代码多端运行
- 统一的用户体验
- 降低维护成本
性能优异
- 原生性能表现
- 流畅的动画效果
- 高效的渲染机制
1.3 核心功能规划
| 功能模块 | 具体功能 |
|---|---|
| 收支管理 | 添加收入/支出记录、删除记录 |
| 数据统计 | 总余额、总收入、总支出计算 |
| 分类管理 | 支出8个分类、收入5个分类 |
| 数据存储 | 本地持久化存储 |
| 界面展示 | 卡片式布局、列表展示 |
二、技术选型与架构设计
2.1 技术栈选择
数据持久化方案
- shared_preferences:轻量级键值对存储
- 适合小规模数据存储
- 支持基本数据类型
日期格式化
- intl包:国际化日期格式化
- 支持多种日期格式
状态管理
- StatefulWidget:基础状态管理
- setState:简单直接的状态更新
2.2 应用架构设计
采用MVVM风格的分层架构:
表现层 (Presentation Layer)
├── HomePage (主页面)
├── BalanceCard (余额卡片)
├── TransactionList (交易列表)
└── AddTransactionDialog (添加对话框)
业务逻辑层 (Business Logic Layer)
├── _loadData (数据加载)
├── _saveData (数据保存)
├── _calculateTotals (统计计算)
└── _addTransaction (添加交易)
数据层 (Data Layer)
├── Transaction (数据模型)
├── SharedPreferences (存储接口)
└── JSON序列化 (数据转换)
2.3 数据流设计

三、数据模型设计
3.1 Transaction模型类设计
交易记录是应用的核心数据模型:
class Transaction {
final String id; // 唯一标识
final String title; // 标题
final double amount; // 金额
final String category; // 分类
final bool isExpense; // 是否为支出
final DateTime date; // 日期时间
Transaction({
required this.id,
required this.title,
required this.amount,
required this.category,
required this.isExpense,
required this.date,
});
}
3.2 JSON序列化实现
为了实现数据持久化,需要实现JSON序列化:
// 序列化方法
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'amount': amount,
'category': category,
'isExpense': isExpense,
'date': date.millisecondsSinceEpoch,
};
}
// 反序列化工厂方法
factory Transaction.fromJson(Map<String, dynamic> json) {
return Transaction(
id: json['id'] as String,
title: json['title'] as String,
amount: json['amount'] as double,
category: json['category'] as String,
isExpense: json['isExpense'] as bool,
date: DateTime.fromMillisecondsSinceEpoch(json['date'] as int),
);
}
3.3 分类数据设计
预定义分类列表,便于用户选择:
支出分类
final List<String> _expenseCategories = [
'餐饮', '交通', '购物', '娱乐',
'医疗', '教育', '住房', '其他'
];

收入分类
final List<String> _incomeCategories = [
'工资', '奖金', '投资', '兼职', '其他'
];

四、UI界面设计与实现
4.1 整体布局结构
应用采用垂直滚动的单页面布局:
AppBar (顶部导航栏)
↓
BalanceCard (余额卡片)
↓
TransactionList (交易列表)
↓
FloatingActionButton (添加按钮)
4.2 余额卡片设计
余额卡片是应用的视觉焦点,展示关键财务信息:
Widget _buildBalanceCard() {
return Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.primary.withAlpha(179),
],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Theme.of(context).colorScheme.primary.withAlpha(77),
blurRadius: 10,
spreadRadius: 2,
),
],
),
child: Column(
children: [
const Text('总余额'),
Text('¥$_totalBalance'),
Row(
children: [
_buildSummaryItem('收入', '¥$_totalIncome', Colors.green[300]!),
_buildSummaryItem('支出', '¥$_totalExpense', Colors.red[300]!),
],
),
],
),
);
}
设计要点
- 渐变背景增强视觉效果
- 大字号突出显示余额
- 收支对比一目了然
- 圆角和阴影营造层次感
4.3 交易列表设计
使用ListView.builder构建高效的滚动列表:
ListView.builder(
padding: const EdgeInsets.all(16),
itemCount: _transactions.length,
itemBuilder: (context, index) {
// 按日期降序排列
final sortedTransactions = List.from(_transactions)
..sort((a, b) => b.date.compareTo(a.date));
final sortedTransaction = sortedTransactions[index];
return _buildTransactionCard(sortedTransaction);
},
)
4.4 交易卡片组件
每条交易记录以卡片形式展示:
Widget _buildTransactionCard(Transaction transaction) {
return Card(
margin: const EdgeInsets.only(bottom: 12),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: ListTile(
leading: CircleAvatar(
backgroundColor: transaction.isExpense
? Colors.red[100]
: Colors.green[100],
child: Icon(
transaction.isExpense ? Icons.arrow_upward : Icons.arrow_downward,
color: transaction.isExpense ? Colors.red : Colors.green,
),
),
title: Text(transaction.title),
subtitle: Text(
'${transaction.category} · ${DateFormat('yyyy-MM-dd HH:mm').format(transaction.date)}',
),
trailing: Column(
children: [
Text(
'${transaction.isExpense ? '-' : '+'}¥${transaction.amount.toStringAsFixed(2)}',
style: TextStyle(
color: transaction.isExpense ? Colors.red : Colors.green,
),
),
IconButton(
icon: const Icon(Icons.delete_outline),
onPressed: () => _deleteTransaction(transaction.id),
),
],
),
),
);
}
设计特点
- 圆形头像区分收支类型
- 红绿色彩直观展示
- 日期格式化显示
- 快捷删除操作
[此处插入图片:记账应用界面截图]
五、数据持久化实现
5.1 SharedPreferences简介
SharedPreferences是Flutter平台上常用的轻量级存储方案:
特点
- 键值对存储方式
- 支持基本数据类型
- 异步读写操作
- 数据自动持久化
适用场景
- 用户偏好设置
- 小规模数据存储
- 缓存临时数据
5.2 数据加载实现
Future<void> _loadData() async {
final prefs = await SharedPreferences.getInstance();
final transactionsJson = prefs.getStringList('transactions') ?? [];
setState(() {
_transactions = transactionsJson.map((json) {
return Transaction.fromJson(
jsonDecode(json) as Map<String, dynamic>
);
}).toList();
_calculateTotals();
});
}
加载流程
- 获取SharedPreferences实例
- 读取存储的JSON字符串列表
- 反序列化为Transaction对象
- 更新状态并计算统计
5.3 数据保存实现
Future<void> _saveData() async {
final prefs = await SharedPreferences.getInstance();
final transactionsJson = _transactions.map((t) {
return jsonEncode(t.toJson());
}).toList();
await prefs.setStringList('transactions', transactionsJson);
}
保存流程
- 遍历交易列表
- 序列化为JSON字符串
- 保存到本地存储
5.4 统计计算逻辑
void _calculateTotals() {
_totalIncome = 0;
_totalExpense = 0;
for (var transaction in _transactions) {
if (transaction.isExpense) {
_totalExpense += transaction.amount;
} else {
_totalIncome += transaction.amount;
}
}
_totalBalance = _totalIncome - _totalExpense;
}
六、完整代码实现
6.1 添加记录对话框
void _showAddTransactionDialog() {
bool isExpense = true;
final titleController = TextEditingController();
final amountController = TextEditingController();
String selectedCategory = _expenseCategories[0];
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setDialogState) {
return AlertDialog(
title: const Text('添加记录'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 收支类型切换
SegmentedButton<bool>(
segments: const [
ButtonSegment(
value: true,
label: Text('支出'),
icon: Icon(Icons.arrow_upward),
),
ButtonSegment(
value: false,
label: Text('收入'),
icon: Icon(Icons.arrow_downward),
),
],
selected: {isExpense},
onSelectionChanged: (Set<bool> newSelection) {
setDialogState(() {
isExpense = newSelection.first;
selectedCategory = isExpense
? _expenseCategories[0]
: _incomeCategories[0];
});
},
),
const SizedBox(height: 16),
// 标题输入框
TextField(
controller: titleController,
decoration: const InputDecoration(
labelText: '标题',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.edit),
),
),
const SizedBox(height: 16),
// 金额输入框
TextField(
controller: amountController,
decoration: const InputDecoration(
labelText: '金额',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.attach_money),
),
keyboardType: TextInputType.number,
),
const SizedBox(height: 16),
// 分类选择器
DropdownButtonFormField<String>(
value: selectedCategory,
decoration: const InputDecoration(
labelText: '分类',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.category),
),
items: (isExpense ? _expenseCategories : _incomeCategories)
.map((category) {
return DropdownMenuItem(
value: category,
child: Text(category),
);
}).toList(),
onChanged: (value) {
if (value != null) {
setDialogState(() {
selectedCategory = value;
});
}
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
// 表单验证
if (titleController.text.isEmpty ||
amountController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请填写完整信息')),
);
return;
}
final amount = double.tryParse(amountController.text) ?? 0;
if (amount <= 0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请输入有效金额')),
);
return;
}
// 创建交易记录
final transaction = Transaction(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: titleController.text,
amount: amount,
category: selectedCategory,
isExpense: isExpense,
date: DateTime.now(),
);
_addTransaction(transaction);
Navigator.pop(context);
},
child: const Text('保存'),
),
],
);
},
);
},
);
}
6.2 空状态处理
当没有交易记录时显示友好的空状态提示:
child: _transactions.isEmpty
? const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.receipt_long, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text(
'暂无记录',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
SizedBox(height: 8),
Text(
'点击右下角按钮添加第一笔记录',
style: TextStyle(fontSize: 14, color: Colors.grey),
),
],
),
)
: ListView.builder(...)
6.3 清空确认对话框
void _showClearDialog() {
if (_transactions.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('暂无记录可清空')),
);
return;
}
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('确认清空'),
content: const Text('确定要清空所有记录吗?此操作不可恢复。'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
ElevatedButton(
onPressed: () {
setState(() {
_transactions.clear();
_calculateTotals();
});
_saveData();
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已清空所有记录')),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
child: const Text('清空'),
),
],
);
},
);
}

七、运行效果与测试
7.1 项目运行命令
cd E:\HarmonyOS\oh.code\expense_tracker
flutter run -d ohos
7.2 功能测试清单
基础功能测试
- 添加支出记录是否正常
- 添加收入记录是否正常
- 删除记录功能是否正常
- 余额统计是否准确
UI交互测试
- 空状态提示是否显示
- 收支切换是否流畅
- 表单验证是否生效
- 日期格式是否正确
数据持久化测试
- 重启应用后数据是否保留
- 添加记录后是否正确保存
- 删除记录后是否正确更新
7.3 性能考虑
ListView优化
- 使用ListView.builder懒加载
- 避免不必要的widget重建
数据计算优化
- 只在数据变化时计算统计
- 缓存计算结果
八、总结
本文详细介绍了使用Flutter for OpenHarmony开发记账助手应用的完整过程,涵盖了以下核心技术点:
- 数据模型设计:定义Transaction类,实现JSON序列化
- 数据持久化:使用SharedPreferences实现本地存储
- UI设计:卡片式布局、列表展示、对话框交互
- 状态管理:使用setState管理应用状态
- 数据统计:实现收支计算和余额统计
这个项目展示了Flutter在工具类应用开发中的完整流程,代码结构清晰,功能完整。读者可以基于此项目添加更多功能,如:
- 图表可视化展示
- 数据导出功能
- 搜索和筛选功能
- 月度报表统计
- 预算管理功能
通过本文的学习,读者应该能够独立开发类似的工具类应用,掌握Flutter在鸿蒙平台上的数据持久化和状态管理技巧。
欢迎加入开源鸿蒙跨平台社区: 开源鸿蒙跨平台开发者社区
更多推荐

所有评论(0)