Flutter全国公积金查询:智能公积金管理助手
项目简介
全国公积金查询是一款专为公积金缴存职工打造的Flutter便民应用,提供公积金余额查询、缴存记录查看、提取指南浏览等核心功能。通过模拟真实的公积金管理系统,帮助用户便捷地管理个人公积金账户,了解提取政策,享受数字化公积金服务。
运行效果图




核心功能
- 账户余额查询:实时显示公积金账户余额和基本信息
- 缴存记录管理:详细的个人和单位缴存历史记录
- 提取记录追踪:完整的提取申请和审批状态跟踪
- 6类提取指南:购房、租房、还贷、装修、退休、医疗提取指南
- 30个城市支持:覆盖全国主要城市公积金中心
- 智能数据分析:缴存统计、提取统计、余额等级评估
- 账户状态监控:正常、封存、冻结状态实时显示
- 快捷操作入口:提取申请、记录查看、贷款计算、在线客服
- 详细办事指南:提取条件、所需材料、办理流程全覆盖
- 个性化服务:城市切换、数据刷新、收藏功能
技术特点
- Material Design 3设计风格
- NavigationBar底部导航
- 三页面架构(余额、记录、指南)
- 圆形余额展示
- TabBar记录分类
- 计算属性优化
- 模态底部表单
- 渐变色主题设计
- 响应式网格布局
- 无需额外依赖包
核心代码实现
1. 公积金账户数据模型
dart
class HousingFundAccount {
final String id; // 账户ID
final String accountNumber; // 公积金账号
final String name; // 姓名
final String idCard; // 身份证号
final String city; // 所在城市
final String company; // 工作单位
final double balance; // 账户余额
final double monthlyDeposit; // 月缴存额
final double companyDeposit; // 单位缴存额
final double personalDeposit; // 个人缴存额
final DateTime openDate; // 开户时间
final DateTime lastUpdateTime; // 最后更新时间
final String accountStatus; // 账户状态
final List<DepositRecord> depositRecords; // 缴存记录
final List<WithdrawalRecord> withdrawalRecords; // 提取记录
HousingFundAccount({
required this.id,
required this.accountNumber,
required this.name,
required this.idCard,
required this.city,
required this.company,
required this.balance,
required this.monthlyDeposit,
required this.companyDeposit,
required this.personalDeposit,
required this.openDate,
required this.lastUpdateTime,
required this.accountStatus,
required this.depositRecords,
required this.withdrawalRecords,
});
// 计算属性:年缴存总额
double get annualDeposit => monthlyDeposit * 12;
// 计算属性:缴存年限
int get depositYears {
final now = DateTime.now();
return now.year - openDate.year;
}
// 计算属性:账户状态颜色
Color get statusColor {
switch (accountStatus) {
case '正常':
return Colors.green;
case '封存':
return Colors.orange;
case '冻结':
return Colors.red;
default:
return Colors.grey;
}
}
// 计算属性:余额等级
String get balanceLevel {
if (balance >= 100000) return '高额';
if (balance >= 50000) return '中等';
if (balance >= 20000) return '一般';
return '较低';
}
}
模型字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | String | 账户唯一标识符 |
| accountNumber | String | 公积金账号 |
| name | String | 账户持有人姓名 |
| idCard | String | 身份证号码 |
| city | String | 公积金缴存城市 |
| company | String | 缴存单位名称 |
| balance | double | 当前账户余额 |
| monthlyDeposit | double | 月缴存总额 |
| companyDeposit | double | 单位月缴存额 |
| personalDeposit | double | 个人月缴存额 |
| openDate | DateTime | 账户开户时间 |
| lastUpdateTime | DateTime | 数据最后更新时间 |
| accountStatus | String | 账户状态 |
| depositRecords | List | 缴存记录列表 |
| withdrawalRecords | List | 提取记录列表 |
计算属性:
annualDeposit:年缴存总额 = 月缴存额 × 12depositYears:缴存年限 = 当前年份 - 开户年份statusColor:根据账户状态返回对应颜色balanceLevel:根据余额返回等级描述
账户状态分类:
| 状态 | 颜色 | 说明 |
|---|---|---|
| 正常 | 绿色 | 账户正常,可正常缴存和提取 |
| 封存 | 橙色 | 账户封存,暂停缴存,可申请提取 |
| 冻结 | 红色 | 账户冻结,暂停所有业务操作 |
余额等级划分:
| 余额范围 | 等级 | 颜色 | 说明 |
|---|---|---|---|
| ≥10万 | 高额 | 红色 | 余额充足 |
| 5-10万 | 中等 | 橙色 | 余额适中 |
| 2-5万 | 一般 | 蓝色 | 余额一般 |
| <2万 | 较低 | 灰色 | 余额较少 |
2. 缴存记录数据模型
dart
class DepositRecord {
final String id; // 记录ID
final DateTime date; // 缴存日期
final double personalAmount; // 个人缴存金额
final double companyAmount; // 单位缴存金额
final double totalAmount; // 缴存总金额
final String type; // 缴存类型
final String remark; // 备注信息
DepositRecord({
required this.id,
required this.date,
required this.personalAmount,
required this.companyAmount,
required this.totalAmount,
required this.type,
required this.remark,
});
// 计算属性:缴存类型颜色
Color get typeColor {
switch (type) {
case '正常缴存':
return Colors.green;
case '补缴':
return Colors.orange;
case '调整':
return Colors.blue;
default:
return Colors.grey;
}
}
}
缴存记录特点:
- 记录个人和单位缴存明细
- 支持正常缴存、补缴、调整等类型
- 自动计算缴存总额
- 颜色区分不同缴存类型
3. 提取记录数据模型
dart
class WithdrawalRecord {
final String id; // 记录ID
final DateTime date; // 申请日期
final double amount; // 提取金额
final String reason; // 提取原因
final String status; // 审批状态
final String approver; // 审批机构
final String remark; // 备注信息
WithdrawalRecord({
required this.id,
required this.date,
required this.amount,
required this.reason,
required this.status,
required this.approver,
required this.remark,
});
// 计算属性:提取状态颜色
Color get statusColor {
switch (status) {
case '已到账':
return Colors.green;
case '审核中':
return Colors.orange;
case '已拒绝':
return Colors.red;
default:
return Colors.grey;
}
}
}
提取状态分类:
| 状态 | 颜色 | 说明 |
|---|---|---|
| 已到账 | 绿色 | 提取成功,资金已到账 |
| 审核中 | 橙色 | 申请已提交,正在审核 |
| 已拒绝 | 红色 | 申请被拒绝,需重新申请 |
4. 提取指南数据模型
dart
class WithdrawalGuide {
final String id; // 指南ID
final String title; // 指南标题
final String category; // 提取类别
final List<String> conditions; // 提取条件
final List<String> materials; // 所需材料
final List<String> procedures; // 办理流程
final String timeLimit; // 办理时限
final String amount; // 提取额度
final String frequency; // 提取频次
final List<String> notes; // 注意事项
WithdrawalGuide({
required this.id,
required this.title,
required this.category,
required this.conditions,
required this.materials,
required this.procedures,
required this.timeLimit,
required this.amount,
required this.frequency,
required this.notes,
});
// 计算属性:类别颜色
Color get categoryColor {
switch (category) {
case '购房':
return Colors.red;
case '租房':
return Colors.blue;
case '还贷':
return Colors.green;
case '装修':
return Colors.orange;
case '退休':
return Colors.purple;
case '医疗':
return Colors.pink;
default:
return Colors.grey;
}
}
// 计算属性:类别图标
IconData get categoryIcon {
switch (category) {
case '购房':
return Icons.home;
case '租房':
return Icons.apartment;
case '还贷':
return Icons.payment;
case '装修':
return Icons.build;
case '退休':
return Icons.elderly;
case '医疗':
return Icons.local_hospital;
default:
return Icons.help;
}
}
}
提取类别分类:
| 类别 | 颜色 | 图标 | 说明 |
|---|---|---|---|
| 购房 | 红色 | home | 购买自住住房提取 |
| 租房 | 蓝色 | apartment | 支付房租提取 |
| 还贷 | 绿色 | payment | 偿还住房贷款提取 |
| 装修 | 橙色 | build | 住房装修提取 |
| 退休 | 紫色 | elderly | 退休提取 |
| 医疗 | 粉色 | local_hospital | 重大疾病医疗提取 |
5. 数据生成和初始化
dart
void _generateAccountData() {
final random = Random();
// 生成缴存记录
final depositRecords = <DepositRecord>[];
for (int i = 0; i < 24; i++) {
final date = DateTime.now().subtract(Duration(days: 30 * i));
final personalAmount = 800 + random.nextDouble() * 400;
final companyAmount = personalAmount;
depositRecords.add(DepositRecord(
id: 'deposit_$i',
date: date,
personalAmount: personalAmount,
companyAmount: companyAmount,
totalAmount: personalAmount + companyAmount,
type: i == 0 ? '正常缴存' : (random.nextBool() ? '正常缴存' : '补缴'),
remark: i == 0 ? '当月缴存' : '${date.year}年${date.month}月缴存',
));
}
// 生成提取记录
final withdrawalRecords = <WithdrawalRecord>[];
final withdrawalReasons = ['购房提取', '租房提取', '还贷提取', '装修提取', '医疗提取'];
final withdrawalStatuses = ['已到账', '审核中', '已拒绝'];
for (int i = 0; i < 5; i++) {
final date = DateTime.now().subtract(Duration(days: 60 * i + 30));
withdrawalRecords.add(WithdrawalRecord(
id: 'withdrawal_$i',
date: date,
amount: 10000 + random.nextDouble() * 40000,
reason: withdrawalReasons[random.nextInt(withdrawalReasons.length)],
status: withdrawalStatuses[random.nextInt(withdrawalStatuses.length)],
approver: '公积金管理中心',
remark: '提取申请已处理',
));
}
_currentAccount = HousingFundAccount(
id: 'account_001',
accountNumber: '${_selectedCity.substring(0, 2)}${DateTime.now().year}${random.nextInt(999999).toString().padLeft(6, '0')}',
name: '张三',
idCard: '110101199001011234',
city: _selectedCity,
company: '北京科技有限公司',
balance: 45000 + random.nextDouble() * 55000,
monthlyDeposit: 1600 + random.nextDouble() * 800,
companyDeposit: 800 + random.nextDouble() * 400,
personalDeposit: 800 + random.nextDouble() * 400,
openDate: DateTime.now().subtract(Duration(days: 365 * 3 + random.nextInt(365 * 5))),
lastUpdateTime: DateTime.now().subtract(Duration(days: random.nextInt(30))),
accountStatus: random.nextDouble() > 0.1 ? '正常' : (random.nextBool() ? '封存' : '冻结'),
depositRecords: depositRecords,
withdrawalRecords: withdrawalRecords,
);
}
数据生成特点:
- 24个月缴存记录:模拟2年的缴存历史
- 5条提取记录:不同类型的提取申请
- 随机金额生成:符合实际缴存标准
- 状态随机分配:90%正常状态,10%异常状态
- 时间序列生成:按月份倒序生成记录
- 账号自动生成:城市代码+年份+随机数字
6. NavigationBar底部导航
dart
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() => _selectedIndex = index);
},
destinations: const [
NavigationDestination(icon: Icon(Icons.account_balance_wallet), label: '账户余额'),
NavigationDestination(icon: Icon(Icons.history), label: '缴存记录'),
NavigationDestination(icon: Icon(Icons.help_outline), label: '提取指南'),
],
),
三个页面功能:
| 页面 | 图标 | 功能 |
|---|---|---|
| 账户余额 | account_balance_wallet | 显示余额、缴存信息、账户状态 |
| 缴存记录 | history | 显示缴存和提取记录 |
| 提取指南 | help_outline | 显示各类提取指南 |
7. 圆形余额展示
dart
Widget _buildBalanceCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
children: [
const Icon(Icons.account_balance_wallet, color: Colors.green),
const SizedBox(width: 8),
const Text(
'账户余额',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: _currentAccount!.balanceLevelColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
_currentAccount!.balanceLevel,
style: TextStyle(
fontSize: 10,
color: _currentAccount!.balanceLevelColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 20),
Container(
width: 150,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.green.withValues(alpha: 0.1),
border: Border.all(color: Colors.green, width: 4),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'¥${_currentAccount!.balance.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
const Text(
'当前余额',
style: TextStyle(
fontSize: 12,
color: Colors.green,
),
),
],
),
),
const SizedBox(height: 16),
Text(
'更新时间:${_currentAccount!.lastUpdateTime.month}月${_currentAccount!.lastUpdateTime.day}日 ${_currentAccount!.lastUpdateTime.hour.toString().padLeft(2, '0')}:${_currentAccount!.lastUpdateTime.minute.toString().padLeft(2, '0')}',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
),
);
}
圆形余额展示特点:
- 150x150像素圆形容器:突出显示余额
- 绿色主题:象征财富和安全
- 余额等级标签:右上角显示余额等级
- 更新时间显示:底部显示最后更新时间
- 透明背景:圆形背景透明度0.1
- 边框设计:4像素绿色边框
8. 缴存信息卡片
dart
Widget _buildDepositInfoCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.savings, color: Colors.blue),
SizedBox(width: 8),
Text(
'缴存信息',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'¥${_currentAccount!.monthlyDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
const Text('月缴存额', style: TextStyle(fontSize: 12)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'¥${_currentAccount!.annualDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
),
const Text('年缴存额', style: TextStyle(fontSize: 12)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.purple.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'${_currentAccount!.depositYears}年',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
const Text('缴存年限', style: TextStyle(fontSize: 12)),
],
),
),
),
],
),
const SizedBox(height: 12),
const Divider(),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'个人缴存',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${_currentAccount!.personalDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'单位缴存',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${_currentAccount!.companyDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'开户时间',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'${_currentAccount!.openDate.year}年${_currentAccount!.openDate.month}月',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
],
),
),
);
}
缴存信息展示特点:
- 三项核心指标:月缴存额、年缴存额、缴存年限
- 颜色区分:蓝色、橙色、紫色区分不同指标
- 分割线设计:Divider分隔上下两部分
- 详细分解:个人缴存、单位缴存、开户时间
- 响应式布局:Expanded自动分配空间
9. 快捷操作卡片
dart
Widget _buildQuickActionsCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.flash_on, color: Colors.purple),
SizedBox(width: 8),
Text(
'快捷操作',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: _buildQuickActionButton(
icon: Icons.download,
label: '提取申请',
color: Colors.green,
onTap: () {
setState(() => _selectedIndex = 2);
},
),
),
const SizedBox(width: 8),
Expanded(
child: _buildQuickActionButton(
icon: Icons.history,
label: '查看记录',
color: Colors.blue,
onTap: () {
setState(() => _selectedIndex = 1);
},
),
),
const SizedBox(width: 8),
Expanded(
child: _buildQuickActionButton(
icon: Icons.calculate,
label: '贷款计算',
color: Colors.orange,
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('贷款计算功能开发中...')),
);
},
),
),
const SizedBox(width: 8),
Expanded(
child: _buildQuickActionButton(
icon: Icons.support_agent,
label: '在线客服',
color: Colors.purple,
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('客服热线:12329')),
);
},
),
),
],
),
],
),
),
);
}
Widget _buildQuickActionButton({
required IconData icon,
required String label,
required Color color,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Icon(icon, color: color, size: 24),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
color: color,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
快捷操作特点:
- 四个核心功能:提取申请、查看记录、贷款计算、在线客服
- 颜色主题:绿色、蓝色、橙色、紫色区分功能
- 图标标识:直观的功能图标
- 点击响应:InkWell提供点击反馈
- 页面跳转:部分功能跳转到对应页面
- SnackBar提示:开发中功能显示提示信息
10. TabBar记录分类
dart
Widget _buildRecordsPage() {
if (_currentAccount == null) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 80, color: Colors.grey),
SizedBox(height: 16),
Text('暂无记录数据'),
],
),
);
}
return DefaultTabController(
length: 2,
child: Column(
children: [
Container(
color: Colors.grey.withValues(alpha: 0.1),
child: const TabBar(
tabs: [
Tab(text: '缴存记录'),
Tab(text: '提取记录'),
],
),
),
Expanded(
child: TabBarView(
children: [
_buildDepositRecords(),
_buildWithdrawalRecords(),
],
),
),
],
),
);
}
TabBar设计特点:
- 两个标签页:缴存记录和提取记录
- 灰色背景:Container提供背景色
- DefaultTabController:自动管理标签状态
- TabBarView:对应的页面内容
- Expanded布局:充满剩余空间
11. 缴存记录统计
dart
Widget _buildDepositRecords() {
return ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.trending_up, color: Colors.green),
SizedBox(width: 8),
Text(
'缴存统计',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'${_currentAccount!.depositRecords.length}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
const Text('缴存月数', style: TextStyle(fontSize: 10)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'¥${_currentAccount!.depositRecords.map((r) => r.totalAmount).reduce((a, b) => a + b).toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
const Text('累计缴存', style: TextStyle(fontSize: 10)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'¥${(_currentAccount!.depositRecords.map((r) => r.totalAmount).reduce((a, b) => a + b) / _currentAccount!.depositRecords.length).toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
),
const Text('月均缴存', style: TextStyle(fontSize: 10)),
],
),
),
),
],
),
],
),
),
),
const SizedBox(height: 16),
..._currentAccount!.depositRecords.map((record) => _buildDepositRecordCard(record)),
],
);
}
缴存统计特点:
- 三项统计指标:缴存月数、累计缴存、月均缴存
- 动态计算:使用map和reduce计算统计数据
- 颜色区分:蓝色、绿色、橙色区分指标
- 实时更新:数据随记录变化自动更新
12. 城市选择器
dart
void _changeCity() {
showModalBottomSheet(
context: context,
builder: (context) => Container(
height: 400,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'选择城市',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Expanded(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 2.5,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _cities.length,
itemBuilder: (context, index) {
final city = _cities[index];
final isSelected = city == _selectedCity;
return InkWell(
onTap: () {
setState(() {
_selectedCity = city;
_generateAccountData();
});
Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
color: isSelected
? Colors.blue.withValues(alpha: 0.1)
: Colors.grey.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: isSelected
? Border.all(color: Colors.blue, width: 2)
: Border.all(color: Colors.grey.withValues(alpha: 0.3)),
),
child: Center(
child: Text(
city,
style: TextStyle(
fontSize: 12,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
color: isSelected ? Colors.blue : Colors.black,
),
),
),
),
);
},
),
),
],
),
),
);
}
城市选择器特点:
- 模态底部表单:showModalBottomSheet展示
- 网格布局:3列网格,宽高比2.5:1
- 30个城市:覆盖全国主要城市
- 选中状态:蓝色边框和背景突出显示
- 点击切换:选择城市后自动生成新数据
- 自动关闭:选择后自动关闭弹窗
支持城市列表:
- 直辖市:北京、上海、天津、重庆
- 省会城市:广州、杭州、南京、武汉、成都、西安等
- 重要城市:深圳、青岛、大连、厦门、宁波等
技术要点详解
1. 计算属性模式
计算属性是Flutter开发中的重要模式,通过getter方法实现数据的动态计算:
dart
class HousingFundAccount {
// 基础数据
final double balance;
final String accountStatus;
final DateTime openDate;
final double monthlyDeposit;
// 计算属性:年缴存总额
double get annualDeposit => monthlyDeposit * 12;
// 计算属性:缴存年限
int get depositYears {
final now = DateTime.now();
return now.year - openDate.year;
}
// 计算属性:账户状态颜色
Color get statusColor {
switch (accountStatus) {
case '正常': return Colors.green;
case '封存': return Colors.orange;
case '冻结': return Colors.red;
default: return Colors.grey;
}
}
// 计算属性:余额等级
String get balanceLevel {
if (balance >= 100000) return '高额';
if (balance >= 50000) return '中等';
if (balance >= 20000) return '一般';
return '较低';
}
}
计算属性优势:
- 实时计算:数据变化时自动更新
- 避免冗余:不需要存储计算结果
- 保持一致性:确保派生数据的准确性
- 提高可读性:语义化的属性名称
- 便于维护:集中管理计算逻辑
应用场景:
- 数据格式化(日期、金额、百分比)
- 状态映射(颜色、图标、文本)
- 业务逻辑计算(等级、评分、统计)
- UI样式控制(主题、布局、动画)
2. TabBar使用技巧
TabBar是Flutter中常用的标签页组件,需要配合TabController使用:
dart
class _HousingFundHomePageState extends State<HousingFundHomePage> {
Widget _buildRecordsPage() {
return DefaultTabController(
length: 2,
child: Column(
children: [
Container(
color: Colors.grey.withValues(alpha: 0.1),
child: const TabBar(
tabs: [
Tab(text: '缴存记录'),
Tab(text: '提取记录'),
],
),
),
Expanded(
child: TabBarView(
children: [
_buildDepositRecords(),
_buildWithdrawalRecords(),
],
),
),
],
),
);
}
}
TabBar最佳实践:
- 使用DefaultTabController:
dart
DefaultTabController(
length: 2, // 标签页数量
child: Column(
children: [
TabBar(tabs: [...]),
Expanded(child: TabBarView(children: [...])),
],
),
)
- 自定义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();
}
}
- TabBar样式定制:
dart
TabBar(
indicatorColor: Colors.blue,
indicatorWeight: 3.0,
labelColor: Colors.blue,
unselectedLabelColor: Colors.grey,
labelStyle: TextStyle(fontWeight: FontWeight.bold),
tabs: [...],
)
3. 模态底部表单设计
模态底部表单是移动应用中常见的交互模式:
dart
void _changeCity() {
showModalBottomSheet(
context: context,
isScrollControlled: true, // 允许全屏高度
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) => Container(
height: 400,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 拖拽指示器
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(2),
),
),
),
const SizedBox(height: 16),
const Text(
'选择城市',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Expanded(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 2.5,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _cities.length,
itemBuilder: (context, index) {
// 城市选择项
},
),
),
],
),
),
);
}
设计要点:
- 拖拽指示器:顶部小横条提示可拖拽
- 圆角设计:顶部圆角增加视觉层次
- 合适高度:不超过屏幕的80%
- 滚动支持:内容过多时支持滚动
- 点击关闭:选择后自动关闭
4. 状态管理策略
合理的状态管理是Flutter应用的核心:
dart
class _HousingFundHomePageState extends State<HousingFundHomePage> {
// 页面状态
int _selectedIndex = 0;
bool _isLoading = false;
String _selectedCity = '北京市';
// 数据状态
HousingFundAccount? _currentAccount;
List<WithdrawalGuide> _withdrawalGuides = [];
// 常量数据
final List<String> _cities = [...];
@override
void initState() {
super.initState();
_generateAccountData();
_generateWithdrawalGuides();
}
// 状态更新方法
void _refreshAccount() {
setState(() {
_isLoading = true;
});
Future.delayed(const Duration(seconds: 2), () {
setState(() {
_generateAccountData();
_isLoading = false;
});
});
}
}
状态分类:
- UI状态:页面索引、加载状态、选择状态
- 数据状态:业务数据、列表数据、缓存数据
- 配置状态:用户设置、主题配置、语言设置
状态管理原则:
- 最小化状态:只存储必要的状态
- 单一数据源:避免状态重复
- 及时清理:防止内存泄漏
- 合理粒度:避免过度重建
5. 数据生成算法
模拟真实数据的生成算法:
dart
void _generateAccountData() {
final random = Random();
// 生成24个月缴存记录
final depositRecords = <DepositRecord>[];
for (int i = 0; i < 24; i++) {
final date = DateTime.now().subtract(Duration(days: 30 * i));
final personalAmount = 800 + random.nextDouble() * 400; // 800-1200
final companyAmount = personalAmount; // 1:1配比
depositRecords.add(DepositRecord(
id: 'deposit_$i',
date: date,
personalAmount: personalAmount,
companyAmount: companyAmount,
totalAmount: personalAmount + companyAmount,
type: i == 0 ? '正常缴存' : (random.nextBool() ? '正常缴存' : '补缴'),
remark: i == 0 ? '当月缴存' : '${date.year}年${date.month}月缴存',
));
}
// 生成账户余额(基于缴存记录)
final totalDeposit = depositRecords.map((r) => r.totalAmount).reduce((a, b) => a + b);
final withdrawalAmount = random.nextDouble() * totalDeposit * 0.3; // 最多提取30%
final balance = totalDeposit - withdrawalAmount;
_currentAccount = HousingFundAccount(
// ... 其他字段
balance: balance,
monthlyDeposit: depositRecords.first.totalAmount,
depositRecords: depositRecords,
// ...
);
}
算法特点:
- 时间序列:按月份倒序生成
- 合理范围:符合实际缴存标准
- 关联计算:余额基于缴存记录计算
- 随机变化:增加数据真实感
- 业务逻辑:遵循公积金业务规则
6. 城市切换机制
城市切换涉及数据重新生成和UI更新:
dart
void _changeCity() {
showModalBottomSheet(
context: context,
builder: (context) => Container(
child: GridView.builder(
itemBuilder: (context, index) {
final city = _cities[index];
final isSelected = city == _selectedCity;
return InkWell(
onTap: () {
setState(() {
_selectedCity = city;
_generateAccountData(); // 重新生成数据
});
Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
color: isSelected
? Colors.blue.withValues(alpha: 0.1)
: Colors.grey.withValues(alpha: 0.1),
border: isSelected
? Border.all(color: Colors.blue, width: 2)
: Border.all(color: Colors.grey.withValues(alpha: 0.3)),
),
child: Center(child: Text(city)),
),
);
},
),
),
);
}
切换流程:
- 显示城市选择器
- 用户选择新城市
- 更新选中城市状态
- 重新生成账户数据
- 关闭选择器
- UI自动更新
7. 响应式布局设计
使用Expanded和Flexible实现响应式布局:
dart
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text('¥${_currentAccount!.monthlyDeposit.toStringAsFixed(0)}'),
const Text('月缴存额'),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
// 第二个指标
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
// 第三个指标
),
),
],
)
响应式原则:
- Ex
panded自动分配:等比例分配可用空间 - 固定间距:SizedBox提供固定间距
- 灵活布局:适应不同屏幕尺寸
- 内容优先:根据内容调整布局
完整代码实现
main.dart
dart
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(const HousingFundApp());
}
class HousingFundApp extends StatelessWidget {
const HousingFundApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter全国公积金查询',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
useMaterial3: true,
),
home: const HousingFundHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class HousingFundHomePage extends StatefulWidget {
const HousingFundHomePage({super.key});
@override
State<HousingFundHomePage> createState() => _HousingFundHomePageState();
}
class _HousingFundHomePageState extends State<HousingFundHomePage> {
int _selectedIndex = 0;
bool _isLoading = false;
String _selectedCity = '北京市';
HousingFundAccount? _currentAccount;
List<WithdrawalGuide> _withdrawalGuides = [];
final List<String> _cities = [
'北京市', '上海市', '广州市', '深圳市', '天津市', '重庆市',
'杭州市', '南京市', '武汉市', '成都市', '西安市', '郑州市',
'沈阳市', '长沙市', '哈尔滨市', '昆明市', '南昌市', '福州市',
'石家庄市', '太原市', '合肥市', '南宁市', '贵阳市', '海口市',
'兰州市', '西宁市', '银川市', '乌鲁木齐市', '拉萨市', '呼和浩特市'
];
@override
void initState() {
super.initState();
_generateAccountData();
_generateWithdrawalGuides();
}
void _generateAccountData() {
final random = Random();
// 生成缴存记录
final depositRecords = <DepositRecord>[];
for (int i = 0; i < 24; i++) {
final date = DateTime.now().subtract(Duration(days: 30 * i));
final personalAmount = 800 + random.nextDouble() * 400;
final companyAmount = personalAmount;
depositRecords.add(DepositRecord(
id: 'deposit_$i',
date: date,
personalAmount: personalAmount,
companyAmount: companyAmount,
totalAmount: personalAmount + companyAmount,
type: i == 0 ? '正常缴存' : (random.nextBool() ? '正常缴存' : '补缴'),
remark: i == 0 ? '当月缴存' : '${date.year}年${date.month}月缴存',
));
}
// 生成提取记录
final withdrawalRecords = <WithdrawalRecord>[];
final withdrawalReasons = ['购房提取', '租房提取', '还贷提取', '装修提取', '医疗提取'];
final withdrawalStatuses = ['已到账', '审核中', '已拒绝'];
for (int i = 0; i < 5; i++) {
final date = DateTime.now().subtract(Duration(days: 60 * i + 30));
withdrawalRecords.add(WithdrawalRecord(
id: 'withdrawal_$i',
date: date,
amount: 10000 + random.nextDouble() * 40000,
reason: withdrawalReasons[random.nextInt(withdrawalReasons.length)],
status: withdrawalStatuses[random.nextInt(withdrawalStatuses.length)],
approver: '公积金管理中心',
remark: '提取申请已处理',
));
}
_currentAccount = HousingFundAccount(
id: 'account_001',
accountNumber: '${_selectedCity.substring(0, 2)}${DateTime.now().year}${random.nextInt(999999).toString().padLeft(6, '0')}',
name: '张三',
idCard: '110101199001011234',
city: _selectedCity,
company: '北京科技有限公司',
balance: 45000 + random.nextDouble() * 55000,
monthlyDeposit: 1600 + random.nextDouble() * 800,
companyDeposit: 800 + random.nextDouble() * 400,
personalDeposit: 800 + random.nextDouble() * 400,
openDate: DateTime.now().subtract(Duration(days: 365 * 3 + random.nextInt(365 * 5))),
lastUpdateTime: DateTime.now().subtract(Duration(days: random.nextInt(30))),
accountStatus: random.nextDouble() > 0.1 ? '正常' : (random.nextBool() ? '封存' : '冻结'),
depositRecords: depositRecords,
withdrawalRecords: withdrawalRecords,
);
}
void _generateWithdrawalGuides() {
_withdrawalGuides = [
WithdrawalGuide(
id: 'guide_1',
title: '购买自住住房提取',
category: '购房',
conditions: [
'购买本市范围内自住住房',
'房屋产权证或购房合同在有效期内',
'申请人为房屋所有权人或共有人',
'未使用住房公积金贷款购买该套住房'
],
materials: [
'身份证原件及复印件',
'住房公积金联名卡',
'房屋所有权证或不动产权证书',
'购房合同及购房发票',
'结婚证(已婚提供)'
],
procedures: [
'准备相关材料',
'填写提取申请表',
'到公积金中心办理',
'等待审核结果',
'资金到账'
],
timeLimit: '15个工作日',
amount: '不超过购房总价,且不超过账户余额',
frequency: '一套房屋只能提取一次',
notes: [
'提取金额不得超过实际购房支出',
'夫妻双方合计提取金额不得超过购房总价',
'提取申请应在购房后2年内办理'
],
),
WithdrawalGuide(
id: 'guide_2',
title: '支付房租提取',
category: '租房',
conditions: [
'在本市无自有住房',
'连续足额缴存住房公积金满3个月',
'租赁住房位于缴存城市',
'租房合同在有效期内'
],
materials: [
'身份证原件及复印件',
'住房公积金联名卡',
'房屋租赁合同',
'房租发票或收据',
'无房证明'
],
procedures: [
'开具无房证明',
'准备租房材料',
'填写提取申请',
'提交审核材料',
'等待资金到账'
],
timeLimit: '10个工作日',
amount: '每月不超过当月应缴存额的50%',
frequency: '每季度可提取一次',
notes: [
'提取金额不得超过实际房租支出',
'需提供真实有效的租房合同',
'连续提取不得超过24个月'
],
),
WithdrawalGuide(
id: 'guide_3',
title: '偿还住房贷款提取',
category: '还贷',
conditions: [
'申请人为借款人或共同借款人',
'住房贷款正常还款中',
'无逾期还款记录',
'贷款银行在本市范围内'
],
materials: [
'身份证原件及复印件',
'住房公积金联名卡',
'借款合同',
'还款明细或银行对账单',
'房屋所有权证'
],
procedures: [
'向贷款银行申请还款证明',
'准备相关证明材料',
'填写提取申请表',
'提交公积金中心审核',
'审核通过后资金到账'
],
timeLimit: '7个工作日',
amount: '不超过当期还款本息,且不超过账户余额',
frequency: '每年可提取一次',
notes: [
'提取金额不得超过实际还款金额',
'可委托银行按月自动提取',
'贷款结清后应及时停止提取'
],
),
WithdrawalGuide(
id: 'guide_4',
title: '住房装修提取',
category: '装修',
conditions: [
'装修自住住房',
'房屋所有权属于申请人',
'装修工程已开工',
'装修费用发票真实有效'
],
materials: [
'身份证原件及复印件',
'住房公积金联名卡',
'房屋所有权证',
'装修合同',
'装修费用发票'
],
procedures: [
'签订装修合同',
'保留装修发票',
'填写提取申请',
'提交审核材料',
'等待审核结果'
],
timeLimit: '15个工作日',
amount: '不超过装修费用,且不超过账户余额的50%',
frequency: '同一套房屋5年内只能提取一次',
notes: [
'装修费用需达到房屋总价的10%以上',
'需提供正规装修公司发票',
'简单维修不在提取范围内'
],
),
WithdrawalGuide(
id: 'guide_5',
title: '退休提取',
category: '退休',
conditions: [
'已达到法定退休年龄',
'已办理退休手续',
'住房公积金账户已封存',
'无未结清的公积金贷款'
],
materials: [
'身份证原件及复印件',
'住房公积金联名卡',
'退休证或退休审批表',
'户口本(异地退休需提供)'
],
procedures: [
'办理退休手续',
'账户封存处理',
'填写提取申请',
'提交相关材料',
'全额提取到账'
],
timeLimit: '5个工作日',
amount: '账户全部余额',
frequency: '一次性全额提取',
notes: [
'退休提取可全额提取账户余额',
'提取后账户自动销户',
'异地退休需提供户口迁移证明'
],
),
WithdrawalGuide(
id: 'guide_6',
title: '重大疾病医疗提取',
category: '医疗',
conditions: [
'本人或家庭成员患重大疾病',
'医疗费用较大造成家庭困难',
'疾病在规定的重大疾病范围内',
'医疗费用发票真实有效'
],
materials: [
'身份证原件及复印件',
'住房公积金联名卡',
'医院诊断证明',
'医疗费用发票',
'家庭关系证明(非本人患病)'
],
procedures: [
'医院开具诊断证明',
'收集医疗费用发票',
'填写提取申请表',
'提交审核材料',
'等待审核通过'
],
timeLimit: '10个工作日',
amount: '不超过个人承担的医疗费用',
frequency: '每年可提取一次',
notes: [
'仅限规定的重大疾病范围',
'需提供三甲医院诊断证明',
'医疗费用需扣除医保报销部分'
],
),
];
}
void _refreshAccount() {
setState(() {
_isLoading = true;
});
Future.delayed(const Duration(seconds: 2), () {
setState(() {
_generateAccountData();
_isLoading = false;
});
});
}
void _changeCity() {
showModalBottomSheet(
context: context,
builder: (context) => Container(
height: 400,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'选择城市',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Expanded(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 2.5,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _cities.length,
itemBuilder: (context, index) {
final city = _cities[index];
final isSelected = city == _selectedCity;
return InkWell(
onTap: () {
setState(() {
_selectedCity = city;
_generateAccountData();
});
Navigator.pop(context);
},
child: Container(
decoration: BoxDecoration(
color: isSelected
? Colors.blue.withValues(alpha: 0.1)
: Colors.grey.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: isSelected
? Border.all(color: Colors.blue, width: 2)
: Border.all(color: Colors.grey.withValues(alpha: 0.3)),
),
child: Center(
child: Text(
city,
style: TextStyle(
fontSize: 12,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
color: isSelected ? Colors.blue : Colors.black,
),
),
),
),
);
},
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('全国公积金查询'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
actions: [
TextButton.icon(
onPressed: _changeCity,
icon: const Icon(Icons.location_on, size: 16),
label: Text(_selectedCity),
),
IconButton(
onPressed: _refreshAccount,
icon: _isLoading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.refresh),
),
],
),
body: IndexedStack(
index: _selectedIndex,
children: [
_buildBalancePage(),
_buildRecordsPage(),
_buildGuidePage(),
],
),
bottomNavigationBar: NavigationBar(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() => _selectedIndex = index);
},
destinations: const [
NavigationDestination(icon: Icon(Icons.account_balance_wallet), label: '账户余额'),
NavigationDestination(icon: Icon(Icons.history), label: '缴存记录'),
NavigationDestination(icon: Icon(Icons.help_outline), label: '提取指南'),
],
),
);
}
Widget _buildBalancePage() {
if (_currentAccount == null) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 80, color: Colors.grey),
SizedBox(height: 16),
Text('暂无账户数据'),
],
),
);
}
return ListView(
padding: const EdgeInsets.all(16),
children: [
_buildBalanceCard(),
const SizedBox(height: 16),
_buildDepositInfoCard(),
const SizedBox(height: 16),
_buildAccountInfoCard(),
const SizedBox(height: 16),
_buildQuickActionsCard(),
],
);
}
Widget _buildBalanceCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
children: [
const Icon(Icons.account_balance_wallet, color: Colors.green),
const SizedBox(width: 8),
const Text(
'账户余额',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: _currentAccount!.balanceLevelColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
_currentAccount!.balanceLevel,
style: TextStyle(
fontSize: 10,
color: _currentAccount!.balanceLevelColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 20),
Container(
width: 150,
height: 150,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.green.withValues(alpha: 0.1),
border: Border.all(color: Colors.green, width: 4),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'¥${_currentAccount!.balance.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
const Text(
'当前余额',
style: TextStyle(
fontSize: 12,
color: Colors.green,
),
),
],
),
),
const SizedBox(height: 16),
Text(
'更新时间:${_currentAccount!.lastUpdateTime.month}月${_currentAccount!.lastUpdateTime.day}日 ${_currentAccount!.lastUpdateTime.hour.toString().padLeft(2, '0')}:${_currentAccount!.lastUpdateTime.minute.toString().padLeft(2, '0')}',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
),
);
}
Widget _buildDepositInfoCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.savings, color: Colors.blue),
SizedBox(width: 8),
Text(
'缴存信息',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'¥${_currentAccount!.monthlyDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
const Text('月缴存额', style: TextStyle(fontSize: 12)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'¥${_currentAccount!.annualDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
),
const Text('年缴存额', style: TextStyle(fontSize: 12)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.purple.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Text(
'${_currentAccount!.depositYears}年',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.purple,
),
),
const Text('缴存年限', style: TextStyle(fontSize: 12)),
],
),
),
),
],
),
const SizedBox(height: 12),
const Divider(),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'个人缴存',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${_currentAccount!.personalDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'单位缴存',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${_currentAccount!.companyDeposit.toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'开户时间',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'${_currentAccount!.openDate.year}年${_currentAccount!.openDate.month}月',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
],
),
),
);
}
Widget _buildAccountInfoCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.person, color: Colors.orange),
SizedBox(width: 8),
Text(
'账户信息',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
_buildInfoRow('姓名', _currentAccount!.name),
_buildInfoRow('公积金账号', _currentAccount!.accountNumber),
_buildInfoRow('身份证号', _currentAccount!.idCard),
_buildInfoRow('缴存城市', _currentAccount!.city),
_buildInfoRow('工作单位', _currentAccount!.company),
Row(
children: [
Text(
'账户状态',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: _currentAccount!.statusColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
_currentAccount!.accountStatus,
style: TextStyle(
fontSize: 12,
color: _currentAccount!.statusColor,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
);
}
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Text(
label,
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const Spacer(),
Text(
value,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
],
),
);
}
Widget _buildQuickActionsCard() {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.flash_on, color: Colors.purple),
SizedBox(width: 8),
Text(
'快捷操作',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: _buildQuickActionButton(
icon: Icons.download,
label: '提取申请',
color: Colors.green,
onTap: () {
setState(() => _selectedIndex = 2);
},
),
),
const SizedBox(width: 8),
Expanded(
child: _buildQuickActionButton(
icon: Icons.history,
label: '查看记录',
color: Colors.blue,
onTap: () {
setState(() => _selectedIndex = 1);
},
),
),
const SizedBox(width: 8),
Expanded(
child: _buildQuickActionButton(
icon: Icons.calculate,
label: '贷款计算',
color: Colors.orange,
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('贷款计算功能开发中...')),
);
},
),
),
const SizedBox(width: 8),
Expanded(
child: _buildQuickActionButton(
icon: Icons.support_agent,
label: '在线客服',
color: Colors.purple,
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('客服热线:12329')),
);
},
),
),
],
),
],
),
),
);
}
Widget _buildQuickActionButton({
required IconData icon,
required String label,
required Color color,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Icon(icon, color: color, size: 24),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 10,
color: color,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
],
),
),
);
}
Widget _buildRecordsPage() {
if (_currentAccount == null) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 80, color: Colors.grey),
SizedBox(height: 16),
Text('暂无记录数据'),
],
),
);
}
return DefaultTabController(
length: 2,
child: Column(
children: [
Container(
color: Colors.grey.withValues(alpha: 0.1),
child: const TabBar(
tabs: [
Tab(text: '缴存记录'),
Tab(text: '提取记录'),
],
),
),
Expanded(
child: TabBarView(
children: [
_buildDepositRecords(),
_buildWithdrawalRecords(),
],
),
),
],
),
);
}
Widget _buildDepositRecords() {
return ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.trending_up, color: Colors.green),
SizedBox(width: 8),
Text(
'缴存统计',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'${_currentAccount!.depositRecords.length}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
const Text('缴存月数', style: TextStyle(fontSize: 10)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'¥${_currentAccount!.depositRecords.map((r) => r.totalAmount).reduce((a, b) => a + b).toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
const Text('累计缴存', style: TextStyle(fontSize: 10)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'¥${(_currentAccount!.depositRecords.map((r) => r.totalAmount).reduce((a, b) => a + b) / _currentAccount!.depositRecords.length).toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
),
const Text('月均缴存', style: TextStyle(fontSize: 10)),
],
),
),
),
],
),
],
),
),
),
const SizedBox(height: 16),
..._currentAccount!.depositRecords.map((record) => _buildDepositRecordCard(record)),
],
);
}
Widget _buildDepositRecordCard(DepositRecord record) {
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: record.typeColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
record.type,
style: TextStyle(
fontSize: 10,
color: record.typeColor,
fontWeight: FontWeight.bold,
),
),
),
const Spacer(),
Text(
'${record.date.year}年${record.date.month}月${record.date.day}日',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'个人缴存',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${record.personalAmount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'单位缴存',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${record.companyAmount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
],
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'缴存总额',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${record.totalAmount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
),
],
),
),
],
),
if (record.remark.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
record.remark,
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
],
),
),
);
}
Widget _buildWithdrawalRecords() {
return ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.trending_down, color: Colors.red),
SizedBox(width: 8),
Text(
'提取统计',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.blue.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'${_currentAccount!.withdrawalRecords.length}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
const Text('提取次数', style: TextStyle(fontSize: 10)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.red.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'¥${_currentAccount!.withdrawalRecords.where((r) => r.status == '已到账').map((r) => r.amount).fold(0.0, (a, b) => a + b).toStringAsFixed(0)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
const Text('累计提取', style: TextStyle(fontSize: 10)),
],
),
),
),
const SizedBox(width: 8),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.orange.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(6),
),
child: Column(
children: [
Text(
'${_currentAccount!.withdrawalRecords.where((r) => r.status == '审核中').length}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
),
const Text('审核中', style: TextStyle(fontSize: 10)),
],
),
),
),
],
),
],
),
),
),
const SizedBox(height: 16),
..._currentAccount!.withdrawalRecords.map((record) => _buildWithdrawalRecordCard(record)),
],
);
}
Widget _buildWithdrawalRecordCard(WithdrawalRecord record) {
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: record.statusColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
record.status,
style: TextStyle(
fontSize: 10,
color: record.statusColor,
fontWeight: FontWeight.bold,
),
),
),
const Spacer(),
Text(
'${record.date.year}年${record.date.month}月${record.date.day}日',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'提取原因',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
record.reason,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'提取金额',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
Text(
'¥${record.amount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
],
),
],
),
const SizedBox(height: 8),
Row(
children: [
Text(
'审批机构:${record.approver}',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
),
if (record.remark.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
record.remark,
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
],
],
),
),
);
}
Widget _buildGuidePage() {
return ListView(
padding: const EdgeInsets.all(16),
children: [
const Text(
'提取指南',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
'了解各类公积金提取条件和办理流程',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
const SizedBox(height: 16),
..._withdrawalGuides.map((guide) => _buildGuideCard(guide)),
],
);
}
Widget _buildGuideCard(WithdrawalGuide guide) {
return Card(
margin: const EdgeInsets.only(bottom: 16),
child: ExpansionTile(
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: guide.categoryColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Icon(guide.categoryIcon, color: guide.categoryColor),
),
title: Text(
guide.title,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
'${guide.category} • ${guide.timeLimit}',
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildGuideSection('提取条件', guide.conditions),
const SizedBox(height: 16),
_buildGuideSection('所需材料', guide.materials),
const SizedBox(height: 16),
_buildGuideSection('办理流程', guide.procedures),
const SizedBox(height: 16),
_buildGuideInfo('办理时限', guide.timeLimit),
_buildGuideInfo('提取额度', guide.amount),
_buildGuideInfo('提取频次', guide.frequency),
const SizedBox(height: 16),
_buildGuideSection('注意事项', guide.notes),
],
),
),
],
),
);
}
Widget _buildGuideSection(String title, List<String> items) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
...items.asMap().entries.map((entry) {
final index = entry.key;
final item = entry.value;
return Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${index + 1}. ',
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
),
Expanded(
child: Text(
item,
style: const TextStyle(fontSize: 14),
),
),
],
),
);
}),
],
);
}
Widget _buildGuideInfo(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Text(
'$label:',
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
Expanded(
child: Text(
value,
style: const TextStyle(fontSize: 14),
),
),
],
),
);
}
}
// 数据模型类
class HousingFundAccount {
final String id;
final String accountNumber;
final String name;
final String idCard;
final String city;
final String company;
final double balance;
final double monthlyDeposit;
final double companyDeposit;
final double personalDeposit;
final DateTime openDate;
final DateTime lastUpdateTime;
final String accountStatus;
final List<DepositRecord> depositRecords;
final List<WithdrawalRecord> withdrawalRecords;
HousingFundAccount({
required this.id,
required this.accountNumber,
required this.name,
required this.idCard,
required this.city,
required this.company,
required this.balance,
required this.monthlyDeposit,
required this.companyDeposit,
required this.personalDeposit,
required this.openDate,
required this.lastUpdateTime,
required this.accountStatus,
required this.depositRecords,
required this.withdrawalRecords,
});
double get annualDeposit => monthlyDeposit * 12;
int get depositYears {
final now = DateTime.now();
return now.year - openDate.year;
}
Color get statusColor {
switch (accountStatus) {
case '正常':
return Colors.green;
case '封存':
return Colors.orange;
case '冻结':
return Colors.red;
default:
return Colors.grey;
}
}
String get balanceLevel {
if (balance >= 100000) return '高额';
if (balance >= 50000) return '中等';
if (balance >= 20000) return '一般';
return '较低';
}
Color get balanceLevelColor {
switch (balanceLevel) {
case '高额':
return Colors.red;
case '中等':
return Colors.orange;
case '一般':
return Colors.blue;
default:
return Colors.grey;
}
}
}
class DepositRecord {
final String id;
final DateTime date;
final double personalAmount;
final double companyAmount;
final double totalAmount;
final String type;
final String remark;
DepositRecord({
required this.id,
required this.date,
required this.personalAmount,
required this.companyAmount,
required this.totalAmount,
required this.type,
required this.remark,
});
Color get typeColor {
switch (type) {
case '正常缴存':
return Colors.green;
case '补缴':
return Colors.orange;
case '调整':
return Colors.blue;
default:
return Colors.grey;
}
}
}
class WithdrawalRecord {
final String id;
final DateTime date;
final double amount;
final String reason;
final String status;
final String approver;
final String remark;
WithdrawalRecord({
required this.id,
required this.date,
required this.amount,
required this.reason,
required this.status,
required this.approver,
required this.remark,
});
Color get statusColor {
switch (status) {
case '已到账':
return Colors.green;
case '审核中':
return Colors.orange;
case '已拒绝':
return Colors.red;
default:
return Colors.grey;
}
}
}
class WithdrawalGuide {
final String id;
final String title;
final String category;
final List<String> conditions;
final List<String> materials;
final List<String> procedures;
final String timeLimit;
final String amount;
final String frequency;
final List<String> notes;
WithdrawalGuide({
required this.id,
required this.title,
required this.category,
required this.conditions,
required this.materials,
required this.procedures,
required this.timeLimit,
required this.amount,
required this.frequency,
required this.notes,
});
Color get categoryColor {
switch (category) {
case '购房':
return Colors.red;
case '租房':
return Colors.blue;
case '还贷':
return Colors.green;
case '装修':
return Colors.orange;
case '退休':
return Colors.purple;
case '医疗':
return Colors.pink;
default:
return Colors.grey;
}
}
IconData get categoryIcon {
switch (category) {
case '购房':
return Icons.home;
case '租房':
return Icons.apartment;
case '还贷':
return Icons.payment;
case '装修':
return Icons.build;
case '退休':
return Icons.elderly;
case '医疗':
return Icons.local_hospital;
default:
return Icons.help;
}
}
}
使用说明
1. 环境要求
- Flutter SDK 3.0+
- Dart 3.0+
- 支持Material Design 3
- 无需额外依赖包
2. 运行步骤
bash
# 1. 创建Flutter项目
flutter create housing_fund_query
# 2. 进入项目目录
cd housing_fund_query
# 3. 替换main.dart文件内容
# 4. 运行项目
flutter run
3. 功能使用
账户余额页面
- 查看圆形余额展示
- 查看缴存信息统计
- 查看账户基本信息
- 使用快捷操作按钮
缴存记录页面
- 切换缴存/提取记录标签
- 查看统计数据
- 浏览详细记录列表
提取指南页面
- 浏览6类提取指南
- 展开查看详细信息
- 了解办理条件和流程
城市切换功能
- 点击顶部城市按钮
- 选择30个支持城市
- 自动生成新的账户数据
数据刷新功能
- 点击刷新按钮
- 2秒加载动画
- 重新生成模拟数据
项目特色
1. 真实业务场景
- 完整的公积金业务流程
- 真实的数据结构设计
- 符合实际使用习惯
2. 优秀的用户体验
- Material Design 3设计
- 流畅的页面切换
- 直观的数据展示
3. 完善的功能覆盖
- 账户管理
- 记录查询
- 指南浏览
- 城市切换
4. 高质量代码
- 清晰的代码结构
- 完善的数据模型
- 良好的性能优化
扩展建议
1. 数据持久化
dart
// 使用SharedPreferences保存数据
import 'package:shared_preferences/shared_preferences.dart';
class DataStorage {
static Future<void> saveAccount(HousingFundAccount account) async {
final prefs = await SharedPreferences.getInstance();
// 保存账户数据
}
static Future<HousingFundAccount?> loadAccount() async {
final prefs = await SharedPreferences.getInstance();
// 加载账户数据
}
}
2. 网络请求
dart
// 使用http包进行网络请求
import 'package:http/http.dart' as http;
class ApiService {
static Future<HousingFundAccount> fetchAccount(String accountNumber) async {
final response = await http.get(
Uri.parse('https://api.example.com/account/$accountNumber'),
);
// 处理响应数据
}
}
3. 状态管理
dart
// 使用Provider进行状态管理
import 'package:provider/provider.dart';
class AccountProvider extends ChangeNotifier {
HousingFundAccount? _account;
HousingFundAccount? get account => _account;
void updateAccount(HousingFundAccount account) {
_account = account;
notifyListeners();
}
}
4. 主题定制
dart
// 自定义主题
ThemeData buildTheme() {
return ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.green,
brightness: Brightness.light,
),
cardTheme: CardTheme(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
useMaterial3: true,
);
}
总结
这个Flutter全国公积金查询应用展示了如何构建一个功能完整、用户体验优秀的移动应用。通过合理的架构设计、优雅的UI实现和完善的功能覆盖,为用户提供了便捷的公积金管理服务。
项目的核心价值在于:
- 实用性:解决真实的用户需求
- 完整性:覆盖完整的业务流程
- 可扩展性:支持功能扩展和优化
- 学习价值:展示Flutter开发最佳实践
通过学习这个项目,开发者可以掌握Flutter应用开发的核心技能,包括状态管理、UI设计、数据处理等关键技术点。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net