import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_clock/model/timer_settings.dart'; import 'package:flutter_clock/pages/timer_settings_page.dart'; import 'package:flutter_clock/utils/audio_manager.dart'; import 'package:flutter_clock/utils/screen_manager.dart'; class TimerPage extends StatefulWidget { @override _TimerPageState createState() => _TimerPageState(); } class _TimerPageState extends State with WidgetsBindingObserver { // Timer duration values int _hours = 0; int _minutes = 10; int _seconds = 0; // For timer controller Timer? _timer; bool _isRunning = false; bool _isCompleted = false; int _remainingSeconds = 0; // Settings late TimerSettings _settings; bool _settingsLoaded = false; final AudioManager _audioManager = AudioManager(); // Wheel controllers final FixedExtentScrollController _hoursController = FixedExtentScrollController(initialItem: 0); final FixedExtentScrollController _minutesController = FixedExtentScrollController(initialItem: 10); final FixedExtentScrollController _secondsController = FixedExtentScrollController(initialItem: 0); @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _loadSettings(); } @override void dispose() { _timer?.cancel(); _audioManager.dispose(); if (ScreenManager.isWakeLockEnabled) { ScreenManager.disableWakeLock(); } WidgetsBinding.instance.removeObserver(this); _hoursController.dispose(); _minutesController.dispose(); _secondsController.dispose(); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed && _isRunning) { _syncTimer(); } } Future _loadSettings() async { _settings = await TimerSettings.loadSettings(); setState(() { _settingsLoaded = true; }); } void _startTimer() { final totalSeconds = _hours * 3600 + _minutes * 60 + _seconds; if (totalSeconds <= 0) return; setState(() { _isRunning = true; _isCompleted = false; _remainingSeconds = totalSeconds; }); _timer = Timer.periodic(Duration(seconds: 1), (timer) { setState(() { if (_remainingSeconds > 0) { _remainingSeconds--; } else { _isCompleted = true; _timerCompleted(); } }); }); } void _pauseTimer() { _timer?.cancel(); setState(() { _isRunning = false; }); } void _resetTimer() { _timer?.cancel(); _audioManager.stopSound(); _audioManager.stopVibration(); setState(() { _isRunning = false; _isCompleted = false; }); } void _syncTimer() { // Recalculate elapsed time if app was in background } Future _timerCompleted() async { _timer?.cancel(); _isRunning = false; // Play sound and vibrate if (_settings.vibrate) { _audioManager.triggerVibration(); } _audioManager.playSound(_settings.sound, _settings.volume, _settings.loop); // If loop is enabled, restart the timer if (_settings.loop) { Future.delayed(Duration(seconds: 5), () { _audioManager.stopSound(); _audioManager.stopVibration(); _startTimer(); }); } } String _formatTime(int seconds) { final hours = seconds ~/ 3600; final minutes = (seconds % 3600) ~/ 60; final secs = seconds % 60; return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${secs.toString().padLeft(2, '0')}'; } @override Widget build(BuildContext context) { if (!_settingsLoaded) { return Center(child: CircularProgressIndicator()); } if (_isRunning || _isCompleted) { return _buildCountdownView(); } else { return _buildTimerSetupView(); } } Widget _buildTimerSetupView() { return Scaffold( backgroundColor: Colors.white, body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: Row( children: [ Expanded( child: _buildTimerWheel( _hoursController, List.generate(24, (index) => index), (value) { setState(() { _hours = value; }); }, 'H', ), ), Expanded( child: _buildTimerWheel( _minutesController, List.generate(60, (index) => index), (value) { setState(() { _minutes = value; }); }, 'M', ), ), Expanded( child: _buildTimerWheel( _secondsController, List.generate(60, (index) => index), (value) { setState(() { _seconds = value; }); }, 'S', ), ), ], ), ), SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildCircleButton( Icons.phone_android, Colors.grey[600]!, () { ScreenManager.toggleWakeLock(); setState(() {}); }, isActive: ScreenManager.isWakeLockEnabled, ), _buildCircleButton( Icons.play_arrow, Colors.blue, () => _startTimer(), ), _buildCircleButton( Icons.settings, Colors.grey[600]!, () async { final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => TimerSettingsPage(settings: _settings)), ); if (result != null) { setState(() { _settings = result; }); } }, ), ], ), ), ], ), ); } Widget _buildCountdownView() { final totalMinutes = _remainingSeconds ~/ 60; return Scaffold( backgroundColor: Colors.white, body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 300, height: 300, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.blue.withOpacity(0.3), width: 1, ), ), child: Stack( alignment: Alignment.center, children: [ // Timer progress SizedBox( width: 300, height: 300, child: CircularProgressIndicator( value: _isCompleted ? 1 : _remainingSeconds / (_hours * 3600 + _minutes * 60 + _seconds), strokeWidth: 1, backgroundColor: Colors.grey.withOpacity(0.1), color: Colors.blue, ), ), // Time display Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( _formatTime(_remainingSeconds), style: TextStyle( fontSize: 40, fontWeight: FontWeight.bold), ), Text( 'Total ${totalMinutes} minutes', style: TextStyle(fontSize: 16, color: Colors.grey), ), ], ), // Indicator dot Positioned( bottom: 0, child: Container( width: 10, height: 10, decoration: BoxDecoration( color: Colors.blue, shape: BoxShape.circle, ), ), ), ], ), ), ], ), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildCircleButton( Icons.phone_android, Colors.grey[600]!, () { ScreenManager.toggleWakeLock(); setState(() {}); }, isActive: ScreenManager.isWakeLockEnabled, ), _isRunning ? _buildCircleButton( Icons.pause, Colors.blue, () => _pauseTimer(), ) : _buildCircleButton( Icons.play_arrow, Colors.blue, () => _startTimer(), ), _buildCircleButton( Icons.stop, Colors.red, () => _resetTimer(), ), ], ), ), ], ), ); } Widget _buildCircleButton(IconData icon, Color color, VoidCallback onPressed, {bool isActive = false}) { return Container( width: 70, height: 70, decoration: BoxDecoration( shape: BoxShape.circle, color: isActive ? color : Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 8, offset: Offset(0, 2), ), ], ), child: IconButton( icon: Icon(icon, size: 30), color: isActive ? Colors.white : color, onPressed: onPressed, ), ); } Widget _buildTimerWheel( FixedExtentScrollController controller, List items, ValueChanged onChanged, String unit, ) { return Column( children: [ Expanded( child: Container( decoration: BoxDecoration( border: Border( top: BorderSide(color: Colors.grey.withOpacity(0.3), width: 1), bottom: BorderSide(color: Colors.grey.withOpacity(0.3), width: 1), ), ), child: Stack( children: [ // Center highlight Positioned.fill( child: Center( child: Container( height: 50, decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), ), ), ), ListWheelScrollView( controller: controller, physics: FixedExtentScrollPhysics(), diameterRatio: 1.5, itemExtent: 50, children: items.map((value) { return Center( child: Text( value.toString().padLeft(2, '0'), style: TextStyle( fontSize: 30, color: Colors.black, fontWeight: FontWeight.w500, ), ), ); }).toList(), onSelectedItemChanged: onChanged, ), ], ), ), ), SizedBox(height: 8), Text( unit, style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), ], ); } }