Merge branch '29-provider-refactoring' into 'main'
Change State Management to Provider Package Closes #29 See merge request Crondung/hsma_cpd!20main
commit
4bde30f00e
|
@ -2,7 +2,9 @@ import 'package:smoke_cess_app/interface/db_record.dart';
|
||||||
import 'package:smoke_cess_app/models/mood.dart';
|
import 'package:smoke_cess_app/models/mood.dart';
|
||||||
import 'package:smoke_cess_app/models/relapse.dart';
|
import 'package:smoke_cess_app/models/relapse.dart';
|
||||||
import 'package:smoke_cess_app/models/sleep.dart';
|
import 'package:smoke_cess_app/models/sleep.dart';
|
||||||
|
import 'package:smoke_cess_app/models/workout.dart';
|
||||||
import 'package:smoke_cess_app/services/database_service.dart';
|
import 'package:smoke_cess_app/services/database_service.dart';
|
||||||
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:sqflite_common/sqlite_api.dart';
|
import 'package:sqflite_common/sqlite_api.dart';
|
||||||
|
|
||||||
class DatabaseMock implements DatabaseService {
|
class DatabaseMock implements DatabaseService {
|
||||||
|
@ -29,6 +31,12 @@ class DatabaseMock implements DatabaseService {
|
||||||
return Future.value(1);
|
return Future.value(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> addWorkout(Workout workout) {
|
||||||
|
_workoutRecords.add(workout);
|
||||||
|
return Future.value(1);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> addRelapse(Relapse relapse) {
|
Future<int> addRelapse(Relapse relapse) {
|
||||||
_relapseRecords.add(relapse);
|
_relapseRecords.add(relapse);
|
||||||
|
@ -36,7 +44,6 @@ class DatabaseMock implements DatabaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
// TODO: implement database
|
|
||||||
Future<Database> get database => DatabaseService.instance.database;
|
Future<Database> get database => DatabaseService.instance.database;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import 'package:smoke_cess_app/interface/db_record.dart';
|
|
||||||
|
|
||||||
class HIITWorkout implements DatabaseRecord {
|
|
||||||
Duration _workoutDuration;
|
|
||||||
String _commentBefore;
|
|
||||||
String _commentAfter;
|
|
||||||
DateTime _workoutDate;
|
|
||||||
|
|
||||||
HIITWorkout(this._workoutDuration, this._commentBefore, this._commentAfter,
|
|
||||||
this._workoutDate);
|
|
||||||
|
|
||||||
//TODO Felder anpassen
|
|
||||||
@override
|
|
||||||
factory HIITWorkout.fromMap(Map<String, dynamic> map) {
|
|
||||||
return HIITWorkout(map['_workoutDuration'], map['_commentBefore'],
|
|
||||||
map['_commentAfter'], map['_workoutDate']);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toCSV() =>
|
|
||||||
"${_workoutDate.toIso8601String()}, $_workoutDuration, $_commentBefore, $_commentAfter";
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return {
|
|
||||||
'workoutDuration': _workoutDuration,
|
|
||||||
'commentBefore': _commentBefore,
|
|
||||||
'commentAfter': _commentAfter,
|
|
||||||
'workoutDate': _workoutDate,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import 'package:smoke_cess_app/interface/db_record.dart';
|
||||||
|
|
||||||
|
class Workout implements DatabaseRecord {
|
||||||
|
int _motivationBefore;
|
||||||
|
int _motivationAfter;
|
||||||
|
DateTime _workoutDate;
|
||||||
|
|
||||||
|
Workout(this._motivationBefore, this._motivationAfter, this._workoutDate);
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory Workout.fromMap(Map<String, dynamic> map) {
|
||||||
|
return Workout(map['_workoutDuration'], map['_motivationBefore'],
|
||||||
|
map['_motivationAfter']);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toCSV() =>
|
||||||
|
"${_workoutDate.toIso8601String()}, $_motivationBefore, $_motivationAfter";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'motivationBefore': _motivationBefore,
|
||||||
|
'motivationAfter': _motivationAfter,
|
||||||
|
'workoutDate': _workoutDate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,225 +1,32 @@
|
||||||
import 'dart:async';
|
|
||||||
import 'package:audioplayers/audioplayers.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:smoke_cess_app/providers/timer_provider.dart';
|
import 'package:smoke_cess_app/providers/timer_provider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/popup_for_start_and_stop.dart';
|
import 'package:smoke_cess_app/providers/workout_provider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/timer_widget.dart';
|
import 'package:smoke_cess_app/widgets/mute_button.dart';
|
||||||
|
import 'package:smoke_cess_app/widgets/workout_timer_widget.dart';
|
||||||
|
|
||||||
import '../providers/input_provider.dart';
|
class IntervalTimerPage extends StatelessWidget {
|
||||||
|
const IntervalTimerPage({super.key});
|
||||||
class IntervalTimerPage extends StatefulWidget {
|
|
||||||
const IntervalTimerPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_IntervalTimerPageState createState() => _IntervalTimerPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
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(minutes: 35);
|
|
||||||
AudioPlayer warmUpPlayer = AudioPlayer();
|
|
||||||
AudioPlayer workoutPlayer = AudioPlayer();
|
|
||||||
AudioPlayer coolDownPlayer = AudioPlayer();
|
|
||||||
final AudioCache _audioCache = AudioCache();
|
|
||||||
final int _numHighIntensityBlocks = 4;
|
|
||||||
final int _numLowIntensityBlocks = 3;
|
|
||||||
|
|
||||||
Timer? _timer;
|
|
||||||
int _currentBlock = 0;
|
|
||||||
Duration _currentDuration = const Duration();
|
|
||||||
bool _isPaused = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_currentDuration = _warmupDuration;
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_timer?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _startTimer() async {
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return ChangeNotifierProvider(
|
|
||||||
create: (context) => InputProvider(),
|
|
||||||
child: const TimerStartStopPopup(
|
|
||||||
title: 'Motivation vor dem Training',
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
|
||||||
_isPaused = false;
|
|
||||||
Source source = AssetSource('go.mp3');
|
|
||||||
await AudioPlayer().play(source);
|
|
||||||
|
|
||||||
_timer = Timer.periodic(const Duration(seconds: 1), (_) => _tick());
|
|
||||||
Future.delayed(const Duration(seconds: 1)).then((value) {
|
|
||||||
_playWarmUpMusic();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _resetTimer() {
|
|
||||||
() async {
|
|
||||||
await coolDownPlayer.stop();
|
|
||||||
await warmUpPlayer.stop();
|
|
||||||
await workoutPlayer.stop();
|
|
||||||
}();
|
|
||||||
_isPaused = true;
|
|
||||||
_timer?.cancel();
|
|
||||||
_currentBlock = 0;
|
|
||||||
_currentDuration = _warmupDuration;
|
|
||||||
_totalDuration = const Duration(minutes: 35);
|
|
||||||
setState(() {});
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return const TimerStartStopPopup(
|
|
||||||
title: 'Motivation nach dem Training',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _playWarmUpMusic() async {
|
|
||||||
Source source = AssetSource('warmUp.mp3');
|
|
||||||
await warmUpPlayer.setReleaseMode(ReleaseMode.loop);
|
|
||||||
await warmUpPlayer.play(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _playWorkoutMusic() async {
|
|
||||||
await warmUpPlayer.stop();
|
|
||||||
Future.delayed(const Duration(microseconds: 600)).then((value) async {
|
|
||||||
Source source = AssetSource('workout.mp3');
|
|
||||||
await workoutPlayer.setReleaseMode(ReleaseMode.loop);
|
|
||||||
await workoutPlayer.play(source);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _intervalChange() async {
|
|
||||||
Source source = AssetSource('beep.mp3');
|
|
||||||
await AudioPlayer().play(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _tick() {
|
|
||||||
setState(() {
|
|
||||||
_currentDuration = Duration(
|
|
||||||
seconds: _currentDuration.inSeconds - 1,
|
|
||||||
);
|
|
||||||
_totalDuration = Duration(
|
|
||||||
seconds: _totalDuration.inSeconds - 1,
|
|
||||||
);
|
|
||||||
if (_currentDuration.inSeconds < 1) {
|
|
||||||
if (_currentBlock < _numHighIntensityBlocks + _numLowIntensityBlocks) {
|
|
||||||
_intervalChange();
|
|
||||||
if (_currentBlock == 0) {
|
|
||||||
_playWorkoutMusic();
|
|
||||||
}
|
|
||||||
_currentBlock++;
|
|
||||||
|
|
||||||
if (_currentBlock % 2 == 1) {
|
|
||||||
_currentDuration = _highIntensityDuration;
|
|
||||||
} else {
|
|
||||||
_currentDuration = _lowIntensityDuration;
|
|
||||||
}
|
|
||||||
} else if (_currentBlock < _numHighIntensityBlocks * 2) {
|
|
||||||
_intervalChange();
|
|
||||||
_currentBlock++;
|
|
||||||
_currentDuration = _cooldownDuration;
|
|
||||||
() async {
|
|
||||||
await workoutPlayer.stop();
|
|
||||||
Source source = AssetSource('cool_down.mp3');
|
|
||||||
await coolDownPlayer.setReleaseMode(ReleaseMode.loop);
|
|
||||||
await coolDownPlayer.play(source);
|
|
||||||
}();
|
|
||||||
} else {
|
|
||||||
() async {
|
|
||||||
Future.delayed(const Duration(microseconds: 900))
|
|
||||||
.then((value) async {
|
|
||||||
Source source = AssetSource('finish.mp3');
|
|
||||||
await AudioPlayer().play(source);
|
|
||||||
});
|
|
||||||
}();
|
|
||||||
_resetTimer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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 '$minutes:$seconds';
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatTotalDuration(Duration duration) {
|
|
||||||
final minutes = duration.inMinutes;
|
|
||||||
final seconds = duration.inSeconds.remainder(60);
|
|
||||||
return _formatDuration(Duration(minutes: minutes, seconds: seconds));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ChangeNotifierProvider(
|
TimerProvider timerProvider = TimerProvider();
|
||||||
create: (context) => TimerProvider(),
|
return MultiProvider(
|
||||||
child: TimerWidget(
|
providers: [
|
||||||
duration: Duration(seconds: 5),
|
ChangeNotifierProvider(create: (context) => timerProvider),
|
||||||
));
|
ChangeNotifierProvider(
|
||||||
return Center(
|
create: (context) => WorkoutProvider(timerProvider)),
|
||||||
child: ChangeNotifierProvider(
|
],
|
||||||
create: (context) => InputProvider(),
|
child: Column(
|
||||||
child: Column(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: const [
|
||||||
children: [
|
Align(
|
||||||
Text(
|
alignment: Alignment.topLeft,
|
||||||
_currentBlock == 0
|
child: MuteButton(),
|
||||||
? 'Warm-up'
|
),
|
||||||
: _currentBlock % 2 == 1
|
WorkoutTimerWidget()
|
||||||
? 'High Intensity'
|
],
|
||||||
: _currentBlock < _numHighIntensityBlocks * 2
|
),
|
||||||
? 'Low Intensity'
|
);
|
||||||
: 'Cool-down',
|
|
||||||
style: const TextStyle(fontSize: 32.0),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
Text(
|
|
||||||
_formatDuration(_currentDuration),
|
|
||||||
style: const TextStyle(fontSize: 80.0),
|
|
||||||
),
|
|
||||||
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 {
|
|
||||||
_resetTimer();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
// ),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,9 @@ class ScannerPage extends StatelessWidget {
|
||||||
List<Mood> moods = await globals.databaseService.getMoodRecords();
|
List<Mood> moods = await globals.databaseService.getMoodRecords();
|
||||||
List<Sleep> sleeps = await globals.databaseService.getSleepRecords();
|
List<Sleep> sleeps = await globals.databaseService.getSleepRecords();
|
||||||
List<Relapse> relapses = await globals.databaseService.getRelapseRecords();
|
List<Relapse> relapses = await globals.databaseService.getRelapseRecords();
|
||||||
for (Mood mood in moods) {
|
moods;
|
||||||
print(mood.toCSV());
|
sleeps;
|
||||||
}
|
relapses;
|
||||||
for (Sleep sleep in sleeps) {
|
|
||||||
print(sleep.toCSV());
|
|
||||||
}
|
|
||||||
for (Relapse relapse in relapses) {
|
|
||||||
print(relapse.toCSV());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadJSON(BuildContext context) async {
|
void loadJSON(BuildContext context) async {
|
||||||
|
|
|
@ -9,7 +9,6 @@ class TimerProvider extends ChangeNotifier {
|
||||||
|
|
||||||
void startTimer(Duration duration) {
|
void startTimer(Duration duration) {
|
||||||
started = true;
|
started = true;
|
||||||
print('starting timer');
|
|
||||||
_timer = Timer.periodic(const Duration(seconds: 1), ((timer) {
|
_timer = Timer.periodic(const Duration(seconds: 1), ((timer) {
|
||||||
if (timer.tick >= duration.inSeconds) {
|
if (timer.tick >= duration.inSeconds) {
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
|
@ -19,5 +18,8 @@ class TimerProvider extends ChangeNotifier {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopTimer() => _timer?.cancel();
|
void stopTimer() {
|
||||||
|
started = false;
|
||||||
|
_timer?.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:audioplayers/audioplayers.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smoke_cess_app/models/workout.dart';
|
||||||
|
import 'package:smoke_cess_app/providers/timer_provider.dart';
|
||||||
|
import '../globals.dart' as globals;
|
||||||
|
|
||||||
|
class WorkoutProvider extends ChangeNotifier {
|
||||||
|
TimerProvider timerProvider;
|
||||||
|
|
||||||
|
final AudioPlayer _audioPlayer = AudioPlayer();
|
||||||
|
late StreamSubscription _onCompleteSubscription;
|
||||||
|
final Source _finishedSoundSource = AssetSource('finish.mp3');
|
||||||
|
final Source _beepSoundSource = AssetSource('beep.mp3');
|
||||||
|
bool isWorkoutStarted = false;
|
||||||
|
bool isWorkoutComplete = false;
|
||||||
|
bool isMuted = false;
|
||||||
|
int motivationBefore = 50;
|
||||||
|
int motivationAfter = 50;
|
||||||
|
|
||||||
|
void mutePlayer() {
|
||||||
|
isMuted = true;
|
||||||
|
_audioPlayer.setVolume(0);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unMutePlayer() {
|
||||||
|
isMuted = false;
|
||||||
|
_audioPlayer.setVolume(1);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkoutProvider(this.timerProvider);
|
||||||
|
|
||||||
|
final List<String> _workoutPhases = [
|
||||||
|
'Warm-Up',
|
||||||
|
'High Intensity',
|
||||||
|
'Low Intensity',
|
||||||
|
'High Intensity',
|
||||||
|
'Low Intensity',
|
||||||
|
'High Intensity',
|
||||||
|
'Low Intensity',
|
||||||
|
'High Intensity',
|
||||||
|
'Cool-down'
|
||||||
|
];
|
||||||
|
int _workoutPhaseIndex = 0;
|
||||||
|
|
||||||
|
String get currentPhase => _workoutPhases[_workoutPhaseIndex];
|
||||||
|
Duration get currentPhaseDuration =>
|
||||||
|
_workoutPhaseSettings[currentPhase]!['duration'];
|
||||||
|
bool get isPhaseComplete =>
|
||||||
|
timerProvider.elapsedSeconds - currentPhaseDuration.inSeconds == 0;
|
||||||
|
Color get currentPhaseColor => _workoutPhaseSettings[currentPhase]!['color'];
|
||||||
|
AssetSource get currentPhaseSource =>
|
||||||
|
_workoutPhaseSettings[currentPhase]!['source'];
|
||||||
|
/* bool get isWorkoutComplete =>
|
||||||
|
_workoutPhaseIndex == _workoutPhases.length - 1 && isPhaseComplete; */
|
||||||
|
|
||||||
|
void nextPhase() {
|
||||||
|
_onCompleteSubscription.cancel();
|
||||||
|
_audioPlayer.stop();
|
||||||
|
if (_workoutPhaseIndex < _workoutPhases.length - 1) {
|
||||||
|
_audioPlayer.play(_beepSoundSource);
|
||||||
|
_workoutPhaseIndex += 1;
|
||||||
|
_onCompleteSubscription = _audioPlayer.onPlayerComplete.listen((event) {
|
||||||
|
_audioPlayer.play(currentPhaseSource);
|
||||||
|
});
|
||||||
|
timerProvider.startTimer(currentPhaseDuration);
|
||||||
|
} else {
|
||||||
|
//workout completed
|
||||||
|
_audioPlayer.play(_finishedSoundSource);
|
||||||
|
stopWorkout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startWorkout() {
|
||||||
|
isWorkoutStarted = true;
|
||||||
|
isWorkoutComplete = false;
|
||||||
|
_audioPlayer.play(_beepSoundSource).whenComplete(() => null);
|
||||||
|
_onCompleteSubscription = _audioPlayer.onPlayerComplete.listen((event) {
|
||||||
|
_audioPlayer.play(currentPhaseSource);
|
||||||
|
});
|
||||||
|
timerProvider.startTimer(currentPhaseDuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopWorkout() {
|
||||||
|
isWorkoutStarted = false;
|
||||||
|
isWorkoutComplete = true;
|
||||||
|
_audioPlayer.stop();
|
||||||
|
timerProvider.stopTimer();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveWorkout() {
|
||||||
|
Workout workout =
|
||||||
|
Workout(motivationBefore, motivationAfter, DateTime.now());
|
||||||
|
globals.databaseService.addWorkout(workout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Map<String, dynamic>> _workoutPhaseSettings = {
|
||||||
|
'Warm-Up': {
|
||||||
|
'duration': const Duration(seconds: 5),
|
||||||
|
'source': AssetSource('warmUp.mp3'),
|
||||||
|
'color': Colors.green
|
||||||
|
},
|
||||||
|
'High Intensity': {
|
||||||
|
'duration': const Duration(seconds: 4),
|
||||||
|
'source': AssetSource('workout.mp3'),
|
||||||
|
'color': Colors.red
|
||||||
|
},
|
||||||
|
'Low Intensity': {
|
||||||
|
'duration': const Duration(seconds: 3),
|
||||||
|
'source': AssetSource('workout.mp3'),
|
||||||
|
'color': Colors.orange
|
||||||
|
},
|
||||||
|
'Cool-down': {
|
||||||
|
'duration': const Duration(seconds: 5),
|
||||||
|
'source': AssetSource('cool_down.mp3'),
|
||||||
|
'color': Colors.blue
|
||||||
|
}
|
||||||
|
};
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:smoke_cess_app/models/mood.dart';
|
import 'package:smoke_cess_app/models/mood.dart';
|
||||||
|
import 'package:smoke_cess_app/models/workout.dart';
|
||||||
import 'package:smoke_cess_app/models/relapse.dart';
|
import 'package:smoke_cess_app/models/relapse.dart';
|
||||||
import 'package:sqflite/sqflite.dart';
|
import 'package:sqflite/sqflite.dart';
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
|
@ -35,7 +36,6 @@ class DatabaseService {
|
||||||
await db.execute(_createWorkoutTable);
|
await db.execute(_createWorkoutTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO use generic function?
|
|
||||||
Future<List<Mood>> getMoodRecords() async {
|
Future<List<Mood>> getMoodRecords() async {
|
||||||
Database db = await instance.database;
|
Database db = await instance.database;
|
||||||
var moodRecords = await db.query('mood');
|
var moodRecords = await db.query('mood');
|
||||||
|
@ -73,13 +73,17 @@ class DatabaseService {
|
||||||
return await db.insert('sleep', sleep.toMap());
|
return await db.insert('sleep', sleep.toMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<int> addWorkout(Workout workout) async {
|
||||||
|
Database db = await instance.database;
|
||||||
|
return await db.insert('workout', workout.toMap());
|
||||||
|
}
|
||||||
|
|
||||||
Future<int> addRelapse(Relapse relapse) async {
|
Future<int> addRelapse(Relapse relapse) async {
|
||||||
Database db = await instance.database;
|
Database db = await instance.database;
|
||||||
return await db.insert('relapse', relapse.toMap());
|
return await db.insert('relapse', relapse.toMap());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String _createMoodTable = '''
|
final String _createMoodTable = '''
|
||||||
CREATE TABLE IF NOT EXISTS mood(
|
CREATE TABLE IF NOT EXISTS mood(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
value INTEGER,
|
value INTEGER,
|
||||||
|
@ -88,7 +92,7 @@ String _createMoodTable = '''
|
||||||
)
|
)
|
||||||
''';
|
''';
|
||||||
|
|
||||||
String _createSleepTable = '''
|
final String _createSleepTable = '''
|
||||||
CREATE TABLE IF NOT EXISTS sleep(
|
CREATE TABLE IF NOT EXISTS sleep(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
value INTEGER,
|
value INTEGER,
|
||||||
|
@ -101,7 +105,7 @@ String _createSleepTable = '''
|
||||||
)
|
)
|
||||||
''';
|
''';
|
||||||
|
|
||||||
String _createRelapseTable = '''
|
final String _createRelapseTable = '''
|
||||||
CREATE TABLE IF NOT EXISTS relapse(
|
CREATE TABLE IF NOT EXISTS relapse(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
date TEXT,
|
date TEXT,
|
||||||
|
@ -110,14 +114,12 @@ String _createRelapseTable = '''
|
||||||
)
|
)
|
||||||
''';
|
''';
|
||||||
|
|
||||||
String _createWorkoutTable = '''
|
final String _createWorkoutTable = '''
|
||||||
CREATE TABLE IF NOT EXISTS workout(
|
CREATE TABLE IF NOT EXISTS workout(
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
date TEXT,
|
date TEXT,
|
||||||
motivationBefore INTEGER,
|
motivationBefore INTEGER,
|
||||||
commentBefore TEXT,
|
|
||||||
motivationAfter INTEGER,
|
motivationAfter INTEGER,
|
||||||
commentAfter TEXT,
|
|
||||||
completed INTEGER
|
|
||||||
)
|
)
|
||||||
''';
|
''';
|
||||||
|
}
|
||||||
|
|
|
@ -23,13 +23,6 @@ Future<int?> getMoodQueryMinutes() => _getIntSetting('mood_query_minutes');
|
||||||
Future<int?> getChessHours() => _getIntSetting('chess_hours');
|
Future<int?> getChessHours() => _getIntSetting('chess_hours');
|
||||||
Future<int?> getChessMinutes() => _getIntSetting('chess_minutes');
|
Future<int?> getChessMinutes() => _getIntSetting('chess_minutes');
|
||||||
|
|
||||||
void _setStringSetting(String settingKey, String settingValue) =>
|
|
||||||
SharedPreferences.getInstance()
|
|
||||||
.then((pref) => pref.setString(settingKey, settingValue));
|
|
||||||
|
|
||||||
Future<String?> _getStringSetting(String settingKey) =>
|
|
||||||
SharedPreferences.getInstance().then((pref) => pref.getString(settingKey));
|
|
||||||
|
|
||||||
void _setIntSetting(String settingKey, int settingValue) =>
|
void _setIntSetting(String settingKey, int settingValue) =>
|
||||||
SharedPreferences.getInstance()
|
SharedPreferences.getInstance()
|
||||||
.then((pref) => pref.setInt(settingKey, settingValue));
|
.then((pref) => pref.setInt(settingKey, settingValue));
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:smoke_cess_app/models/mood.dart';
|
|
||||||
import 'package:smoke_cess_app/services/database_service.dart';
|
|
||||||
import 'package:smoke_cess_app/widgets/slider.dart';
|
import 'package:smoke_cess_app/widgets/slider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/submit_form_button.dart';
|
import 'package:smoke_cess_app/widgets/submit_form_button.dart';
|
||||||
import 'package:smoke_cess_app/widgets/text_formfield.dart';
|
import 'package:smoke_cess_app/widgets/text_formfield.dart';
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smoke_cess_app/providers/workout_provider.dart';
|
||||||
|
|
||||||
|
class MuteButton extends StatelessWidget {
|
||||||
|
const MuteButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
WorkoutProvider workoutProvider = context.watch<WorkoutProvider>();
|
||||||
|
|
||||||
|
return IconButton(
|
||||||
|
onPressed: workoutProvider.isMuted
|
||||||
|
? workoutProvider.unMutePlayer
|
||||||
|
: workoutProvider.mutePlayer,
|
||||||
|
icon: Icon(workoutProvider.isMuted
|
||||||
|
? Icons.volume_off_outlined
|
||||||
|
: Icons.volume_up_outlined),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +1,52 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smoke_cess_app/providers/input_provider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/slider.dart';
|
import 'package:smoke_cess_app/widgets/slider.dart';
|
||||||
import 'package:smoke_cess_app/widgets/text_formfield.dart';
|
|
||||||
|
Future showMotivationPopup(
|
||||||
|
BuildContext context, Function onSave, String title) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return ChangeNotifierProvider(
|
||||||
|
create: (context) => InputProvider(),
|
||||||
|
child: TimerStartStopPopup(
|
||||||
|
title: title,
|
||||||
|
onSaveAction: onSave,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class TimerStartStopPopup extends StatelessWidget {
|
class TimerStartStopPopup extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
|
final Function onSaveAction;
|
||||||
const TimerStartStopPopup({Key? key, required this.title}) : super(key: key);
|
const TimerStartStopPopup(
|
||||||
|
{Key? key, required this.title, required this.onSaveAction})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
InputProvider inputProvider = context.watch<InputProvider>();
|
||||||
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: const [
|
children: [
|
||||||
Padding(
|
const Padding(
|
||||||
padding: const EdgeInsets.only(top: 8),
|
padding: EdgeInsets.only(top: 8),
|
||||||
child: MySlider(),
|
child: MySlider(),
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
MyTextFormField('Beschreibe deinen Motivation'),
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
onSaveAction(inputProvider.sliderValue);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: const Text('Speichern'))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,11 +14,6 @@ class TimerWidget extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(formatTime(duration.inSeconds - timerProvider.elapsedSeconds)),
|
Text(formatTime(duration.inSeconds - timerProvider.elapsedSeconds)),
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => timerProvider.started
|
|
||||||
? timerProvider.stopTimer()
|
|
||||||
: timerProvider.startTimer(duration),
|
|
||||||
child: Text(timerProvider.started ? 'Stop' : 'Start'))
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:smoke_cess_app/providers/workout_provider.dart';
|
||||||
|
import 'package:smoke_cess_app/widgets/timer_widget.dart';
|
||||||
|
|
||||||
|
import '../providers/timer_provider.dart';
|
||||||
|
import 'popup_for_start_and_stop.dart';
|
||||||
|
|
||||||
|
class WorkoutTimerWidget extends StatelessWidget {
|
||||||
|
const WorkoutTimerWidget({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
TimerProvider timerProvider = context.watch<TimerProvider>();
|
||||||
|
WorkoutProvider workoutProvider = context.watch<WorkoutProvider>();
|
||||||
|
|
||||||
|
if (workoutProvider.isPhaseComplete && !workoutProvider.isWorkoutComplete) {
|
||||||
|
Timer(const Duration(milliseconds: 1), () => workoutProvider.nextPhase());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workoutProvider.isWorkoutComplete) {
|
||||||
|
Timer(
|
||||||
|
const Duration(milliseconds: 1),
|
||||||
|
() => showMotivationPopup(context, (double value) {
|
||||||
|
workoutProvider.motivationAfter = value.toInt();
|
||||||
|
workoutProvider.saveWorkout();
|
||||||
|
}, 'Motivation nach dem Training'));
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleStartStopWorkout() {
|
||||||
|
if (!workoutProvider.isWorkoutStarted) {
|
||||||
|
showMotivationPopup(context, (double value) {
|
||||||
|
workoutProvider.motivationBefore = value.toInt();
|
||||||
|
workoutProvider.startWorkout();
|
||||||
|
}, 'Motivation vor dem Training');
|
||||||
|
} else {
|
||||||
|
workoutProvider.stopWorkout();
|
||||||
|
showMotivationPopup(
|
||||||
|
context,
|
||||||
|
(double value) => workoutProvider.motivationAfter = value.toInt(),
|
||||||
|
'Motivation nach dem Training');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(workoutProvider.currentPhase),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: workoutProvider.currentPhaseColor,
|
||||||
|
value: (workoutProvider.currentPhaseDuration.inSeconds
|
||||||
|
.toDouble() -
|
||||||
|
timerProvider.elapsedSeconds) /
|
||||||
|
workoutProvider.currentPhaseDuration.inSeconds)),
|
||||||
|
TimerWidget(duration: workoutProvider.currentPhaseDuration),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: handleStartStopWorkout,
|
||||||
|
child: Text(timerProvider.started ? 'Stop' : 'Start'))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue