196 lines
5.6 KiB
Dart
196 lines
5.6 KiB
Dart
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();
|
||
}
|
||
} |