swing_account/lib/pages/home_page.dart

196 lines
5.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 '../data/mock_data.dart'; // 暂时使用模拟数据
import '../services/database_service.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
StreamSubscription? _accelerometerSubscription;
DateTime? _lastShakeTime;
List<double> _recentXValues = []; // 存储原始x轴加速度值不取绝对值
bool _isNavigating = false;
final _dbService = DatabaseService();
List<Record> records = [];
// 定义摇晃检测的常量
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<void> _loadRecords() async {
final loadedRecords = await _dbService.getAllRecords();
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();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('记账本'),
),
body: RecordList(
records: records,
onRecordTap: _navigateToRecordPage,
onRecordDelete: _deleteRecord,
),
floatingActionButton: FloatingActionButton(
onPressed: () => _navigateToRecordPage(),
child: const Icon(Icons.add),
),
);
}
@override
void dispose() {
_accelerometerSubscription?.cancel();
super.dispose();
}
}