Flutter 框架跨平台鸿蒙开发 - 全国公积金查询:智能公积金管理助手

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:年缴存总额 = 月缴存额 × 12
  • depositYears:缴存年限 = 当前年份 - 开户年份
  • 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,
  );
}

数据生成特点

  1. 24个月缴存记录:模拟2年的缴存历史
  2. 5条提取记录:不同类型的提取申请
  3. 随机金额生成:符合实际缴存标准
  4. 状态随机分配:90%正常状态,10%异常状态
  5. 时间序列生成:按月份倒序生成记录
  6. 账号自动生成:城市代码+年份+随机数字
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]),
          ),
        ],
      ),
    ),
  );
}

圆形余额展示特点

  1. 150x150像素圆形容器:突出显示余额
  2. 绿色主题:象征财富和安全
  3. 余额等级标签:右上角显示余额等级
  4. 更新时间显示:底部显示最后更新时间
  5. 透明背景:圆形背景透明度0.1
  6. 边框设计: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,
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ],
      ),
    ),
  );
}

缴存信息展示特点

  1. 三项核心指标:月缴存额、年缴存额、缴存年限
  2. 颜色区分:蓝色、橙色、紫色区分不同指标
  3. 分割线设计:Divider分隔上下两部分
  4. 详细分解:个人缴存、单位缴存、开户时间
  5. 响应式布局: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,
          ),
        ],
      ),
    ),
  );
}

快捷操作特点

  1. 四个核心功能:提取申请、查看记录、贷款计算、在线客服
  2. 颜色主题:绿色、蓝色、橙色、紫色区分功能
  3. 图标标识:直观的功能图标
  4. 点击响应:InkWell提供点击反馈
  5. 页面跳转:部分功能跳转到对应页面
  6. 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设计特点

  1. 两个标签页:缴存记录和提取记录
  2. 灰色背景:Container提供背景色
  3. DefaultTabController:自动管理标签状态
  4. TabBarView:对应的页面内容
  5. 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)),
    ],
  );
}

缴存统计特点

  1. 三项统计指标:缴存月数、累计缴存、月均缴存
  2. 动态计算:使用map和reduce计算统计数据
  3. 颜色区分:蓝色、绿色、橙色区分指标
  4. 实时更新:数据随记录变化自动更新

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,
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    ),
  );
}

城市选择器特点

  1. 模态底部表单:showModalBottomSheet展示
  2. 网格布局:3列网格,宽高比2.5:1
  3. 30个城市:覆盖全国主要城市
  4. 选中状态:蓝色边框和背景突出显示
  5. 点击切换:选择城市后自动生成新数据
  6. 自动关闭:选择后自动关闭弹窗

支持城市列表

  • 直辖市:北京、上海、天津、重庆
  • 省会城市:广州、杭州、南京、武汉、成都、西安等
  • 重要城市:深圳、青岛、大连、厦门、宁波等

技术要点详解

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最佳实践

  1. 使用DefaultTabController
dart 复制代码
DefaultTabController(
  length: 2,  // 标签页数量
  child: Column(
    children: [
      TabBar(tabs: [...]),
      Expanded(child: TabBarView(children: [...])),
    ],
  ),
)
  1. 自定义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();
  }
}
  1. 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;
      });
    });
  }
}

状态分类

  1. UI状态:页面索引、加载状态、选择状态
  2. 数据状态:业务数据、列表数据、缓存数据
  3. 配置状态:用户设置、主题配置、语言设置

状态管理原则

  • 最小化状态:只存储必要的状态
  • 单一数据源:避免状态重复
  • 及时清理:防止内存泄漏
  • 合理粒度:避免过度重建

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)),
            ),
          );
        },
      ),
    ),
  );
}

切换流程

  1. 显示城市选择器
  2. 用户选择新城市
  3. 更新选中城市状态
  4. 重新生成账户数据
  5. 关闭选择器
  6. 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

相关推荐
大雷神2 小时前
HarmonyOS智慧农业管理应用开发教程--高高种地--第11篇:任务管理与提醒系统
harmonyos
翰德恩咨询2 小时前
DSTE咨询洞见:华为战略管理体系的进化之路
华为·华为战略·dste
zilikew2 小时前
Flutter框架跨平台鸿蒙开发——小说人物生成APP开发流程
flutter·华为·harmonyos·鸿蒙
kirk_wang2 小时前
Flutter艺术探索-Flutter文件操作:path_provider与文件管理
flutter·移动开发·flutter教程·移动开发教程
ShuiShenHuoLe2 小时前
记录一次在使用beta版DevecoStudio打包的问题
harmonyos
@大迁世界2 小时前
Swift、Flutter 还是 React Native:2026 年你该学哪个
开发语言·flutter·react native·ios·swift
zilikew2 小时前
Flutter框架跨平台鸿蒙开发——图书馆座位预约APP开发流程
flutter·华为·harmonyos·鸿蒙
Easonmax2 小时前
基础入门 React Native 鸿蒙跨平台开发:积分商城页面实现(积分商品+兑换+记录)
react native·react.js·harmonyos
小学生波波2 小时前
HarmonyOS6 - 鸿蒙AI卡证识别实战案例
ai·harmonyos·鸿蒙ai·卡证识别