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>[
|
static const List<Widget> _widgetOptions = <Widget>[
|
||||||
MoodPage(),
|
MoodPage(),
|
||||||
SleepPage(),
|
SleepPage(),
|
||||||
StopWatchTimerPage(),
|
IntervalTimerPage(),
|
||||||
RelapsePage(),
|
RelapsePage(),
|
||||||
SettingsPage(),
|
SettingsPage(),
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,136 +1,160 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
import 'package:flutter/material.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 {
|
class IntervalTimerPage extends StatefulWidget {
|
||||||
const StopWatchTimerPage({super.key});
|
const IntervalTimerPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
StopWatchTimerPageState createState() => StopWatchTimerPageState();
|
_IntervalTimerPageState createState() => _IntervalTimerPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class StopWatchTimerPageState extends State<StopWatchTimerPage> {
|
class _IntervalTimerPageState extends State<IntervalTimerPage> {
|
||||||
SettingsService settings = SettingsService();
|
final Duration _warmupDuration = const Duration(seconds: 5);
|
||||||
Duration duration = const Duration(minutes: 1);
|
final Duration _cooldownDuration = const Duration(seconds: 5);
|
||||||
Timer? timer;
|
final Duration _highIntensityDuration = const Duration(seconds: 4);
|
||||||
|
final Duration _lowIntensityDuration = const Duration(seconds: 3);
|
||||||
bool countDown = true;
|
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
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
setDurationWithSetting();
|
_currentDuration = _warmupDuration;
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDurationWithSetting() {
|
@override
|
||||||
settings.getIntSetting('workout_duration_minutes').then((workoutMinutes) =>
|
void dispose() {
|
||||||
{setState(() => duration = Duration(minutes: workoutMinutes ?? 10))});
|
_timer?.cancel();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void _startTimer() {
|
||||||
if (countDown) {
|
_isPaused = false;
|
||||||
setDurationWithSetting();
|
_timer = Timer.periodic(const Duration(seconds: 1), (_) => _tick());
|
||||||
} else {
|
|
||||||
setState(() => duration = const Duration());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void startTimer() {
|
void _pauseTimer() {
|
||||||
timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
|
_isPaused = true;
|
||||||
|
_timer?.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addTime() {
|
void _resetTimer() {
|
||||||
final addSeconds = countDown ? -1 : 1;
|
_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(() {
|
setState(() {
|
||||||
final seconds = duration.inSeconds + addSeconds;
|
_currentDuration = Duration(
|
||||||
if (seconds < 0) {
|
seconds: _currentDuration.inSeconds - 1,
|
||||||
timer?.cancel();
|
);
|
||||||
} else {
|
_totalDuration = Duration(
|
||||||
duration = Duration(seconds: seconds);
|
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 {
|
||||||
|
// End workout.
|
||||||
|
_currentDuration = _cooldownDuration;
|
||||||
|
_pauseTimer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopTimer({bool resets = true}) {
|
String _formatDuration(Duration duration) {
|
||||||
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 twoDigits(int n) => n.toString().padLeft(2, '0');
|
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
||||||
final minutes = twoDigits(duration.inMinutes.remainder(60));
|
final minutes = twoDigits(duration.inMinutes.remainder(60));
|
||||||
final seconds = twoDigits(duration.inSeconds.remainder(60));
|
final seconds = twoDigits(duration.inSeconds.remainder(60));
|
||||||
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
return '$minutes:$seconds';
|
||||||
const SizedBox(
|
|
||||||
width: 8,
|
|
||||||
),
|
|
||||||
buildTimeCard(time: minutes, header: 'MINUTEN'),
|
|
||||||
const SizedBox(
|
|
||||||
width: 8,
|
|
||||||
),
|
|
||||||
buildTimeCard(time: seconds, header: 'SEKUNDEN'),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildTimeCard({required String time, required String header}) =>
|
String _formatTotalDuration(Duration duration) {
|
||||||
Column(
|
final minutes = duration.inMinutes;
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
final seconds = duration.inSeconds.remainder(60);
|
||||||
children: [
|
return _formatDuration(Duration(minutes: minutes, seconds: seconds));
|
||||||
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
Text(header, style: const TextStyle(color: Colors.black45)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget buildButtons() {
|
@override
|
||||||
final isRunning = timer == null ? false : timer!.isActive;
|
Widget build(BuildContext context) {
|
||||||
final isCompleted = duration.inSeconds == 0;
|
return Scaffold(
|
||||||
return isRunning || isCompleted
|
appBar: AppBar(
|
||||||
? TimerButton(
|
title: const Text('Interval Timer'),
|
||||||
onClicked: stopTimer,
|
),
|
||||||
icon: const Icon(
|
body: Center(
|
||||||
Icons.stop,
|
child: Column(
|
||||||
size: 50,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
color: Colors.white,
|
children: [
|
||||||
|
Text(
|
||||||
|
_currentBlock == 0
|
||||||
|
? 'Warm-up'
|
||||||
|
: _currentBlock <= _numHighIntensityBlocks
|
||||||
|
? 'High Intensity'
|
||||||
|
: 'Low Intensity',
|
||||||
|
style: const TextStyle(fontSize: 32.0),
|
||||||
),
|
),
|
||||||
color: Colors.red)
|
const SizedBox(height: 16.0),
|
||||||
: TimerButton(
|
Text(
|
||||||
onClicked: startTimer,
|
_formatDuration(_currentDuration),
|
||||||
icon: const Icon(
|
style: const TextStyle(fontSize: 80.0),
|
||||||
Icons.play_arrow,
|
|
||||||
size: 50,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
color: Colors.green);
|
const SizedBox(height: 32.0),
|
||||||
|
Text(
|
||||||
|
'Total: ${_formatTotalDuration(_totalDuration)}',
|
||||||
|
style: const TextStyle(fontSize: 24.0),
|
||||||
|
),
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,6 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
class SettingsService {
|
class SettingsService {
|
||||||
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
|
||||||
|
|
||||||
SettingsService() {
|
|
||||||
setIntSetting('workout_duration_minutes', 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setStringSetting(String settingKey, String settingValue) =>
|
void setStringSetting(String settingKey, String settingValue) =>
|
||||||
_prefs.then((pref) => pref.setString(settingKey, settingValue));
|
_prefs.then((pref) => pref.setString(settingKey, settingValue));
|
||||||
|
|
||||||
|
|
140
pubspec.lock
|
@ -8,6 +8,62 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.9.0"
|
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:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -36,6 +92,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.16.0"
|
||||||
|
crypto:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: crypto
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -86,6 +149,20 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -93,6 +170,27 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.4"
|
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:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -128,6 +226,27 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
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:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -170,6 +289,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.4"
|
version: "4.2.4"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.27.7"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -266,6 +392,20 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.12"
|
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:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -3,7 +3,7 @@ description: A new Flutter project.
|
||||||
|
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# 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.
|
# The following defines the version and build number for your application.
|
||||||
# A version number is three numbers separated by dots, like 1.2.43
|
# 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
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
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.
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
# To automatically upgrade your package dependencies to the latest versions
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
@ -36,6 +36,8 @@ dependencies:
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
shared_preferences: ^2.0.17
|
shared_preferences: ^2.0.17
|
||||||
|
audioplayers: ^3.0.1
|
||||||
|
just_audio: ^0.9.31
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -59,7 +61,8 @@ flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
# assets:
|
assets:
|
||||||
|
- beep.mp3
|
||||||
# - images/a_dot_burr.jpeg
|
# - images/a_dot_burr.jpeg
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
|
|