Flutter框架跨平台鸿蒙开发——水电缴费提醒APP的开发流程

🚀运行效果展示


Flutter框架跨平台鸿蒙开发------水电费缴费提醒APP的开发流程

前言

随着移动互联网的快速发展,跨平台开发已经成为移动应用开发的主流趋势。Flutter作为Google推出的开源UI工具包,以其"一次编写,多处运行"的特性,为开发者提供了高效、统一的开发体验。本文将详细介绍如何使用Flutter框架开发一款水电费缴费提醒APP,并实现跨平台运行,特别是在鸿蒙操作系统上的适配。

APP介绍

水电费缴费提醒APP是一款专为用户提供水电费账单管理、缴费提醒和统计分析的移动应用。该APP具有以下特点:

  • 账单管理:支持添加、编辑、删除水费和电费账单
  • 缴费提醒:可设置提前提醒天数和提醒时间,避免逾期缴费
  • 统计分析:提供水费、电费的使用趋势和缴费记录统计
  • 多平台支持:基于Flutter框架,可在Android、iOS、Web和鸿蒙等平台运行

核心功能实现及代码展示

1. 项目架构设计

本项目采用典型的Flutter三层架构设计:

  1. 数据层:负责数据的存储和管理,使用Flutter Secure Storage进行本地存储
  2. 服务层:处理业务逻辑,包括账单管理、通知服务等
  3. UI层:负责用户界面的展示和交互

2. 数据模型设计

首先,我们需要设计水电费账单的数据模型:

dart 复制代码
/// 水电费账单模型
class UtilityBill {
  /// 账单ID
  final String id;
  /// 账单类型(水费/电费)
  final BillType type;
  /// 账单金额
  final double amount;
  /// 缴费截止日期
  final DateTime dueDate;
  /// 账单月份
  final String month;
  /// 缴费状态
  final PaymentStatus status;
  /// 账单描述
  final String description;

  /// 构造函数
  UtilityBill({
    required this.id,
    required this.type,
    required this.amount,
    required this.dueDate,
    required this.month,
    required this.status,
    this.description = '',
  });

  /// 从Map创建UtilityBill实例
  factory UtilityBill.fromMap(Map<String, dynamic> map) {
    return UtilityBill(
      id: map['id'],
      type: map['type'] == 'water' ? BillType.water : BillType.electricity,
      amount: map['amount'],
      dueDate: DateTime.parse(map['dueDate']),
      month: map['month'],
      status: map['status'] == 'unpaid' ? PaymentStatus.unpaid : PaymentStatus.paid,
      description: map['description'] ?? '',
    );
  }

  /// 转换为Map
  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'type': type == BillType.water ? 'water' : 'electricity',
      'amount': amount,
      'dueDate': dueDate.toIso8601String(),
      'month': month,
      'status': status == PaymentStatus.unpaid ? 'unpaid' : 'paid',
      'description': description,
    };
  }

  /// 计算距离截止日期的天数
  int get daysUntilDue {
    final now = DateTime.now();
    final due = DateTime(dueDate.year, dueDate.month, dueDate.day);
    final today = DateTime(now.year, now.month, now.day);
    return due.difference(today).inDays;
  }

  /// 判断是否逾期
  bool get isOverdue {
    return daysUntilDue < 0;
  }
}

/// 账单类型枚举
enum BillType {
  water, // 水费
  electricity, // 电费
}

/// 缴费状态枚举
enum PaymentStatus {
  unpaid, // 未缴费
  paid, // 已缴费
}

3. 服务层实现

3.1 账单服务

账单服务负责账单的增删改查和统计功能:

dart 复制代码
/// 水电费账单服务
class UtilityBillService {
  /// 存储键
  static const String _storageKey = 'utility_bills';
  /// 安全存储实例
  final FlutterSecureStorage _storage = FlutterSecureStorage();

  /// 获取所有账单
  Future<List<UtilityBill>> getBills() async {
    try {
      final billsJson = await _storage.read(key: _storageKey);
      if (billsJson == null) {
        return [];
      }

      final List<dynamic> billsList = json.decode(billsJson);
      return billsList
          .map((bill) => UtilityBill.fromMap(bill))
          .toList();
    } catch (e) {
      print('Error getting bills: $e');
      return [];
    }
  }

  /// 获取未缴费账单
  Future<List<UtilityBill>> getUnpaidBills() async {
    final allBills = await getBills();
    return allBills.where((bill) => bill.status == PaymentStatus.unpaid).toList();
  }

  /// 添加新账单
  Future<void> addBill(UtilityBill bill) async {
    try {
      final bills = await getBills();
      bills.add(bill);
      await _saveBills(bills);
    } catch (e) {
      print('Error adding bill: $e');
      throw Exception('添加账单失败');
    }
  }

  /// 更新账单
  Future<void> updateBill(UtilityBill updatedBill) async {
    try {
      final bills = await getBills();
      final index = bills.indexWhere((bill) => bill.id == updatedBill.id);
      if (index != -1) {
        bills[index] = updatedBill;
        await _saveBills(bills);
      }
    } catch (e) {
      print('Error updating bill: $e');
      throw Exception('更新账单失败');
    }
  }

  /// 删除账单
  Future<void> deleteBill(String billId) async {
    try {
      final bills = await getBills();
      bills.removeWhere((bill) => bill.id == billId);
      await _saveBills(bills);
    } catch (e) {
      print('Error deleting bill: $e');
      throw Exception('删除账单失败');
    }
  }

  /// 标记账单为已缴费
  Future<void> markAsPaid(String billId) async {
    try {
      final bills = await getBills();
      final index = bills.indexWhere((bill) => bill.id == billId);
      if (index != -1) {
        final updatedBill = UtilityBill(
          id: bills[index].id,
          type: bills[index].type,
          amount: bills[index].amount,
          dueDate: bills[index].dueDate,
          month: bills[index].month,
          status: PaymentStatus.paid,
          description: bills[index].description,
        );
        bills[index] = updatedBill;
        await _saveBills(bills);
      }
    } catch (e) {
      print('Error marking bill as paid: $e');
      throw Exception('标记账单为已缴费失败');
    }
  }

  /// 保存账单列表到存储
  Future<void> _saveBills(List<UtilityBill> bills) async {
    try {
      final billsJson = json.encode(
        bills.map((bill) => bill.toMap()).toList(),
      );
      await _storage.write(key: _storageKey, value: billsJson);
    } catch (e) {
      print('Error saving bills: $e');
      throw Exception('保存账单失败');
    }
  }

  /// 计算总未缴费金额
  Future<double> getTotalUnpaidAmount() async {
    final unpaidBills = await getUnpaidBills();
    double total = 0.0;
    for (final bill in unpaidBills) {
      total += bill.amount;
    }
    return total;
  }
}
3.2 通知服务

通知服务负责在账单到期前发送提醒:

dart 复制代码
/// 通知服务
class NotificationService {
  /// 单例实例
  static final NotificationService _instance = NotificationService._internal();
  /// 本地通知插件
  final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
  /// 安全存储实例
  final FlutterSecureStorage _storage = FlutterSecureStorage();
  /// 账单服务实例
  final UtilityBillService _billService = UtilityBillService();

  /// 工厂构造函数
  factory NotificationService() {
    return _instance;
  }

  /// 内部构造函数
  NotificationService._internal();

  /// 初始化通知服务
  Future<void> initialize() async {
    const AndroidInitializationSettings initializationSettingsAndroid =
        AndroidInitializationSettings('@mipmap/ic_launcher');
    const DarwinInitializationSettings initializationSettingsIOS =
        DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
    );
    const InitializationSettings initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsIOS,
    );

    await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
  }

  /// 检查并发送通知
  Future<void> checkAndSendNotifications() async {
    try {
      // 检查是否启用通知
      final enableReminders = await _storage.read(key: 'enable_reminders');
      if (enableReminders != 'true') {
        return;
      }

      // 获取提醒天数
      final reminderDaysStr = await _storage.read(key: 'reminder_days');
      final reminderDays = int.tryParse(reminderDaysStr ?? '3') ?? 3;

      // 获取所有未缴费账单
      final unpaidBills = await _billService.getUnpaidBills();
      final now = DateTime.now();

      for (final bill in unpaidBills) {
        final dueDate = DateTime(bill.dueDate.year, bill.dueDate.month, bill.dueDate.day);
        final daysUntilDue = dueDate.difference(now).inDays;

        // 检查是否需要发送提前提醒
        if (daysUntilDue == reminderDays) {
          await _sendNotification(
            title: '缴费提醒',
            body: '${bill.type == BillType.water ? '水费' : '电费'}账单将于$reminderDays天后到期,金额:¥${bill.amount.toStringAsFixed(2)}',
            payload: bill.id,
          );
        }

        // 检查是否需要发送逾期提醒
        if (daysUntilDue < 0) {
          final enableOverdueReminders = await _storage.read(key: 'enable_overdue_reminders');
          if (enableOverdueReminders == 'true') {
            await _sendNotification(
              title: '逾期提醒',
              body: '${bill.type == BillType.water ? '水费' : '电费'}账单已逾期${-daysUntilDue}天,金额:¥${bill.amount.toStringAsFixed(2)}',
              payload: bill.id,
            );
          }
        }
      }
    } catch (e) {
      print('Error checking and sending notifications: $e');
    }
  }

  /// 发送通知
  Future<void> _sendNotification({
    required String title,
    required String body,
    required String payload,
  }) async {
    try {
      const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails(
        'utility_bill_reminders',
        '水电费缴费提醒',
        channelDescription: '用于提醒用户水电费缴费',
        importance: Importance.max,
        priority: Priority.high,
        ticker: 'ticker',
      );
      const DarwinNotificationDetails iOSPlatformChannelSpecifics = DarwinNotificationDetails();
      const NotificationDetails platformChannelSpecifics = NotificationDetails(
        android: androidPlatformChannelSpecifics,
        iOS: iOSPlatformChannelSpecifics,
      );

      await _flutterLocalNotificationsPlugin.show(
        DateTime.now().millisecondsSinceEpoch ~/ 1000,
        title,
        body,
        platformChannelSpecifics,
        payload: payload,
      );
    } catch (e) {
      print('Error sending notification: $e');
    }
  }
}

4. UI层实现

4.1 主页面

主页面展示账单列表,并提供添加、编辑、删除账单的功能:

dart 复制代码
/// 水电费账单管理页面
class UtilityBillScreen extends StatefulWidget {
  /// 构造函数
  const UtilityBillScreen({Key? key}) : super(key: key);

  @override
  _UtilityBillScreenState createState() => _UtilityBillScreenState();
}

class _UtilityBillScreenState extends State<UtilityBillScreen> {
  /// 账单服务实例
  final UtilityBillService _billService = UtilityBillService();
  /// 账单列表
  List<UtilityBill> _bills = [];
  /// 加载状态
  bool _isLoading = true;
  /// 选中的标签页
  int _selectedTab = 0;

  @override
  void initState() {
    super.initState();
    _loadBills();
  }

  /// 加载账单数据
  Future<void> _loadBills() async {
    setState(() {
      _isLoading = true;
    });

    try {
      final bills = await _billService.getBills();
      setState(() {
        _bills = bills;
        _isLoading = false;
      });
    } catch (e) {
      print('Error loading bills: $e');
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('加载账单失败')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    final filteredBills = _getFilteredBills();

    return Scaffold(
      appBar: AppBar(
        title: const Text('水电费管理'),
        backgroundColor: Colors.blue,
        actions: [
          IconButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const UtilityBillReminderSettingsScreen()),
              );
            },
            icon: const Icon(Icons.settings),
          ),
          IconButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const UtilityBillStatisticsScreen()),
              );
            },
            icon: const Icon(Icons.bar_chart),
          ),
        ],
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : Column(
              children: [
                // 标签页
                Container(
                  margin: const EdgeInsets.all(16),
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(12),
                    color: Colors.grey[200],
                  ),
                  child: Row(
                    children: [
                      Expanded(
                        child: GestureDetector(
                          onTap: () => setState(() => _selectedTab = 0),
                          child: Container(
                            padding: const EdgeInsets.symmetric(vertical: 12),
                            alignment: Alignment.center,
                            decoration: BoxDecoration(
                              borderRadius: const BorderRadius.only(
                                topLeft: Radius.circular(12),
                                bottomLeft: Radius.circular(12),
                              ),
                              color: _selectedTab == 0 ? Colors.blue : Colors.transparent,
                            ),
                            child: Text(
                              '全部',
                              style: TextStyle(
                                color: _selectedTab == 0 ? Colors.white : Colors.black,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ),
                        ),
                      ),
                      Expanded(
                        child: GestureDetector(
                          onTap: () => setState(() => _selectedTab = 1),
                          child: Container(
                            padding: const EdgeInsets.symmetric(vertical: 12),
                            alignment: Alignment.center,
                            decoration: BoxDecoration(
                              color: _selectedTab == 1 ? Colors.blue : Colors.transparent,
                            ),
                            child: Text(
                              '未缴费',
                              style: TextStyle(
                                color: _selectedTab == 1 ? Colors.white : Colors.black,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ),
                        ),
                      ),
                      Expanded(
                        child: GestureDetector(
                          onTap: () => setState(() => _selectedTab = 2),
                          child: Container(
                            padding: const EdgeInsets.symmetric(vertical: 12),
                            alignment: Alignment.center,
                            decoration: BoxDecoration(
                              borderRadius: const BorderRadius.only(
                                topRight: Radius.circular(12),
                                bottomRight: Radius.circular(12),
                              ),
                              color: _selectedTab == 2 ? Colors.blue : Colors.transparent,
                            ),
                            child: Text(
                              '已缴费',
                              style: TextStyle(
                                color: _selectedTab == 2 ? Colors.white : Colors.black,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
                // 账单列表
                Expanded(
                  child: filteredBills.isEmpty
                      ? const Center(
                          child: Text(
                            '暂无账单',
                            style: TextStyle(fontSize: 16, color: Colors.grey),
                          ),
                        )
                      : ListView.builder(
                          itemCount: filteredBills.length,
                          itemBuilder: (context, index) {
                            return _buildBillCard(filteredBills[index]);
                          },
                        ),
                ),
              ],
            ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          final result = await Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => const AddUtilityBillScreen()),
          );
          if (result == true) {
            _loadBills();
          }
        },
        backgroundColor: Colors.blue,
        child: const Icon(Icons.add),
      ),
    );
  }
}
4.2 提醒设置页面

提醒设置页面允许用户配置提醒相关的参数:

dart 复制代码
/// 缴费提醒设置页面
class UtilityBillReminderSettingsScreen extends StatefulWidget {
  /// 构造函数
  const UtilityBillReminderSettingsScreen({Key? key}) : super(key: key);

  @override
  _UtilityBillReminderSettingsScreenState createState() => _UtilityBillReminderSettingsScreenState();
}

class _UtilityBillReminderSettingsScreenState extends State<UtilityBillReminderSettingsScreen> {
  /// 安全存储实例
  final FlutterSecureStorage _storage = FlutterSecureStorage();
  /// 是否启用提醒
  bool _enableReminders = true;
  /// 提前提醒天数
  int _reminderDays = 3;
  /// 提醒时间
  TimeOfDay _reminderTime = TimeOfDay(hour: 9, minute: 0);
  /// 是否启用逾期提醒
  bool _enableOverdueReminders = true;
  /// 是否启用声音提醒
  bool _enableSound = true;
  /// 是否启用震动提醒
  bool _enableVibration = true;
  /// 加载状态
  bool _isLoading = true;

  @override
  void initState() {
    super.initState();
    _loadSettings();
  }

  /// 加载设置
  Future<void> _loadSettings() async {
    try {
      final enableReminders = await _storage.read(key: 'enable_reminders');
      final reminderDays = await _storage.read(key: 'reminder_days');
      final reminderTime = await _storage.read(key: 'reminder_time');
      final enableOverdueReminders = await _storage.read(key: 'enable_overdue_reminders');
      final enableSound = await _storage.read(key: 'enable_sound');
      final enableVibration = await _storage.read(key: 'enable_vibration');

      setState(() {
        _enableReminders = enableReminders != null ? enableReminders == 'true' : true;
        _reminderDays = reminderDays != null ? int.parse(reminderDays) : 3;
        _reminderTime = reminderTime != null
            ? TimeOfDay.fromDateTime(DateTime.parse(reminderTime))
            : TimeOfDay(hour: 9, minute: 0);
        _enableOverdueReminders = enableOverdueReminders != null ? enableOverdueReminders == 'true' : true;
        _enableSound = enableSound != null ? enableSound == 'true' : true;
        _enableVibration = enableVibration != null ? enableVibration == 'true' : true;
        _isLoading = false;
      });
    } catch (e) {
      print('Error loading settings: $e');
      setState(() {
        _isLoading = false;
      });
    }
  }

  /// 保存设置
  Future<void> _saveSettings() async {
    try {
      await _storage.write(key: 'enable_reminders', value: _enableReminders.toString());
      await _storage.write(key: 'reminder_days', value: _reminderDays.toString());
      await _storage.write(
        key: 'reminder_time',
        value: DateTime(
          DateTime.now().year,
          DateTime.now().month,
          DateTime.now().day,
          _reminderTime.hour,
          _reminderTime.minute,
        ).toIso8601String(),
      );
      await _storage.write(key: 'enable_overdue_reminders', value: _enableOverdueReminders.toString());
      await _storage.write(key: 'enable_sound', value: _enableSound.toString());
      await _storage.write(key: 'enable_vibration', value: _enableVibration.toString());

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('设置已保存')),
      );
    } catch (e) {
      print('Error saving settings: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('保存设置失败')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }

    return Scaffold(
      appBar: AppBar(
        title: const Text('提醒设置'),
        backgroundColor: Colors.blue,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: ListView(
          children: [
            // 提醒开关
            Card(
              margin: const EdgeInsets.only(bottom: 16),
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: SwitchListTile(
                  title: const Text('启用缴费提醒'),
                  subtitle: const Text('开启后将在账单到期前发送提醒'),
                  value: _enableReminders,
                  onChanged: (value) {
                    setState(() {
                      _enableReminders = value;
                    });
                  },
                  activeColor: Colors.blue,
                ),
              ),
            ),

            // 提前提醒天数
            Card(
              margin: const EdgeInsets.only(bottom: 16),
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text('提前提醒天数', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
                    const SizedBox(height: 12),
                    Row(
                      children: [
                        IconButton(
                          onPressed: _reminderDays > 1
                              ? () {
                                  setState(() {
                                    _reminderDays--;
                                  });
                                }
                              : null,
                          icon: const Icon(Icons.remove),
                        ),
                        Expanded(
                          child: Center(
                            child: Text(
                              '$_reminderDays 天',
                              style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                            ),
                          ),
                        ),
                        IconButton(
                          onPressed: _reminderDays < 30
                              ? () {
                                  setState(() {
                                    _reminderDays++;
                                  });
                                }
                              : null,
                          icon: const Icon(Icons.add),
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),

            // 保存按钮
            ElevatedButton(
              onPressed: _saveSettings,
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.blue,
                padding: const EdgeInsets.symmetric(vertical: 16),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
              ),
              child: const Text(
                '保存设置',
                style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
4.3 统计分析页面

统计分析页面展示账单的使用趋势和缴费记录:

dart 复制代码
/// 缴费记录追踪和统计页面
class UtilityBillStatisticsScreen extends StatefulWidget {
  /// 构造函数
  const UtilityBillStatisticsScreen({Key? key}) : super(key: key);

  @override
  _UtilityBillStatisticsScreenState createState() => _UtilityBillStatisticsScreenState();
}

class _UtilityBillStatisticsScreenState extends State<UtilityBillStatisticsScreen> {
  /// 账单服务实例
  final UtilityBillService _billService = UtilityBillService();
  /// 所有账单
  List<UtilityBill> _allBills = [];
  /// 加载状态
  bool _isLoading = true;
  /// 选中的时间范围
  String _selectedTimeRange = 'month';

  @override
  void initState() {
    super.initState();
    _loadBills();
  }

  /// 加载账单数据
  Future<void> _loadBills() async {
    setState(() {
      _isLoading = true;
    });

    try {
      final bills = await _billService.getBills();
      setState(() {
        _allBills = bills;
        _isLoading = false;
      });
    } catch (e) {
      print('Error loading bills: $e');
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('加载账单失败')),
      );
    }
  }

  /// 计算统计数据
  Map<String, double> _calculateStatistics() {
    final filteredBills = _filterBills();
    double totalWater = 0;
    double totalElectricity = 0;
    double totalPaid = 0;
    double totalUnpaid = 0;

    for (final bill in filteredBills) {
      if (bill.type == BillType.water) {
        totalWater += bill.amount;
      } else {
        totalElectricity += bill.amount;
      }

      if (bill.status == PaymentStatus.paid) {
        totalPaid += bill.amount;
      } else {
        totalUnpaid += bill.amount;
      }
    }

    return {
      'totalWater': totalWater,
      'totalElectricity': totalElectricity,
      'totalPaid': totalPaid,
      'totalUnpaid': totalUnpaid,
      'total': totalWater + totalElectricity,
    };
  }

  @override
  Widget build(BuildContext context) {
    if (_isLoading) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }

    final statistics = _calculateStatistics();

    return Scaffold(
      appBar: AppBar(
        title: const Text('缴费统计'),
        backgroundColor: Colors.blue,
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: ListView(
          children: [
            // 时间范围选择
            Container(
              margin: const EdgeInsets.only(bottom: 16),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  _buildTimeRangeButton('本月', 'month'),
                  _buildTimeRangeButton('本季度', 'quarter'),
                  _buildTimeRangeButton('本年', 'year'),
                  _buildTimeRangeButton('全部', 'all'),
                ],
              ),
            ),

            // 统计卡片
            GridView.count(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              crossAxisCount: 2,
              crossAxisSpacing: 8,
              mainAxisSpacing: 8,
              children: [
                _buildStatCard('水费总计', statistics['totalWater']!, Colors.blue),
                _buildStatCard('电费总计', statistics['totalElectricity']!, Colors.yellow),
                _buildStatCard('已缴费', statistics['totalPaid']!, Colors.green),
                _buildStatCard('未缴费', statistics['totalUnpaid']!, Colors.red),
              ],
            ),

            // 总费用
            Card(
              margin: const EdgeInsets.only(top: 16),
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    const Text('总费用', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
                    Text('¥${statistics['total']!.toStringAsFixed(2)}', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
                  ],
                ),
              ),
            ),

            // 缴费趋势
            _buildTrendChart(),

            // 缴费记录
            _buildPaymentHistory(),
          ],
        ),
      ),
    );
  }
}

5. 主入口配置

修改main.dart文件,配置应用的启动逻辑和路由:

dart 复制代码
import 'package:flutter/material.dart';
import 'screens/utility_bill_screen.dart';
import 'screens/add_utility_bill_screen.dart';
import 'screens/edit_utility_bill_screen.dart';
import 'screens/utility_bill_reminder_settings_screen.dart';
import 'screens/utility_bill_statistics_screen.dart';
import 'services/notification_service.dart';

/// 水电费缴费提醒APP主入口
/// 用于启动水电费缴费提醒APP应用
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化通知服务
  final notificationService = NotificationService();
  await notificationService.initialize();
  await notificationService.scheduleDailyNotificationCheck();

  runApp(const UtilityBillApp());
}

/// 水电费缴费提醒APP根组件
class UtilityBillApp extends StatelessWidget {
  /// 构造函数
  const UtilityBillApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '水电费缴费提醒',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
        appBarTheme: const AppBarTheme(
          backgroundColor: Colors.blue,
          elevation: 4,
          titleTextStyle: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.blue,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(12.0),
            ),
          ),
        ),
        cardTheme: CardTheme(
          elevation: 2,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16),
          ),
        ),
      ),
      debugShowCheckedModeBanner: false,
      initialRoute: '/',
      routes: {
        '/': (context) => const UtilityBillScreen(),
        '/add_bill': (context) => const AddUtilityBillScreen(),
        '/edit_bill': (context) => EditUtilityBillScreen(bill: ModalRoute.of(context)!.settings.arguments as dynamic),
        '/reminder_settings': (context) => const UtilityBillReminderSettingsScreen(),
        '/statistics': (context) => const UtilityBillStatisticsScreen(),
      },
    );
  }
}

鸿蒙平台适配

Flutter框架对鸿蒙平台的支持是通过Flutter Engine的鸿蒙适配实现的。在开发过程中,我们需要注意以下几点:

  1. 权限配置:在鸿蒙应用的配置文件中添加必要的权限,如通知权限等
  2. 资源适配:确保应用图标、启动画面等资源符合鸿蒙平台的规范
  3. API兼容性:注意使用跨平台兼容的API,避免使用平台特定的功能

构建流程

在鸿蒙平台构建应用的流程如下:

  1. 确保已安装鸿蒙开发工具和Flutter的鸿蒙插件

  2. 在项目根目录执行以下命令:

    bash 复制代码
    flutter build ohos
  3. 构建成功后,会在build/ohos/hap目录生成鸿蒙应用安装包(.hap文件)

总结

本文详细介绍了使用Flutter框架开发水电费缴费提醒APP的完整流程,包括项目架构设计、数据模型实现、核心功能开发和鸿蒙平台适配。通过Flutter的跨平台特性,我们成功实现了一款可以在多个平台运行的水电费缴费提醒APP,特别是在鸿蒙操作系统上的良好适配。

项目亮点

  1. 模块化设计:采用清晰的三层架构,代码结构清晰,易于维护
  2. 用户友好界面:设计了直观、美观的用户界面,提供良好的用户体验
  3. 功能完善:实现了账单管理、缴费提醒、统计分析等核心功能
  4. 跨平台兼容:基于Flutter框架,可在Android、iOS、Web和鸿蒙等平台运行
  5. 数据安全:使用Flutter Secure Storage进行本地数据存储,保障数据安全

未来展望

  1. 云同步功能:添加云同步功能,实现多设备数据共享
  2. 在线缴费:集成在线支付功能,支持直接在APP内完成缴费
  3. 智能预测:基于历史数据,预测未来的水电费使用情况
  4. 多语言支持:添加多语言支持,扩大应用的适用范围

通过本项目的开发,我们展示了Flutter框架在跨平台开发中的强大能力,特别是在鸿蒙操作系统上的应用。相信随着Flutter和鸿蒙生态的不断发展,跨平台开发将会变得更加高效和便捷。

📚 参考资料

  1. Flutter官方文档
  2. flutter_tts库文档
  3. 鸿蒙开发者文档

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

相关推荐
2401_892000522 小时前
Flutter for OpenHarmony 猫咪管家App实战 - 添加支出实现
前端·javascript·flutter
2601_949613024 小时前
flutter_for_openharmony家庭药箱管理app实战+药品分类实现
大数据·数据库·flutter
摘星编程4 小时前
React Native鸿蒙版:StackNavigation页面返回拦截
react native·react.js·harmonyos
BlackWolfSky4 小时前
鸿蒙中级课程笔记4—应用程序框架进阶1—Stage模型应用组成结构、UIAbility启动模式、启动应用内UIAbility
笔记·华为·harmonyos
Miguo94well5 小时前
Flutter框架跨平台鸿蒙开发——植物养殖APP的开发流程
flutter·华为·harmonyos·鸿蒙
九 龙5 小时前
Flutter框架跨平台鸿蒙开发——电影拍摄知识APP的开发流程
flutter·华为·harmonyos·鸿蒙
星辰徐哥5 小时前
鸿蒙APP开发从入门到精通:ArkUI组件库详解与常用组件实战
华为·app·harmonyos·组件·arkui·组件库
九 龙5 小时前
Flutter框架跨平台鸿蒙开发——如何养花APP的开发流程
flutter·华为·harmonyos·鸿蒙
雨季6665 小时前
构建 OpenHarmony 随机颜色生成器:用纯数学生成视觉灵感
开发语言·javascript·flutter·ui·ecmascript·dart