Merge branch '24-timer-darf-nur-ein-mal-pro-tag-gedruckt-werden' into 'main'

Tasks können nur ausgeführt werden, wenn gefordert.

See merge request Crondung/hsma_cpd!25
main
Julian Gegner 2023-03-04 09:06:38 +00:00
commit 42f4fcd05d
29 changed files with 444 additions and 92 deletions

View File

@ -5,6 +5,7 @@ import 'package:smoke_cess_app/providers/tasks_provider.dart';
import 'package:smoke_cess_app/services/notification_service.dart'; import 'package:smoke_cess_app/services/notification_service.dart';
import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/data/latest.dart' as tz;
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'providers/page_provider.dart';
import 'providers/settings_provider.dart'; import 'providers/settings_provider.dart';
void main() { void main() {
@ -24,18 +25,21 @@ class MyApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MultiProvider(
title: _title, providers: [
home: MultiProvider( ChangeNotifierProvider(create: (context) => SettingsProvider()),
providers: [ ChangeNotifierProxyProvider<SettingsProvider, TasksProvider>(
ChangeNotifierProvider(create: (context) => SettingsProvider()), create: (context) => TasksProvider(null),
ChangeNotifierProxyProvider<SettingsProvider, TasksProvider>( update: (context, value, TasksProvider? previous) =>
create: (context) => TasksProvider(null), TasksProvider(value),
update: (context, value, TasksProvider? previous) => ),
TasksProvider(value), ChangeNotifierProvider(
), create: (context) => PageProvider(),
], ),
child: const MyHomePage(), ],
child: const MaterialApp(
title: _title,
home: MyHomePage(),
)); ));
} }
} }

View File

@ -8,6 +8,7 @@ class Mood implements DatabaseRecord {
Mood(this._moodValue, this._comment, this._date); Mood(this._moodValue, this._comment, this._date);
DateTime get date => _date; DateTime get date => _date;
int get moodValue => _moodValue;
@override @override
factory Mood.fromDatabase(Map<String, dynamic> map) { factory Mood.fromDatabase(Map<String, dynamic> map) {

View File

@ -7,6 +7,9 @@ class Relapse implements DatabaseRecord {
Relapse(this._category, this._comment, this._date); Relapse(this._category, this._comment, this._date);
String get category => _category;
DateTime get date => _date;
@override @override
factory Relapse.fromDatabase(Map<String, dynamic> map) { factory Relapse.fromDatabase(Map<String, dynamic> map) {
DateTime date = DateTime.parse(map['date']); DateTime date = DateTime.parse(map['date']);

View File

@ -12,6 +12,7 @@ class Sleep implements DatabaseRecord {
this._wokeUpAt); this._wokeUpAt);
DateTime get date => _date; DateTime get date => _date;
int get sleepQualitiyValue => _sleepQualityValue;
@override @override
factory Sleep.fromDatabase(Map<String, dynamic> map) { factory Sleep.fromDatabase(Map<String, dynamic> map) {

View File

@ -8,6 +8,8 @@ class Workout implements DatabaseRecord {
Workout(this._motivationBefore, this._motivationAfter, this._workoutDate); Workout(this._motivationBefore, this._motivationAfter, this._workoutDate);
DateTime get date => _workoutDate; DateTime get date => _workoutDate;
int get motivationBefore => _motivationBefore;
int get motivationAfter => _motivationAfter;
@override @override
factory Workout.fromDatabase(Map<String, dynamic> map) { factory Workout.fromDatabase(Map<String, dynamic> map) {

View File

@ -1,32 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:smoke_cess_app/services/pages_service.dart';
import 'package:smoke_cess_app/providers/timer_provider.dart'; import 'package:smoke_cess_app/widgets/workout_form.dart';
import 'package:smoke_cess_app/providers/workout_provider.dart'; import 'package:smoke_cess_app/widgets/workout_view.dart';
import 'package:smoke_cess_app/widgets/mute_button.dart';
import 'package:smoke_cess_app/widgets/workout_timer_widget.dart'; import '../widgets/view_form_page.dart';
class IntervalTimerPage extends StatelessWidget { class IntervalTimerPage extends StatelessWidget {
const IntervalTimerPage({super.key}); const IntervalTimerPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
TimerProvider timerProvider = TimerProvider(); return const ViewFormPage(
return MultiProvider( form: WorkoutForm(), view: WorkoutView(), page: Pages.timer);
providers: [
ChangeNotifierProvider(create: (context) => timerProvider),
ChangeNotifierProvider(
create: (context) => WorkoutProvider(timerProvider)),
],
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Align(
alignment: Alignment.topLeft,
child: MuteButton(),
),
WorkoutTimerWidget()
],
),
);
} }
} }

View File

@ -1,6 +1,7 @@
import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:awesome_dialog/awesome_dialog.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/page_provider.dart';
import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/providers/tasks_provider.dart';
import 'package:smoke_cess_app/services/pages_service.dart'; import 'package:smoke_cess_app/services/pages_service.dart';
import 'package:smoke_cess_app/providers/settings_provider.dart'; import 'package:smoke_cess_app/providers/settings_provider.dart';
@ -19,15 +20,19 @@ class MyHomePageState extends State<MyHomePage> {
bool _isConfigured = false; bool _isConfigured = false;
void _onItemTapped(int index) { void _onItemTapped(int index) {
PageProvider pageProvider = context.read<PageProvider>();
setState(() { setState(() {
_isConfigured if (_isConfigured) {
? _selectedIndex = index pageProvider.showForm = false;
: AwesomeDialog( _selectedIndex = index;
context: context, return;
dialogType: DialogType.info, }
title: 'Fehlende Konfiguration', AwesomeDialog(
desc: 'Bitte QR Code Scannen!', context: context,
).show(); dialogType: DialogType.info,
title: 'Fehlende Konfiguration',
desc: 'Bitte QR Code Scannen!',
).show();
}); });
} }
@ -40,9 +45,9 @@ class MyHomePageState extends State<MyHomePage> {
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'${pages.values.elementAt(_selectedIndex)['title']} ${_isConfigured ? "Gruppe ${settingsModel.settings?.group}" : ""}')), '${pages.values.elementAt(_selectedIndex)['title']} ${_isConfigured ? "Gruppe ${settingsModel.settings?.group}" : ""}')),
body: Center( body: SingleChildScrollView(
child: SingleChildScrollView( child: pages.values.elementAt(_selectedIndex)['page'],
child: pages.values.elementAt(_selectedIndex)['page'])), ),
bottomNavigationBar: NavigationBar( bottomNavigationBar: NavigationBar(
onDestinationSelected: _onItemTapped, onDestinationSelected: _onItemTapped,
selectedIndex: _selectedIndex, selectedIndex: _selectedIndex,

View File

@ -1,17 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:smoke_cess_app/services/pages_service.dart';
import 'package:smoke_cess_app/providers/input_provider.dart';
import 'package:smoke_cess_app/widgets/mood_form.dart'; import 'package:smoke_cess_app/widgets/mood_form.dart';
import 'package:smoke_cess_app/widgets/mood_view.dart';
import 'package:smoke_cess_app/widgets/view_form_page.dart';
class MoodPage extends StatelessWidget { class MoodPage extends StatelessWidget {
const MoodPage({super.key}); const MoodPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return const ViewFormPage(
child: ChangeNotifierProvider( form: MoodForm(), view: MoodView(), page: Pages.mood);
create: (context) => InputProvider(),
child: const MoodForm(),
));
} }
} }

View File

@ -1,17 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:smoke_cess_app/services/pages_service.dart';
import 'package:smoke_cess_app/widgets/relapse_form.dart'; import 'package:smoke_cess_app/widgets/relapse_form.dart';
import '../providers/input_provider.dart'; import 'package:smoke_cess_app/widgets/relapse_view.dart';
import '../widgets/view_form_page.dart';
class RelapsePage extends StatelessWidget { class RelapsePage extends StatelessWidget {
const RelapsePage({super.key}); const RelapsePage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return const ViewFormPage(
child: ChangeNotifierProvider( form: RelapseForm(), view: RelapseView(), page: Pages.relapse);
create: (context) => InputProvider(),
child: const RelapseForm(),
));
} }
} }

View File

@ -1,17 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:smoke_cess_app/services/pages_service.dart';
import 'package:smoke_cess_app/providers/input_provider.dart';
import 'package:smoke_cess_app/widgets/sleep_form.dart'; import 'package:smoke_cess_app/widgets/sleep_form.dart';
import 'package:smoke_cess_app/widgets/sleep_view.dart';
import 'package:smoke_cess_app/widgets/view_form_page.dart';
class SleepPage extends StatelessWidget { class SleepPage extends StatelessWidget {
const SleepPage({super.key}); const SleepPage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Center( return const ViewFormPage(
child: ChangeNotifierProvider( form: SleepForm(), view: SleepView(), page: Pages.sleep);
create: (context) => InputProvider(),
child: const SleepForm(),
));
} }
} }

View File

@ -11,22 +11,16 @@ class InputProvider extends ChangeNotifier {
'wokeUpAt': const TimeOfDay(hour: 8, minute: 0), 'wokeUpAt': const TimeOfDay(hour: 8, minute: 0),
'sleptAt': const TimeOfDay(hour: 22, minute: 0), 'sleptAt': const TimeOfDay(hour: 22, minute: 0),
}; };
String _relapseCategory = ''; String relapseCategory = '';
double get sliderValue => _sliderValue; double get sliderValue => _sliderValue;
TextEditingController get textController => _textController; TextEditingController get textController => _textController;
String get relapseCategory => _relapseCategory;
set sliderValue(double newValue) { set sliderValue(double newValue) {
_sliderValue = newValue; _sliderValue = newValue;
notifyListeners(); notifyListeners();
} }
set relapseCategory(String newValue) {
_relapseCategory = newValue;
notifyListeners();
}
TimeOfDay getTimeEntry(String key) { TimeOfDay getTimeEntry(String key) {
return _times[key] ?? const TimeOfDay(hour: 12, minute: 0); return _times[key] ?? const TimeOfDay(hour: 12, minute: 0);
} }
@ -53,7 +47,7 @@ class InputProvider extends ChangeNotifier {
Future<int> saveRelapse() { Future<int> saveRelapse() {
Relapse relapse = Relapse relapse =
Relapse(_relapseCategory, _textController.text, DateTime.now()); Relapse(relapseCategory, _textController.text, DateTime.now());
_resetFields(); _resetFields();
return globals.databaseService.addRelapse(relapse); return globals.databaseService.addRelapse(relapse);
} }

View File

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class PageProvider extends ChangeNotifier {
bool showForm = false;
void swap() {
showForm = !showForm;
notifyListeners();
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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/models/workout.dart';
import 'package:smoke_cess_app/providers/settings_provider.dart'; import 'package:smoke_cess_app/providers/settings_provider.dart';
@ -15,8 +16,14 @@ class TasksProvider extends ChangeNotifier {
Pages.timer: true, Pages.timer: true,
}; };
List<Mood> moodHistory = [];
List<Sleep> sleepHistory = [];
List<Workout> workoutHistory = [];
List<Relapse> relapseHistory = [];
TasksProvider(SettingsProvider? settingsProvider) { TasksProvider(SettingsProvider? settingsProvider) {
initTasks(); initTasks();
initHistories();
} }
void setTaskDone(Pages taskName) { void setTaskDone(Pages taskName) {
@ -24,13 +31,23 @@ class TasksProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void initHistories() async {
moodHistory = await globals.databaseService.getMoodRecords();
sleepHistory = await globals.databaseService.getSleepRecords();
workoutHistory = await globals.databaseService.getWorkoutRecords();
relapseHistory = await globals.databaseService.getRelapseRecords();
notifyListeners();
}
void initTasks() async { void initTasks() async {
DateTime now = DateTime.now();
TZDateTime? moodToday = await getTodayMood(); TZDateTime? moodToday = await getTodayMood();
if (moodToday != null) { if (moodToday != null) {
List<Mood> moodList = await globals.databaseService.getMoodRecords(); List<Mood> moodList = await globals.databaseService.getMoodRecords();
if (moodList.isNotEmpty) { if (moodList.isNotEmpty) {
Mood mood = moodList.last; Mood mood = moodList.last;
tasks[Pages.mood] = !isSameDay(moodToday, mood.date); tasks[Pages.mood] =
!isSameDay(moodToday, mood.date) && moodToday.isBefore(now);
} }
} else { } else {
tasks[Pages.mood] = false; tasks[Pages.mood] = false;
@ -40,7 +57,8 @@ class TasksProvider extends ChangeNotifier {
List<Sleep> sleepList = await globals.databaseService.getSleepRecords(); List<Sleep> sleepList = await globals.databaseService.getSleepRecords();
if (sleepList.isNotEmpty) { if (sleepList.isNotEmpty) {
Sleep sleep = sleepList.last; Sleep sleep = sleepList.last;
tasks[Pages.sleep] = !isSameDay(sleepToday, sleep.date); tasks[Pages.sleep] =
!isSameDay(sleepToday, sleep.date) && sleepToday.isBefore(now);
} }
} else { } else {
tasks[Pages.sleep] = false; tasks[Pages.sleep] = false;

View File

@ -21,5 +21,14 @@ class TimerProvider extends ChangeNotifier {
void stopTimer() { void stopTimer() {
started = false; started = false;
_timer?.cancel(); _timer?.cancel();
_timer = null;
}
@override
void dispose() {
started = false;
_timer?.cancel();
_timer = null;
super.dispose();
} }
} }

View File

@ -92,11 +92,25 @@ class WorkoutProvider extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void interruptWorkout() {
isWorkoutStarted = false;
isWorkoutComplete = false;
_audioPlayer.stop();
timerProvider.stopTimer();
notifyListeners();
}
void saveWorkout() { void saveWorkout() {
Workout workout = Workout workout =
Workout(motivationBefore, motivationAfter, DateTime.now()); Workout(motivationBefore, motivationAfter, DateTime.now());
globals.databaseService.addWorkout(workout); globals.databaseService.addWorkout(workout);
} }
@override
void dispose() {
interruptWorkout();
super.dispose();
}
} }
Map<String, Map<String, dynamic>> _workoutPhaseSettings = { Map<String, Map<String, dynamic>> _workoutPhaseSettings = {

View File

@ -1,6 +1,8 @@
import 'package:smoke_cess_app/services/settings_service.dart'; import 'package:smoke_cess_app/services/settings_service.dart';
import 'package:timezone/timezone.dart'; import 'package:timezone/timezone.dart';
import 'pages_service.dart';
const int trainingTime = 40; const int trainingTime = 40;
const weekDays = { const weekDays = {
@ -47,6 +49,45 @@ Future<TZDateTime?> getTodaySleep() async {
return today.isNotEmpty ? today.first : null; return today.isNotEmpty ? today.first : null;
} }
Future<Duration> getTimeTillNextMood() async {
List<TZDateTime> moodDates = await getDatesforMood();
Iterable<TZDateTime> nextDate =
moodDates.where((element) => element.isAfter(DateTime.now()));
Duration duration = nextDate.isNotEmpty
? nextDate.first.difference(DateTime.now())
: const Duration(seconds: 0);
return duration;
}
Future<Duration> getTimeTillNextSleep() async {
List<TZDateTime> sleepDates = await getDatesforSleep();
Iterable<TZDateTime> nextDate =
sleepDates.where((element) => element.isAfter(DateTime.now()));
Duration duration = nextDate.isNotEmpty
? nextDate.first.difference(DateTime.now())
: const Duration(seconds: 0);
return duration;
}
Future<Duration> getTimeTillNextWorkout() async {
DateTime now = DateTime.now();
DateTime tomorrow =
DateTime(now.year, now.month, now.day).add(const Duration(days: 1));
Duration duration = tomorrow.difference(now);
return duration;
}
Future<Duration> getTimeTill(Pages page) {
switch (page) {
case Pages.mood:
return getTimeTillNextMood();
case Pages.sleep:
return getTimeTillNextSleep();
default:
return getTimeTillNextWorkout();
}
}
List<TZDateTime> createTZDateTimes( List<TZDateTime> createTZDateTimes(
List<String>? selectedDays, int? selectedHours, int? selectedMinutes) { List<String>? selectedDays, int? selectedHours, int? selectedMinutes) {
final List<TZDateTime> tzDateTimes = []; final List<TZDateTime> tzDateTimes = [];

View File

@ -1,4 +1,14 @@
String formatTime(int seconds) { String formatTime(int seconds) {
Duration duration = Duration(seconds: seconds); Duration duration = Duration(seconds: seconds);
return '${duration.inMinutes.remainder(60).toString().padLeft(2, '0')}:${duration.inSeconds.remainder(60).toString().padLeft(2, '0')}'; String formattedTime = '';
String twoDigits(int n) => n.toString().padLeft(2, "0");
String days = duration.inDays.toString();
String hours = twoDigits(duration.inHours.remainder(24));
String minutes = twoDigits(duration.inMinutes.remainder(60));
String formattedSeconds = twoDigits(duration.inSeconds.remainder(60));
if (duration.inDays != 0) formattedTime += '$days:';
if (duration.inHours != 0) formattedTime += '$hours:';
formattedTime += '$minutes:';
formattedTime += formattedSeconds;
return formattedTime;
} }

View File

@ -9,6 +9,7 @@ class DropDown extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var inputModel = context.watch<InputProvider>(); var inputModel = context.watch<InputProvider>();
inputModel.relapseCategory = _items.isNotEmpty ? _items[0] : '';
return DropdownButtonFormField<String>( return DropdownButtonFormField<String>(
value: _items.isEmpty ? null : _items[0], value: _items.isEmpty ? null : _items[0],
icon: const Icon(Icons.arrow_downward), icon: const Icon(Icons.arrow_downward),

View File

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import '../models/mood.dart';
import '../providers/tasks_provider.dart';
class MoodView extends StatelessWidget {
const MoodView({super.key});
@override
Widget build(BuildContext context) {
var tasksModel = context.watch<TasksProvider>();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SfCartesianChart(
primaryXAxis: DateTimeAxis(),
series: <ChartSeries>[
LineSeries<Mood, DateTime>(
dataSource: tasksModel.moodHistory,
xValueMapper: (Mood value, _) => value.date,
yValueMapper: (Mood value, _) => value.moodValue)
],
),
Column(
children: tasksModel.moodHistory.map((mood) {
return Text('${mood.date}: ${mood.moodValue}');
}).toList())
],
);
}
}

View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/providers/timer_provider.dart';
import 'package:smoke_cess_app/widgets/timer_widget.dart';
import '../services/date_service.dart';
import '../services/pages_service.dart';
void showTaskDonePopup(BuildContext context, Pages page) async {
Duration duration = await getTimeTill(page);
await showDialog(
context: context,
builder: (BuildContext context) {
return ChangeNotifierProvider(
create: (context) => TimerProvider(),
child: TaskDonePopup(
duration: duration,
),
);
},
);
}
class TaskDonePopup extends StatelessWidget {
final Duration duration;
const TaskDonePopup({super.key, required this.duration});
@override
Widget build(BuildContext context) {
TimerProvider timerProvider = context.read<TimerProvider>();
timerProvider.startTimer(duration);
return AlertDialog(
title: const Text('Schon gemacht'),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Nächstes mal wieder:'),
TimerWidget(duration: duration)
]));
}
}

View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/tasks_provider.dart';
class RelapseView extends StatelessWidget {
const RelapseView({super.key});
@override
Widget build(BuildContext context) {
var tasksModel = context.watch<TasksProvider>();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: tasksModel.relapseHistory.map((relapse) {
return Text('${relapse.date}: ${relapse.category}');
}).toList());
}
}

View File

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/models/sleep.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import '../providers/tasks_provider.dart';
class SleepView extends StatelessWidget {
const SleepView({super.key});
@override
Widget build(BuildContext context) {
var tasksModel = context.watch<TasksProvider>();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SfCartesianChart(
primaryXAxis: DateTimeAxis(),
series: <ChartSeries>[
LineSeries<Sleep, DateTime>(
dataSource: tasksModel.sleepHistory,
xValueMapper: (Sleep value, _) => value.date,
yValueMapper: (Sleep value, _) => value.sleepQualitiyValue)
],
),
Column(
children: tasksModel.sleepHistory.map((sleep) {
return Text('${sleep.date}: ${sleep.sleepQualitiyValue}');
}).toList())
],
);
}
}

View File

@ -1,5 +1,7 @@
import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:awesome_dialog/awesome_dialog.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/providers/page_provider.dart';
class SubmitFormButton extends StatelessWidget { class SubmitFormButton extends StatelessWidget {
final Future<int> Function() submitCallback; final Future<int> Function() submitCallback;
@ -9,21 +11,23 @@ class SubmitFormButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
PageProvider pageProvider = context.watch<PageProvider>();
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0), padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
int success = await submitCallback(); int success = await submitCallback();
if (success != 0) { if (success != 0) {
AwesomeDialog( await AwesomeDialog(
context: context, context: context,
dialogType: DialogType.success, dialogType: DialogType.success,
title: 'Gespeichert', title: 'Gespeichert',
desc: 'Der Eintrag wurde erfolgreich gespeichert', desc: 'Der Eintrag wurde erfolgreich gespeichert',
).show(); ).show();
updateTasks(); updateTasks();
pageProvider.swap();
} else { } else {
AwesomeDialog( await AwesomeDialog(
context: context, context: context,
dialogType: DialogType.error, dialogType: DialogType.error,
title: 'Fehler', title: 'Fehler',

View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/services/pages_service.dart';
import '../providers/input_provider.dart';
import '../providers/page_provider.dart';
import '../providers/tasks_provider.dart';
import 'popup_for_task_done.dart';
class ViewFormPage extends StatelessWidget {
final Widget form;
final Widget view;
final Pages page;
const ViewFormPage(
{super.key, required this.form, required this.view, required this.page});
@override
Widget build(BuildContext context) {
PageProvider pageProvider = context.watch<PageProvider>();
TasksProvider tasksProvider = context.watch<TasksProvider>();
return Wrap(children: [
Align(
alignment: Alignment.topLeft,
child: IconButton(
icon: pageProvider.showForm
? const Icon(Icons.arrow_back, color: Colors.black)
: const Icon(Icons.add_outlined, color: Colors.black),
onPressed: tasksProvider.tasks[page] ?? true
? pageProvider.swap
: () => showTaskDonePopup(context, page),
),
),
pageProvider.showForm
? Center(
child: ChangeNotifierProvider(
create: (context) => InputProvider(),
child: form,
))
: Center(child: view)
]);
}
}

View File

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/timer_provider.dart';
import '../providers/workout_provider.dart';
import 'mute_button.dart';
import 'workout_timer_widget.dart';
class WorkoutForm extends StatelessWidget {
const WorkoutForm({super.key});
@override
Widget build(BuildContext context) {
TimerProvider timerProvider = TimerProvider();
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => timerProvider),
ChangeNotifierProvider(
create: (context) => WorkoutProvider(timerProvider)),
],
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Align(
alignment: Alignment.topLeft,
child: MuteButton(),
),
WorkoutTimerWidget()
],
));
}
}

View File

@ -1,7 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:awesome_dialog/awesome_dialog.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/page_provider.dart';
import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/providers/tasks_provider.dart';
import 'package:smoke_cess_app/providers/workout_provider.dart'; import 'package:smoke_cess_app/providers/workout_provider.dart';
import 'package:smoke_cess_app/services/pages_service.dart'; import 'package:smoke_cess_app/services/pages_service.dart';
@ -18,19 +20,29 @@ class WorkoutTimerWidget extends StatelessWidget {
TimerProvider timerProvider = context.watch<TimerProvider>(); TimerProvider timerProvider = context.watch<TimerProvider>();
WorkoutProvider workoutProvider = context.watch<WorkoutProvider>(); WorkoutProvider workoutProvider = context.watch<WorkoutProvider>();
TasksProvider tasksProvider = context.read<TasksProvider>(); TasksProvider tasksProvider = context.read<TasksProvider>();
PageProvider pageProvider = context.read<PageProvider>();
void handleStopWorkout() async {
await showMotivationPopup(context, (double value) {
workoutProvider.motivationAfter = value.toInt();
workoutProvider.saveWorkout();
tasksProvider.setTaskDone(Pages.timer);
}, 'Motivation nach dem Training');
await AwesomeDialog(
context: context,
dialogType: DialogType.success,
title: 'Gespeichert',
desc: 'Der Eintrag wurde erfolgreich gespeichert',
).show();
pageProvider.swap();
}
if (workoutProvider.isPhaseComplete && !workoutProvider.isWorkoutComplete) { if (workoutProvider.isPhaseComplete && !workoutProvider.isWorkoutComplete) {
Timer(const Duration(milliseconds: 1), () => workoutProvider.nextPhase()); Timer(const Duration(milliseconds: 1), () => workoutProvider.nextPhase());
} }
if (workoutProvider.isWorkoutComplete) { if (workoutProvider.isWorkoutComplete) {
Timer( Timer(const Duration(milliseconds: 1), handleStopWorkout);
const Duration(milliseconds: 1),
() => showMotivationPopup(context, (double value) {
workoutProvider.motivationAfter = value.toInt();
workoutProvider.saveWorkout();
tasksProvider.setTaskDone(Pages.timer);
}, 'Motivation nach dem Training'));
} }
void handleStartStopWorkout() { void handleStartStopWorkout() {
@ -40,11 +52,8 @@ class WorkoutTimerWidget extends StatelessWidget {
workoutProvider.startWorkout(); workoutProvider.startWorkout();
}, 'Motivation vor dem Training'); }, 'Motivation vor dem Training');
} else { } else {
workoutProvider.stopWorkout(); workoutProvider.interruptWorkout();
showMotivationPopup( handleStopWorkout();
context,
(double value) => workoutProvider.motivationAfter = value.toInt(),
'Motivation nach dem Training');
} }
} }

View File

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/models/workout.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import '../providers/tasks_provider.dart';
class WorkoutView extends StatelessWidget {
const WorkoutView({super.key});
@override
Widget build(BuildContext context) {
var tasksModel = context.watch<TasksProvider>();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SfCartesianChart(
primaryXAxis: DateTimeAxis(),
series: <ChartSeries>[
LineSeries<Workout, DateTime>(
dataSource: tasksModel.workoutHistory,
xValueMapper: (Workout value, _) => value.date,
yValueMapper: (Workout value, _) => value.motivationBefore),
LineSeries<Workout, DateTime>(
dataSource: tasksModel.workoutHistory,
xValueMapper: (Workout value, _) => value.date,
yValueMapper: (Workout value, _) => value.motivationAfter)
],
),
],
);
}
}

View File

@ -205,6 +205,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.18.0"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -441,6 +448,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
syncfusion_flutter_charts:
dependency: "direct main"
description:
name: syncfusion_flutter_charts
url: "https://pub.dartlang.org"
source: hosted
version: "20.4.52"
syncfusion_flutter_core:
dependency: transitive
description:
name: syncfusion_flutter_core
url: "https://pub.dartlang.org"
source: hosted
version: "20.4.52"
synchronized: synchronized:
dependency: transitive dependency: transitive
description: description:

View File

@ -42,6 +42,7 @@ dependencies:
mobile_scanner: ^3.0.0 mobile_scanner: ^3.0.0
flutter_local_notifications: ^13.0.0 flutter_local_notifications: ^13.0.0
http: ^0.13.5 http: ^0.13.5
syncfusion_flutter_charts: ^20.4.52
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.