import 'package:flutter/material.dart'; import 'package:sensors_plus/sensors_plus.dart'; import 'package:vibration/vibration.dart'; import 'dart:async'; import '../models/record.dart'; import './record_page.dart'; import '../widgets/record_list.dart'; import '../services/database_service.dart'; import '../widgets/weekly_spending_chart.dart'; import '../widgets/monthly_summary_card.dart'; class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @override State createState() => _HomePageState(); } class _HomePageState extends State { StreamSubscription? _accelerometerSubscription; DateTime? _lastShakeTime; List _recentXValues = []; // 存储原始x轴加速度值(不取绝对值) bool _isNavigating = false; final _dbService = DatabaseService(); List records = []; DateTime _selectedMonth = DateTime.now(); // 添加选中月份状态 bool _isAmountVisible = true; // 添加可视状态 // 定义摇晃检测的常量 static const double _shakeThreshold = 8.0; // 单次摇晃的加速度阈值 static const double _directionThreshold = 2.0; // 方向改变的最小加速 static const int _sampleSize = 3; // 采样数量 static const Duration _shakeWindow = Duration(milliseconds: 800); static const Duration _cooldown = Duration(milliseconds: 50); /// 检测有效的摇晃 bool _isValidShake() { if (_recentXValues.length < _sampleSize) return false; // 检查是否有足够大的加速度 bool hasStrongShake = _recentXValues.any((x) => x.abs() > _shakeThreshold); if (!hasStrongShake) return false; // 检查方向变化 bool hasDirectionChange = false; for (int i = 1; i < _recentXValues.length; i++) { // 如果相邻两个值的符号相反,且都超过方向阈值,说明发生了方向改变 if (_recentXValues[i].abs() > _directionThreshold && _recentXValues[i - 1].abs() > _directionThreshold && _recentXValues[i].sign != _recentXValues[i - 1].sign) { hasDirectionChange = true; break; } } return hasDirectionChange; } @override void initState() { super.initState(); _initShakeDetection(); _loadRecords(); } /// 重置摇晃检测状态 void _resetShakeDetection() { _lastShakeTime = null; _recentXValues.clear(); _isNavigating = false; // 重新初始化传感器监听 _accelerometerSubscription?.cancel(); _initShakeDetection(); } /// 加载记录 Future _loadRecords() async { // 计算选中月份的起始和结束日期 final startDate = DateTime(_selectedMonth.year, _selectedMonth.month, 1); final endDate = DateTime(_selectedMonth.year, _selectedMonth.month + 1, 0); final loadedRecords = await _dbService.getRecordsByDateRange( startDate, endDate, ); setState(() { records = loadedRecords; records.sort((a, b) => b.createTime.compareTo(a.createTime)); }); } /// 初始化摇晃检测 void _initShakeDetection() { _accelerometerSubscription = accelerometerEvents.listen( (event) { if (_isNavigating) return; // 如果正在导航,忽略摇晃检测 final now = DateTime.now(); // 添加新的x轴值(保留正负号) _recentXValues.add(event.x); if (_recentXValues.length > _sampleSize) { _recentXValues.removeAt(0); } // 检查是否处于冷却期 if (_lastShakeTime != null && now.difference(_lastShakeTime!) < _cooldown) { return; } // 检测有效的摇晃 if (_isValidShake()) { if (_lastShakeTime == null) { _lastShakeTime = now; _recentXValues.clear(); } else { final timeDiff = now.difference(_lastShakeTime!); if (timeDiff < _shakeWindow) { _navigateToRecordPageWithVibration(); _lastShakeTime = null; _recentXValues.clear(); } else { _lastShakeTime = now; _recentXValues.clear(); } } } }, onError: (error) { debugPrint('Accelerometer error: $error'); _resetShakeDetection(); // 发生错误时重置检测 }, cancelOnError: false, // 错误时不取消订阅,而是重置 ); } /// 带震动的记账页面导航 void _navigateToRecordPageWithVibration() async { if (_isNavigating) return; _isNavigating = true; // 添加震动反馈 if (await Vibration.hasVibrator() ?? false) { Vibration.vibrate(duration: 200); } _navigateToRecordPage(); } /// 导航到记账页面 void _navigateToRecordPage([Record? record]) { _isNavigating = true; // 设置导航标志 Navigator.push( context, MaterialPageRoute( builder: (context) => RecordPage( record: record, onSave: (newRecord) async { if (record != null) { await _dbService.updateRecord(newRecord); } else { await _dbService.insertRecord(newRecord); } await _loadRecords(); }, ), maintainState: false, ), ).then((_) { // 返回时重置检测状态 _resetShakeDetection(); }); } /// 删除记录 void _deleteRecord(String recordId) async { await _dbService.deleteRecord(recordId); await _loadRecords(); } // 添加月份选择器组件 Widget _buildMonthSelector() { return GestureDetector( onTap: () => _showMonthPicker(), child: Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Theme.of(context).primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8.0), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( '${_selectedMonth.year}年${_selectedMonth.month}月', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), const Icon(Icons.arrow_drop_down), ], ), ), ); } // 显示月份选择器 Future _showMonthPicker() async { final DateTime? picked = await showDatePicker( context: context, initialDate: _selectedMonth, firstDate: DateTime(2000), lastDate: DateTime(2100), initialDatePickerMode: DatePickerMode.year, ); if (picked != null) { setState(() { _selectedMonth = DateTime(picked.year, picked.month); }); _loadRecords(); // 重新加载数据 } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('记账本'), ), body: Column( children: [ Padding( padding: const EdgeInsets.all(16.0), child: _buildMonthSelector(), ), MonthlySummaryCard( records: records, isVisible: _isAmountVisible, onAddRecord: () => _navigateToRecordPage(), onVisibilityChanged: (value) { setState(() { _isAmountVisible = value; }); }, ), WeeklySpendingChart(records: records), Expanded( child: RecordList( records: records, onRecordTap: _navigateToRecordPage, onRecordDelete: _deleteRecord, ), ), ], ), ); } @override void dispose() { _accelerometerSubscription?.cancel(); super.dispose(); } }