【maaath】Flutter for OpenHarmony 实战:记账理财应用开发指南

Flutter for OpenHarmony 实战:记账理财应用开发指南

前言

欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net


在移动互联网时代,个人财务管理已成为现代人生活中不可或缺的一部分。一款好用、美观的记账应用,能帮助用户养成良好的消费习惯,实现财务目标。本文将详细介绍如何使用 Flutter 开发一款功能完善的记账理财应用,并成功运行在 OpenHarmony 设备上。通过这个实战项目,读者将掌握 Flutter 跨平台开发的核心技能,为后续开发更复杂的应用打下坚实基础。

一、项目概述与功能设计

本文要实现的记账理财应用具备以下核心功能:

核心功能模块:

  • 账单列表展示:支持按日期分组显示收支明细
  • 统计图表页面:饼图展示支出构成,柱状图展示收支趋势
  • 预算管理功能:分类预算设置与进度追踪
  • 个人中心页面:用户数据统计与设置入口
  • 底部选项卡导航:账单/统计/预算/我的四大模块
  • 下拉刷新与上拉加载:流畅的数据交互体验

技术特点:

  • 采用 Flutter 3.x 框架,Dart 语言开发
  • 支持 OpenHarmony 设备运行
  • 模拟数据服务,便于快速调试
  • 动画效果增强用户体验

1.1 项目架构设计

良好的项目结构是大型应用开发的基石。本次记账应用采用经典的三层架构设计:

复制代码
lib/
├── main.dart                    # 应用入口
├── models/
│   └── bill_model.dart         # 数据模型层
├── services/
│   └── bill_service.dart       # 业务服务层
└── pages/
    ├── bill_main_page.dart     # 主页面与导航
    ├── bills_page.dart          # 账单列表页
    ├── statistics_page.dart      # 统计图表页
    ├── budget_page.dart         # 预算管理页
    └── profile_page.dart        # 个人中心页

这种分层设计使得代码职责清晰,便于维护和扩展。模型层负责数据结构定义,服务层处理业务逻辑,页面层负责 UI 展示。

二、数据模型层实现

数据模型是应用的数据骨架,定义清晰的数据结构对于后续开发至关重要。

dart 复制代码
// lib/models/bill_model.dart

/// 账单分类数据模型
class BillCategory {
  final int id;
  final String name;
  final String icon;
  final Color color;
  final String type;

  const BillCategory({
    required this.id,
    required this.name,
    required this.icon,
    required this.color,
    required this.type,
  });
}

/// 账单数据模型
class Bill {
  final int id;
  final double amount;
  final int categoryId;
  final String categoryName;
  final String categoryIcon;
  final Color categoryColor;
  final String note;
  final String date;
  final String type;

  const Bill({
    required this.id,
    required this.amount,
    required this.categoryId,
    required this.categoryName,
    required this.categoryIcon,
    required this.categoryColor,
    required this.note,
    required this.date,
    required this.type,
  });
}

在 Dart 中,使用 const 构造函数可以创建不可变对象,这对于并发场景和数据一致性非常有帮助。模型类中包含了账单的核心属性:金额、分类、备注、日期等。

三、服务层架构

服务层承担着数据获取和处理的重任。为了便于读者快速运行和调试,本次采用模拟数据的方式。

dart 复制代码
// lib/services/bill_service.dart

/// 支出分类数据
const List<BillCategory> expenseCategories = [
  BillCategory(id: 1, name: '餐饮', icon: '🍝', color: Color(0xFFFF6B6B), type: 'expense'),
  BillCategory(id: 2, name: '交通', icon: '🚗', color: Color(0xFF4ECDC4), type: 'expense'),
  BillCategory(id: 3, name: '购物', icon: '🛍️', color: Color(0xFFFFE66D), type: 'expense'),
  // ... 更多分类
];

/// 账单服务类 - 模拟网络请求
class BillService {
  /// 获取账单列表
  Future<List<Bill>> getBills(int page, {int pageSize = 20}) async {
    await Future.delayed(const Duration(milliseconds: 500));
    return _generateMockBills(page, pageSize);
  }

  /// 获取月度统计
  Future<MonthStatistic> getMonthStatistics(String? month) async {
    // 业务逻辑实现
  }
}

服务层采用 Promise 风格的异步编程,通过 Future 实现数据获取。这种模式在 Flutter 开发中非常常见,读者可以轻松替换为真实的 HTTP 请求。

四、主页面与导航设计

底部导航栏是移动应用最常见的交互模式之一,通过 BottomNavigationBar 可以快速实现这一功能。

dart 复制代码
// lib/pages/bill_main_page.dart

class BillMainPage extends StatefulWidget {
  const BillMainPage({super.key});

  @override
  State<BillMainPage> createState() => _BillMainPageState();
}

class _BillMainPageState extends State<BillMainPage> {
  int _currentIndex = 0;

  final List<Widget> _pages = const [
    BillsPage(),
    StatisticsPage(),
    BudgetPage(),
    ProfilePage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: IndexedStack(
        index: _currentIndex,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) {
          setState(() => _currentIndex = index);
        },
        type: BottomNavigationBarType.fixed,
        selectedItemColor: const Color(0xFF4A90E2),
        items: const [
          BottomNavigationBarItem(icon: Text('💰'), label: '账单'),
          BottomNavigationBarItem(icon: Text('📊'), label: '统计'),
          BottomNavigationBarItem(icon: Text('🎯'), label: '预算'),
          BottomNavigationBarItem(icon: Text('👤'), label: '我的'),
        ],
      ),
    );
  }
}

这里使用 IndexedStack 替代简单的 Stack,可以有效保持各页面的状态,避免切换时重新加载数据,提升用户体验。

五、账单列表页面实现

账单列表是应用的核心页面,需要实现下拉刷新和上拉加载功能。

dart 复制代码
// lib/pages/bills_page.dart

class BillsPage extends StatefulWidget {
  const BillsPage({super.key});

  @override
  State<BillsPage> createState() => _BillsPageState();
}

class _BillsPageState extends State<BillsPage> {
  final ScrollController _scrollController = ScrollController();

  Future<void> _loadMore() async {
    if (_isLoadingMore || !_hasMore) return;
    setState(() => _isLoadingMore = true);
    _currentPage++;
    await _loadData();
  }

  void _onScroll() {
    if (_scrollController.position.pixels >=
        _scrollController.position.maxScrollExtent - 200) {
      _loadMore();
    }
  }

  @override
  Widget build(BuildContext context) {
    return RefreshIndicator(
      onRefresh: _onRefresh,
      child: ListView.builder(
        controller: _scrollController,
        itemCount: groups.length + 1,
        itemBuilder: (context, index) {
          // 列表项渲染逻辑
        },
      ),
    );
  }
}

下拉刷新通过 RefreshIndicator 组件实现,当用户下拉时会触发 _onRefresh 回调。上拉加载则通过监听滚动控制器位置,在接近底部时自动加载更多数据。

六、统计图表页面

统计页面需要展示饼图和柱状图,并带有动画效果,让数据可视化更加生动。

dart 复制代码
// lib/pages/statistics_page.dart

class StatisticsPage extends StatefulWidget {
  const StatisticsPage({super.key});

  @override
  State<StatisticsPage> createState() => _StatisticsPageState();
}

class _StatisticsPageState extends State<StatisticsPage>
    with SingleTickerProviderStateMixin {
  late AnimationController _animationController;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: const Duration(milliseconds: 800),
      vsync: this,
    );
    _animation = CurvedAnimation(
      parent: _animationController,
      curve: Curves.easeOut,
    );
    _loadData();
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  Widget _buildPieChart() {
    return AnimatedBuilder(
      animation: _animation,
      builder: (context, child) {
        return SizedBox(
          width: 140,
          height: 140,
          child: Stack(
            alignment: Alignment.center,
            children: [
              // 饼图渲染
              ..._buildPieRings(),
              // 中心文字
              Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('总支出', style: TextStyle(fontSize: 12)),
                  Text('¥${_monthStats?.totalExpense ?? 0}', style: TextStyle(fontSize: 18)),
                ],
              ),
            ],
          ),
        );
      },
    );
  }
}

动画是提升应用质感的利器。使用 AnimationController 配合 AnimatedBuilder,可以让图表在数据加载后有一个平滑的出现动画,给用户带来更流畅的视觉体验。

七、预算管理页面

预算页面展示用户的预算设置和消费进度,使用渐变色卡片和动画进度条。

dart 复制代码
// lib/pages/budget_page.dart

Widget _buildOverviewCard() {
  return Container(
    padding: const EdgeInsets.all(20),
    decoration: BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
        colors: [_themeColor, _themeColor.withOpacity(0.8)],
      ),
      borderRadius: BorderRadius.circular(16),
      boxShadow: [
        BoxShadow(
          color: _themeColor.withOpacity(0.3),
          blurRadius: 12,
          offset: const Offset(0, 4),
        ),
      ],
    ),
    child: Column(
      children: [
        // 预算金额
        TweenAnimationBuilder<double>(
          tween: Tween(begin: 0, end: _totalBudget),
          duration: const Duration(milliseconds: 600),
          builder: (context, value, child) {
            return Text('¥${value.toStringAsFixed(2)}');
          },
        ),
        // 进度条
        Stack(
          children: [
            Container(height: 8, decoration: BoxDecoration(
              color: Colors.white30,
              borderRadius: BorderRadius.circular(4),
            )),
            FractionallySizedBox(
              widthFactor: progress,
              child: Container(height: 8, decoration: BoxDecoration(
                color: _getProgressColor(progress),
                borderRadius: BorderRadius.circular(4),
              )),
            ),
          ],
        ),
      ],
    ),
  );
}

预算卡片采用渐变色设计,配合阴影效果营造层次感。进度条使用 TweenAnimationBuilder 实现数字动画,让预算金额从 0 渐变到实际值。

八、个人中心页面

个人中心展示用户信息和各种功能入口。

dart 复制代码
// lib/pages/profile_page.dart

Widget _buildUserHeader() {
  return Container(
    padding: const EdgeInsets.fromLTRB(20, 40, 20, 30),
    decoration: BoxDecoration(
      gradient: LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
        colors: [_themeColor, _themeColor.withOpacity(0.8)],
      ),
    ),
    child: Row(
      children: [
        Container(
          width: 70,
          height: 70,
          decoration: BoxDecoration(
            color: Colors.white.withOpacity(0.2),
            shape: BoxShape.circle,
          ),
          alignment: Alignment.center,
          child: const Text('记', style: TextStyle(fontSize: 28, color: Colors.white)),
        ),
        const SizedBox(width: 16),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(_userName, style: const TextStyle(fontSize: 20, color: Colors.white)),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
              decoration: BoxDecoration(
                color: Colors.white.withOpacity(0.2),
                borderRadius: BorderRadius.circular(10),
              ),
              child: Text(_memberLevel, style: const TextStyle(fontSize: 12)),
            ),
          ],
        ),
      ],
    ),
  );
}

头部区域采用渐变色背景,配合半透明的头像容器,营造出简洁而不失个性的视觉效果。

九、应用入口配置

最后,配置应用入口文件,设置主题色和默认页面。

dart 复制代码
// lib/main.dart

import 'package:flutter/material.dart';
import 'pages/bill_main_page.dart';

void main() {
  runApp(const BillApp());
}

class BillApp extends StatelessWidget {
  const BillApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '记账理财',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: const Color(0xFF4A90E2),
        scaffoldBackgroundColor: const Color(0xFFF5F5F5),
        useMaterial3: true,
      ),
      home: const BillMainPage(),
    );
  }
}

十、截图运行板块

以下是记账理财应用在 OpenHarmony 设备上的运行截图:

1. 账单页面

在账单页面中,我们可以看到:

  • 顶部蓝色背景展示当前月份和收支统计
  • 收支数据清晰展示:支出、收入、结余
  • 月份切换按钮方便查看历史数据
  • 账单按日期分组,每条账单显示分类图标、名称、金额
  • 支出显示红色,收入显示绿色,一目了然
  • 支持下拉刷新和上拉加载更多

2. 统计页面

统计页面的特点:

  • 月份选择器可查看不同时间段的数据
  • 收入/支出/结余三大指标清晰展示
  • 饼图展示支出构成比例,直观了解消费分布
  • 柱状图展示近6个月的收支趋势
  • 动画效果让图表出现更加流畅
  • 分类图例详细标注各项支出占比

3. 预算页面

预算管理页面功能:

  • 总预算卡片采用渐变色设计,视觉效果突出
  • 进度条实时显示预算消耗情况
  • 不同进度阶段显示不同颜色(绿色-正常、橙色-警告、红色-超支)
  • 分类预算列表展示各支出类别的预算和消费情况
  • 小贴士提示帮助用户养成良好消费习惯

4. 我的页面

个人中心页面包含:

  • 用户头像和昵称展示
  • 会员等级标识
  • 记账数据统计:笔数、天数、累计支出
  • 功能菜单入口:账单导出、提醒设置、账户管理等
  • 设置选项:通用设置、隐私安全、数据备份
  • 版本信息展示

十一、总结与展望

通过本文的实战开发,读者应该已经掌握了以下技能:

核心技能点:

  1. Flutter 项目结构设计和规范
  2. 数据模型的设计与类型定义
  3. 服务层架构和异步编程
  4. 底部导航栏的实现与状态管理
  5. 下拉刷新与上拉加载更多
  6. 动画控制器和 Tween 动画
  7. 统计图表的自定义绘制
  8. OpenHarmony 设备适配要点

Flutter for OpenHarmony 为开发者提供了"一次开发,多端运行"的强大能力。本文实现的记账理财应用只是冰山一角,读者可以在此基础上进行功能扩展:

  • 添加真实的后端 API 对接
  • 实现数据本地持久化存储
  • 增加账单分类管理功能
  • 添加数据导出功能
  • 实现预算提醒通知

希望本文能帮助读者快速入门 Flutter 鸿蒙开发,在实际项目中发挥跨平台技术的优势。

附录

完整代码仓库

本文涉及的完整代码已托管至 AtomGit:

依赖配置

yaml 复制代码
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

相关推荐
jiejiejiejie_6 小时前
Flutter for OpenHarmony 账单记录功能实战指南
flutter
千码君20166 小时前
flutter: 分享一下基于trae cn 构建的过程
java·vscode·flutter·kotlin·trae
MonkeyKing6 小时前
Flutter高级动画体系实战:从基础封装到自定义动画
flutter
MonkeyKing6 小时前
Flutter手势系统与冲突处理实战
flutter
key_3_feng6 小时前
鸿蒙6.0地图导航应用(可集成到其他APP)开发方案
华为·harmonyos
UnicornDev6 小时前
【HarmonyOS 6】底部悬浮导航的沉浸光感适配(API23)
华为·harmonyos·arkts·鸿蒙·鸿蒙系统
轻口味6 小时前
HarmonyOS 6 轻相机应用开发5:实时自动戴眼镜功能实现
数码相机·华为·harmonyos
maaath6 小时前
【maaath】Flutter for OpenHarmony 实战:构建跨平台房产租售应用
flutter·华为·harmonyos
骆驼10246 小时前
华为AC+FIT AP典型组网部署配置
华为·hcia·ac+ap