sound works and intervall needs improfmends
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 544 B |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 721 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
@ -24,7 +24,7 @@ class MyHomePageState extends State<MyHomePage> {
|
|||
static const List<Widget> _widgetOptions = <Widget>[
|
||||
MoodPage(),
|
||||
SleepPage(),
|
||||
StopWatchTimerPage(),
|
||||
IntervalTimerPage(),
|
||||
RelapsePage(),
|
||||
SettingsPage(),
|
||||
];
|
||||
|
|
|
@ -1,136 +1,160 @@
|
|||
import 'dart:async';
|
||||
import 'package:audioplayers/audioplayers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:smoke_cess_app/service/settings_service.dart';
|
||||
import 'package:smoke_cess_app/widgets/timer_button.dart';
|
||||
|
||||
class StopWatchTimerPage extends StatefulWidget {
|
||||
const StopWatchTimerPage({super.key});
|
||||
class IntervalTimerPage extends StatefulWidget {
|
||||
const IntervalTimerPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
StopWatchTimerPageState createState() => StopWatchTimerPageState();
|
||||
_IntervalTimerPageState createState() => _IntervalTimerPageState();
|
||||
}
|
||||
|
||||
class StopWatchTimerPageState extends State<StopWatchTimerPage> {
|
||||
SettingsService settings = SettingsService();
|
||||
Duration duration = const Duration(minutes: 1);
|
||||
Timer? timer;
|
||||
|
||||
bool countDown = true;
|
||||
class _IntervalTimerPageState extends State<IntervalTimerPage> {
|
||||
final Duration _warmupDuration = const Duration(seconds: 5);
|
||||
final Duration _cooldownDuration = const Duration(seconds: 5);
|
||||
final Duration _highIntensityDuration = const Duration(seconds: 4);
|
||||
final Duration _lowIntensityDuration = const Duration(seconds: 3);
|
||||
late Duration _totalDuration = const Duration(seconds: 35);
|
||||
final int _numHighIntensityBlocks = 4;
|
||||
final int _numLowIntensityBlocks = 3;
|
||||
Timer? _timer;
|
||||
int _currentBlock = 0;
|
||||
Duration _currentDuration = const Duration();
|
||||
bool _isPaused = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
setDurationWithSetting();
|
||||
_currentDuration = _warmupDuration;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void setDurationWithSetting() {
|
||||
settings.getIntSetting('workout_duration_minutes').then((workoutMinutes) =>
|
||||
{setState(() => duration = Duration(minutes: workoutMinutes ?? 10))});
|
||||
@override
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (countDown) {
|
||||
setDurationWithSetting();
|
||||
} else {
|
||||
setState(() => duration = const Duration());
|
||||
}
|
||||
void _startTimer() {
|
||||
_isPaused = false;
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (_) => _tick());
|
||||
}
|
||||
|
||||
void startTimer() {
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
|
||||
void _pauseTimer() {
|
||||
_isPaused = true;
|
||||
_timer?.cancel();
|
||||
}
|
||||
|
||||
void addTime() {
|
||||
final addSeconds = countDown ? -1 : 1;
|
||||
void _resetTimer() {
|
||||
_isPaused = true;
|
||||
_timer?.cancel();
|
||||
_currentBlock = 0;
|
||||
_currentDuration = _warmupDuration;
|
||||
_totalDuration = const Duration(minutes: 35);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _playSoundEffect() async {
|
||||
final player = AudioPlayer();
|
||||
await player.play(UrlSource('assets/beep.mp3'));
|
||||
}
|
||||
|
||||
void _tick() {
|
||||
setState(() {
|
||||
final seconds = duration.inSeconds + addSeconds;
|
||||
if (seconds < 0) {
|
||||
timer?.cancel();
|
||||
_currentDuration = Duration(
|
||||
seconds: _currentDuration.inSeconds - 1,
|
||||
);
|
||||
_totalDuration = Duration(
|
||||
seconds: _totalDuration.inSeconds - 1,
|
||||
);
|
||||
if (_currentDuration.inSeconds == 0) {
|
||||
_playSoundEffect();
|
||||
}
|
||||
if (_currentDuration.inSeconds < 0) {
|
||||
if (_currentBlock == 0) {
|
||||
// Start high intensity blocks.
|
||||
_currentBlock++;
|
||||
_currentDuration = _highIntensityDuration;
|
||||
} else if (_currentBlock <= _numHighIntensityBlocks) {
|
||||
// Start low intensity blocks.
|
||||
_currentBlock++;
|
||||
_currentDuration = _lowIntensityDuration;
|
||||
} else if (_currentBlock <=
|
||||
_numHighIntensityBlocks + _numLowIntensityBlocks) {
|
||||
// Start high intensity blocks again.
|
||||
_currentBlock++;
|
||||
_currentDuration = _highIntensityDuration;
|
||||
} else {
|
||||
duration = Duration(seconds: seconds);
|
||||
// End workout.
|
||||
_currentDuration = _cooldownDuration;
|
||||
_pauseTimer();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void stopTimer({bool resets = true}) {
|
||||
if (resets) {
|
||||
reset();
|
||||
}
|
||||
setState(() => timer?.cancel());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
buildTime(),
|
||||
const SizedBox(
|
||||
height: 80,
|
||||
),
|
||||
buildButtons()
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Widget buildTime() {
|
||||
String _formatDuration(Duration duration) {
|
||||
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
||||
final minutes = twoDigits(duration.inMinutes.remainder(60));
|
||||
final seconds = twoDigits(duration.inSeconds.remainder(60));
|
||||
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
buildTimeCard(time: minutes, header: 'MINUTEN'),
|
||||
const SizedBox(
|
||||
width: 8,
|
||||
),
|
||||
buildTimeCard(time: seconds, header: 'SEKUNDEN'),
|
||||
]);
|
||||
return '$minutes:$seconds';
|
||||
}
|
||||
|
||||
Widget buildTimeCard({required String time, required String header}) =>
|
||||
Column(
|
||||
String _formatTotalDuration(Duration duration) {
|
||||
final minutes = duration.inMinutes;
|
||||
final seconds = duration.inSeconds.remainder(60);
|
||||
return _formatDuration(Duration(minutes: minutes, seconds: seconds));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Interval Timer'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white, borderRadius: BorderRadius.circular(20)),
|
||||
child: Text(
|
||||
time,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
fontSize: 50),
|
||||
Text(
|
||||
_currentBlock == 0
|
||||
? 'Warm-up'
|
||||
: _currentBlock <= _numHighIntensityBlocks
|
||||
? 'High Intensity'
|
||||
: 'Low Intensity',
|
||||
style: const TextStyle(fontSize: 32.0),
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
Text(
|
||||
_formatDuration(_currentDuration),
|
||||
style: const TextStyle(fontSize: 80.0),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
const SizedBox(height: 32.0),
|
||||
Text(
|
||||
'Total: ${_formatTotalDuration(_totalDuration)}',
|
||||
style: const TextStyle(fontSize: 24.0),
|
||||
),
|
||||
Text(header, style: const TextStyle(color: Colors.black45)),
|
||||
const SizedBox(height: 32.0),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(_isPaused
|
||||
? Icons.play_arrow_rounded
|
||||
: Icons.stop_rounded),
|
||||
iconSize: 48.0,
|
||||
onPressed: () {
|
||||
if (_isPaused) {
|
||||
_startTimer();
|
||||
} else {
|
||||
_pauseTimer();
|
||||
_resetTimer();
|
||||
}
|
||||
},
|
||||
),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
|
||||
Widget buildButtons() {
|
||||
final isRunning = timer == null ? false : timer!.isActive;
|
||||
final isCompleted = duration.inSeconds == 0;
|
||||
return isRunning || isCompleted
|
||||
? TimerButton(
|
||||
onClicked: stopTimer,
|
||||
icon: const Icon(
|
||||
Icons.stop,
|
||||
size: 50,
|
||||
color: Colors.white,
|
||||
),
|
||||
color: Colors.red)
|
||||
: TimerButton(
|
||||
onClicked: startTimer,
|
||||
icon: const Icon(
|
||||
Icons.play_arrow,
|
||||
size: 50,
|
||||
color: Colors.white,
|
||||
),
|
||||
color: Colors.green);
|
||||
],
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
class SettingsService {
|
||||
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
||||
|
||||
SettingsService() {
|
||||
setIntSetting('workout_duration_minutes', 5);
|
||||
}
|
||||
|
||||
void setStringSetting(String settingKey, String settingValue) =>
|
||||
_prefs.then((pref) => pref.setString(settingKey, settingValue));
|
||||
|
||||
|
|
140
pubspec.lock
|
@ -8,6 +8,62 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.9.0"
|
||||
audio_session:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audio_session
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.13"
|
||||
audioplayers:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: audioplayers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
audioplayers_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
audioplayers_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_darwin
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
audioplayers_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_linux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
audioplayers_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
audioplayers_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
audioplayers_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: audioplayers_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -36,6 +92,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -86,6 +149,20 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.13.5"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -93,6 +170,27 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.4"
|
||||
just_audio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.31"
|
||||
just_audio_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
just_audio_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: just_audio_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.7"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -128,6 +226,27 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.2"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.12"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -170,6 +289,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.4"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rxdart
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.27.7"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -266,6 +392,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.12"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -3,7 +3,7 @@ description: A new Flutter project.
|
|||
|
||||
# The following line prevents the package from being accidentally published to
|
||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
|
@ -20,7 +20,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=2.18.2 <3.0.0'
|
||||
sdk: ">=2.18.2 <3.0.0"
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
|
@ -36,6 +36,8 @@ dependencies:
|
|||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
shared_preferences: ^2.0.17
|
||||
audioplayers: ^3.0.1
|
||||
just_audio: ^0.9.31
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -59,7 +61,8 @@ flutter:
|
|||
uses-material-design: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
assets:
|
||||
- beep.mp3
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
|
|