Implement everything
Thanks claude!"
This commit is contained in:
177
lib/main.dart
177
lib/main.dart
@@ -1,4 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
@@ -27,12 +29,179 @@ class MyHomePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
class _MyHomePageState extends State<MyHomePage> {
|
||||||
|
Timer? _timer;
|
||||||
|
DateTime? _startTime;
|
||||||
|
DateTime? _pauseTime;
|
||||||
|
Duration _elapsedTime = Duration.zero;
|
||||||
|
bool _isRunning = false;
|
||||||
|
List<String> _laps = [];
|
||||||
|
final FocusNode _focusNode = FocusNode();
|
||||||
|
|
||||||
|
void _startTimer() {
|
||||||
|
final now = DateTime.now();
|
||||||
|
if (_pauseTime != null) {
|
||||||
|
// Resuming from pause
|
||||||
|
final pausedDuration = now.difference(_pauseTime!);
|
||||||
|
_startTime = _startTime?.add(pausedDuration);
|
||||||
|
_pauseTime = null;
|
||||||
|
} else {
|
||||||
|
// Starting fresh
|
||||||
|
_startTime = now;
|
||||||
|
_elapsedTime = Duration.zero;
|
||||||
|
_laps = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_timer = Timer.periodic(const Duration(milliseconds: 100), (timer) {
|
||||||
|
setState(() {
|
||||||
|
_elapsedTime = DateTime.now().difference(_startTime!);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isRunning = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pauseTimer() {
|
||||||
|
_timer?.cancel();
|
||||||
|
_pauseTime = DateTime.now();
|
||||||
|
setState(() {
|
||||||
|
_isRunning = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stopTimer() {
|
||||||
|
_timer?.cancel();
|
||||||
|
setState(() {
|
||||||
|
_isRunning = false;
|
||||||
|
_startTime = null;
|
||||||
|
_pauseTime = null;
|
||||||
|
_elapsedTime = Duration.zero;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _recordLap() {
|
||||||
|
if (_isRunning) {
|
||||||
|
final now = DateTime.now();
|
||||||
|
setState(() {
|
||||||
|
_laps.add(now.toIso8601String());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDuration(Duration duration) {
|
||||||
|
// Format the duration as ISO 8601 duration format: PT[hours]H[minutes]M[seconds]S
|
||||||
|
final hours = duration.inHours;
|
||||||
|
final minutes = duration.inMinutes.remainder(60);
|
||||||
|
final seconds = duration.inSeconds.remainder(60);
|
||||||
|
final milliseconds = duration.inMilliseconds.remainder(1000);
|
||||||
|
|
||||||
|
return 'PT${hours}H${minutes}M${seconds}.${milliseconds}S';
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleKeyEvent(RawKeyEvent event) {
|
||||||
|
if (event is RawKeyDownEvent) {
|
||||||
|
if (event.logicalKey == LogicalKeyboardKey.space) {
|
||||||
|
// Space: Start if paused, Lap if running
|
||||||
|
if (_isRunning) {
|
||||||
|
_recordLap();
|
||||||
|
} else {
|
||||||
|
_startTimer();
|
||||||
|
}
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||||||
|
// Enter: Stop if paused, Pause if running
|
||||||
|
if (_isRunning) {
|
||||||
|
_pauseTimer();
|
||||||
|
} else {
|
||||||
|
_stopTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_focusNode.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
_focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return RawKeyboardListener(
|
||||||
appBar: AppBar(
|
focusNode: _focusNode,
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
onKey: _handleKeyEvent,
|
||||||
title: Text(widget.title),
|
autofocus: true,
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
|
title: Text(widget.title),
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Time Elapsed:',
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
_formatDuration(_elapsedTime),
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _isRunning ? _pauseTimer : _startTimer,
|
||||||
|
child: Text(_isRunning ? 'Pause' : 'Start'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _stopTimer,
|
||||||
|
child: const Text('Stop'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _recordLap,
|
||||||
|
child: const Text('Lap'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'Keyboard: SPACE for Start/Lap, ENTER for Pause/Stop',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (_laps.isNotEmpty) ...[
|
||||||
|
Text(
|
||||||
|
'Laps:',
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
itemCount: _laps.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text('Lap ${index + 1}: ${_laps[index]}'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user