Merge branch '29-provider-refactoring' into 'main'

Change State Management to Provider Package

Closes #29

See merge request Crondung/hsma_cpd!20
main
Kai Mannweiler 2023-03-02 15:23:47 +00:00
commit 4bde30f00e
14 changed files with 333 additions and 291 deletions

View File

@ -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

View File

@ -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,
};
}
}

View File

@ -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,
};
}
}

View File

@ -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: [ children: const [
Text( Align(
_currentBlock == 0 alignment: Alignment.topLeft,
? 'Warm-up' child: MuteButton(),
: _currentBlock % 2 == 1
? 'High Intensity'
: _currentBlock < _numHighIntensityBlocks * 2
? 'Low Intensity'
: 'Cool-down',
style: const TextStyle(fontSize: 32.0),
), ),
const SizedBox(height: 16.0), WorkoutTimerWidget()
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();
}
},
),
// ),
], ],
), ),
], );
)));
} }
} }

View File

@ -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 {

View File

@ -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();
}
} }

View File

@ -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
}
};

View File

@ -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
) )
'''; ''';
}

View File

@ -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));

View File

@ -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';

View File

@ -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),
);
}
}

View File

@ -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'))
], ],
), ),
); );

View File

@ -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'))
], ],
); );
} }

View File

@ -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'))
],
);
}
}