|
@@ -4,6 +4,7 @@ import 'package:flutter_clock/model/timer_settings.dart';
|
|
|
import 'package:flutter_clock/pages/timer/timer_settings_page.dart';
|
|
|
import 'package:flutter_clock/utils/audio_manager.dart';
|
|
|
import 'package:flutter_clock/utils/screen_manager.dart';
|
|
|
+
|
|
|
/// Description: 倒计时页面
|
|
|
/// Time : 04/06/2025 Sunday
|
|
|
/// Author : liuyuqi.gov@msn.cn
|
|
@@ -12,6 +13,8 @@ class TimerPage extends StatefulWidget {
|
|
|
_TimerPageState createState() => _TimerPageState();
|
|
|
}
|
|
|
|
|
|
+enum TimerState { prepare, running, pause, finish }
|
|
|
+
|
|
|
class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
// Timer duration values
|
|
|
int _hours = 0;
|
|
@@ -20,8 +23,7 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
|
|
|
// For timer controller
|
|
|
Timer? _timer;
|
|
|
- bool _isRunning = false;
|
|
|
- bool _isCompleted = false;
|
|
|
+ TimerState timerState = TimerState.prepare;
|
|
|
int _remainingSeconds = 0;
|
|
|
|
|
|
// Settings
|
|
@@ -60,7 +62,8 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
|
|
|
@override
|
|
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
|
|
- if (state == AppLifecycleState.resumed && _isRunning) {
|
|
|
+ if (state == AppLifecycleState.resumed &&
|
|
|
+ timerState == TimerState.running) {
|
|
|
_syncTimer();
|
|
|
}
|
|
|
}
|
|
@@ -72,22 +75,27 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- void _startTimer() {
|
|
|
- final totalSeconds = _hours * 3600 + _minutes * 60 + _seconds;
|
|
|
- if (totalSeconds <= 0) return;
|
|
|
-
|
|
|
- setState(() {
|
|
|
- _isRunning = true;
|
|
|
- _isCompleted = false;
|
|
|
- _remainingSeconds = totalSeconds;
|
|
|
- });
|
|
|
+ /// resumed 暂停, 恢复
|
|
|
+ void _startTimer(bool resumed) {
|
|
|
+ if (resumed) {
|
|
|
+ setState(() {
|
|
|
+ timerState = TimerState.running;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ final totalSeconds = _hours * 3600 + _minutes * 60 + _seconds;
|
|
|
+ if (totalSeconds <= 0) return;
|
|
|
|
|
|
+ setState(() {
|
|
|
+ timerState = TimerState.running;
|
|
|
+ _remainingSeconds = totalSeconds;
|
|
|
+ });
|
|
|
+ }
|
|
|
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
|
|
|
setState(() {
|
|
|
if (_remainingSeconds > 0) {
|
|
|
_remainingSeconds--;
|
|
|
} else {
|
|
|
- _isCompleted = true;
|
|
|
+ timerState = TimerState.finish;
|
|
|
_timerCompleted();
|
|
|
}
|
|
|
});
|
|
@@ -97,7 +105,7 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
void _pauseTimer() {
|
|
|
_timer?.cancel();
|
|
|
setState(() {
|
|
|
- _isRunning = false;
|
|
|
+ timerState = TimerState.pause;
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -106,18 +114,22 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
_audioManager.stopSound();
|
|
|
_audioManager.stopVibration();
|
|
|
setState(() {
|
|
|
- _isRunning = false;
|
|
|
- _isCompleted = false;
|
|
|
+ timerState = TimerState.prepare;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
void _syncTimer() {
|
|
|
// Recalculate elapsed time if app was in background
|
|
|
+ // final elapsedSeconds = _hours * 3600 + _minutes * 60 + _seconds -
|
|
|
+ // _remainingSeconds;
|
|
|
+ // setState(() {
|
|
|
+ // _remainingSeconds = elapsedSeconds;
|
|
|
+ // });
|
|
|
}
|
|
|
|
|
|
Future<void> _timerCompleted() async {
|
|
|
_timer?.cancel();
|
|
|
- _isRunning = false;
|
|
|
+ timerState = TimerState.finish;
|
|
|
|
|
|
// Play sound and vibrate
|
|
|
if (_settings.vibrate) {
|
|
@@ -130,8 +142,8 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
Future.delayed(Duration(seconds: 5), () {
|
|
|
_audioManager.stopSound();
|
|
|
_audioManager.stopVibration();
|
|
|
- _startTimer();
|
|
|
});
|
|
|
+ _startTimer(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -147,8 +159,8 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
if (!_settingsLoaded) {
|
|
|
return Center(child: CircularProgressIndicator());
|
|
|
}
|
|
|
-
|
|
|
- if (_isRunning || _isCompleted) {
|
|
|
+
|
|
|
+ if (timerState != TimerState.prepare) {
|
|
|
return _buildCountdownView();
|
|
|
} else {
|
|
|
return _buildTimerSetupView();
|
|
@@ -221,7 +233,7 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
_buildCircleButton(
|
|
|
Icons.play_arrow,
|
|
|
Colors.blue,
|
|
|
- () => _startTimer(),
|
|
|
+ () => _startTimer(false),
|
|
|
),
|
|
|
_buildCircleButton(
|
|
|
Icons.settings,
|
|
@@ -267,7 +279,7 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
shape: BoxShape.circle,
|
|
|
border: Border.all(
|
|
|
color: Colors.blue.withOpacity(0.3),
|
|
|
- width: 1,
|
|
|
+ width: 3,
|
|
|
),
|
|
|
),
|
|
|
child: Stack(
|
|
@@ -278,11 +290,11 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
width: 300,
|
|
|
height: 300,
|
|
|
child: CircularProgressIndicator(
|
|
|
- value: _isCompleted
|
|
|
+ value: timerState == TimerState.finish
|
|
|
? 1
|
|
|
: _remainingSeconds /
|
|
|
(_hours * 3600 + _minutes * 60 + _seconds),
|
|
|
- strokeWidth: 1,
|
|
|
+ strokeWidth: 5,
|
|
|
backgroundColor: Colors.grey.withOpacity(0.1),
|
|
|
color: Colors.blue,
|
|
|
),
|
|
@@ -307,8 +319,8 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
Positioned(
|
|
|
bottom: 0,
|
|
|
child: Container(
|
|
|
- width: 10,
|
|
|
- height: 10,
|
|
|
+ width: 20,
|
|
|
+ height: 20,
|
|
|
decoration: BoxDecoration(
|
|
|
color: Colors.blue,
|
|
|
shape: BoxShape.circle,
|
|
@@ -338,7 +350,7 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
isActive: ScreenManager.isWakeLockEnabled,
|
|
|
),
|
|
|
// 暂停/开始
|
|
|
- _isRunning
|
|
|
+ timerState == TimerState.running
|
|
|
? _buildCircleButton(
|
|
|
Icons.pause,
|
|
|
Colors.blue,
|
|
@@ -347,7 +359,7 @@ class _TimerPageState extends State<TimerPage> with WidgetsBindingObserver {
|
|
|
: _buildCircleButton(
|
|
|
Icons.play_arrow,
|
|
|
Colors.blue,
|
|
|
- () => _startTimer(),
|
|
|
+ () => _startTimer(true),
|
|
|
),
|
|
|
// 重置
|
|
|
_buildCircleButton(
|