diff --git a/lib/main.dart b/lib/main.dart index c5a19b7..4a49cd4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:smoke_cess_app/providers/tasks_provider.dart'; import 'package:smoke_cess_app/services/notification_service.dart'; import 'package:timezone/data/latest.dart' as tz; import 'globals.dart' as globals; +import 'providers/page_provider.dart'; import 'providers/settings_provider.dart'; void main() { @@ -24,18 +25,21 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: _title, - home: MultiProvider( - providers: [ - ChangeNotifierProvider(create: (context) => SettingsProvider()), - ChangeNotifierProxyProvider( - create: (context) => TasksProvider(null), - update: (context, value, TasksProvider? previous) => - TasksProvider(value), - ), - ], - child: const MyHomePage(), + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => SettingsProvider()), + ChangeNotifierProxyProvider( + create: (context) => TasksProvider(null), + update: (context, value, TasksProvider? previous) => + TasksProvider(value), + ), + ChangeNotifierProvider( + create: (context) => PageProvider(), + ), + ], + child: const MaterialApp( + title: _title, + home: MyHomePage(), )); } } diff --git a/lib/models/mood.dart b/lib/models/mood.dart index 7f5e7a6..9d34051 100644 --- a/lib/models/mood.dart +++ b/lib/models/mood.dart @@ -8,6 +8,7 @@ class Mood implements DatabaseRecord { Mood(this._moodValue, this._comment, this._date); DateTime get date => _date; + int get moodValue => _moodValue; @override factory Mood.fromDatabase(Map map) { diff --git a/lib/models/relapse.dart b/lib/models/relapse.dart index 0271d31..1507188 100644 --- a/lib/models/relapse.dart +++ b/lib/models/relapse.dart @@ -7,6 +7,9 @@ class Relapse implements DatabaseRecord { Relapse(this._category, this._comment, this._date); + String get category => _category; + DateTime get date => _date; + @override factory Relapse.fromDatabase(Map map) { DateTime date = DateTime.parse(map['date']); diff --git a/lib/models/sleep.dart b/lib/models/sleep.dart index 74fd6cb..4447be4 100644 --- a/lib/models/sleep.dart +++ b/lib/models/sleep.dart @@ -12,6 +12,7 @@ class Sleep implements DatabaseRecord { this._wokeUpAt); DateTime get date => _date; + int get sleepQualitiyValue => _sleepQualityValue; @override factory Sleep.fromDatabase(Map map) { diff --git a/lib/models/workout.dart b/lib/models/workout.dart index 187b7f6..dd27105 100644 --- a/lib/models/workout.dart +++ b/lib/models/workout.dart @@ -8,6 +8,8 @@ class Workout implements DatabaseRecord { Workout(this._motivationBefore, this._motivationAfter, this._workoutDate); DateTime get date => _workoutDate; + int get motivationBefore => _motivationBefore; + int get motivationAfter => _motivationAfter; @override factory Workout.fromDatabase(Map map) { diff --git a/lib/pages/interval_page.dart b/lib/pages/interval_page.dart index 1c0d64f..5117a8d 100644 --- a/lib/pages/interval_page.dart +++ b/lib/pages/interval_page.dart @@ -1,32 +1,16 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:smoke_cess_app/providers/timer_provider.dart'; -import 'package:smoke_cess_app/providers/workout_provider.dart'; -import 'package:smoke_cess_app/widgets/mute_button.dart'; -import 'package:smoke_cess_app/widgets/workout_timer_widget.dart'; +import 'package:smoke_cess_app/services/pages_service.dart'; +import 'package:smoke_cess_app/widgets/workout_form.dart'; +import 'package:smoke_cess_app/widgets/workout_view.dart'; + +import '../widgets/view_form_page.dart'; class IntervalTimerPage extends StatelessWidget { const IntervalTimerPage({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() - ], - ), - ); + return const ViewFormPage( + form: WorkoutForm(), view: WorkoutView(), page: Pages.timer); } } diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index ce6a1ff..10ff0ec 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -1,6 +1,7 @@ import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.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/services/pages_service.dart'; import 'package:smoke_cess_app/providers/settings_provider.dart'; @@ -19,15 +20,19 @@ class MyHomePageState extends State { bool _isConfigured = false; void _onItemTapped(int index) { + PageProvider pageProvider = context.read(); setState(() { - _isConfigured - ? _selectedIndex = index - : AwesomeDialog( - context: context, - dialogType: DialogType.info, - title: 'Fehlende Konfiguration', - desc: 'Bitte QR Code Scannen!', - ).show(); + if (_isConfigured) { + pageProvider.showForm = false; + _selectedIndex = index; + return; + } + AwesomeDialog( + context: context, + dialogType: DialogType.info, + title: 'Fehlende Konfiguration', + desc: 'Bitte QR Code Scannen!', + ).show(); }); } @@ -40,9 +45,9 @@ class MyHomePageState extends State { appBar: AppBar( title: Text( '${pages.values.elementAt(_selectedIndex)['title']} ${_isConfigured ? "Gruppe ${settingsModel.settings?.group}" : ""}')), - body: Center( - child: SingleChildScrollView( - child: pages.values.elementAt(_selectedIndex)['page'])), + body: SingleChildScrollView( + child: pages.values.elementAt(_selectedIndex)['page'], + ), bottomNavigationBar: NavigationBar( onDestinationSelected: _onItemTapped, selectedIndex: _selectedIndex, diff --git a/lib/pages/mood_page.dart b/lib/pages/mood_page.dart index 546c6f1..8c2447d 100644 --- a/lib/pages/mood_page.dart +++ b/lib/pages/mood_page.dart @@ -1,17 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:smoke_cess_app/providers/input_provider.dart'; +import 'package:smoke_cess_app/services/pages_service.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 { const MoodPage({super.key}); @override Widget build(BuildContext context) { - return Center( - child: ChangeNotifierProvider( - create: (context) => InputProvider(), - child: const MoodForm(), - )); + return const ViewFormPage( + form: MoodForm(), view: MoodView(), page: Pages.mood); } } diff --git a/lib/pages/relapse_page.dart b/lib/pages/relapse_page.dart index 75e007d..e7b6127 100644 --- a/lib/pages/relapse_page.dart +++ b/lib/pages/relapse_page.dart @@ -1,17 +1,15 @@ 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 '../providers/input_provider.dart'; +import 'package:smoke_cess_app/widgets/relapse_view.dart'; +import '../widgets/view_form_page.dart'; class RelapsePage extends StatelessWidget { const RelapsePage({super.key}); @override Widget build(BuildContext context) { - return Center( - child: ChangeNotifierProvider( - create: (context) => InputProvider(), - child: const RelapseForm(), - )); + return const ViewFormPage( + form: RelapseForm(), view: RelapseView(), page: Pages.relapse); } } diff --git a/lib/pages/sleep_page.dart b/lib/pages/sleep_page.dart index 22221f3..3cf3fd5 100644 --- a/lib/pages/sleep_page.dart +++ b/lib/pages/sleep_page.dart @@ -1,17 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:smoke_cess_app/providers/input_provider.dart'; +import 'package:smoke_cess_app/services/pages_service.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 { const SleepPage({super.key}); @override Widget build(BuildContext context) { - return Center( - child: ChangeNotifierProvider( - create: (context) => InputProvider(), - child: const SleepForm(), - )); + return const ViewFormPage( + form: SleepForm(), view: SleepView(), page: Pages.sleep); } } diff --git a/lib/providers/input_provider.dart b/lib/providers/input_provider.dart index a61514f..3bd473f 100644 --- a/lib/providers/input_provider.dart +++ b/lib/providers/input_provider.dart @@ -11,22 +11,16 @@ class InputProvider extends ChangeNotifier { 'wokeUpAt': const TimeOfDay(hour: 8, minute: 0), 'sleptAt': const TimeOfDay(hour: 22, minute: 0), }; - String _relapseCategory = ''; + String relapseCategory = ''; double get sliderValue => _sliderValue; TextEditingController get textController => _textController; - String get relapseCategory => _relapseCategory; set sliderValue(double newValue) { _sliderValue = newValue; notifyListeners(); } - set relapseCategory(String newValue) { - _relapseCategory = newValue; - notifyListeners(); - } - TimeOfDay getTimeEntry(String key) { return _times[key] ?? const TimeOfDay(hour: 12, minute: 0); } @@ -53,7 +47,7 @@ class InputProvider extends ChangeNotifier { Future saveRelapse() { Relapse relapse = - Relapse(_relapseCategory, _textController.text, DateTime.now()); + Relapse(relapseCategory, _textController.text, DateTime.now()); _resetFields(); return globals.databaseService.addRelapse(relapse); } diff --git a/lib/providers/page_provider.dart b/lib/providers/page_provider.dart new file mode 100644 index 0000000..a17ff26 --- /dev/null +++ b/lib/providers/page_provider.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class PageProvider extends ChangeNotifier { + bool showForm = false; + + void swap() { + showForm = !showForm; + notifyListeners(); + } +} diff --git a/lib/providers/tasks_provider.dart b/lib/providers/tasks_provider.dart index f7cd673..d3fc042 100644 --- a/lib/providers/tasks_provider.dart +++ b/lib/providers/tasks_provider.dart @@ -1,4 +1,5 @@ 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/workout.dart'; import 'package:smoke_cess_app/providers/settings_provider.dart'; @@ -15,8 +16,14 @@ class TasksProvider extends ChangeNotifier { Pages.timer: true, }; + List moodHistory = []; + List sleepHistory = []; + List workoutHistory = []; + List relapseHistory = []; + TasksProvider(SettingsProvider? settingsProvider) { initTasks(); + initHistories(); } void setTaskDone(Pages taskName) { @@ -24,13 +31,23 @@ class TasksProvider extends ChangeNotifier { 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 { + DateTime now = DateTime.now(); TZDateTime? moodToday = await getTodayMood(); if (moodToday != null) { List moodList = await globals.databaseService.getMoodRecords(); if (moodList.isNotEmpty) { Mood mood = moodList.last; - tasks[Pages.mood] = !isSameDay(moodToday, mood.date); + tasks[Pages.mood] = + !isSameDay(moodToday, mood.date) && moodToday.isBefore(now); } } else { tasks[Pages.mood] = false; @@ -40,7 +57,8 @@ class TasksProvider extends ChangeNotifier { List sleepList = await globals.databaseService.getSleepRecords(); if (sleepList.isNotEmpty) { Sleep sleep = sleepList.last; - tasks[Pages.sleep] = !isSameDay(sleepToday, sleep.date); + tasks[Pages.sleep] = + !isSameDay(sleepToday, sleep.date) && sleepToday.isBefore(now); } } else { tasks[Pages.sleep] = false; diff --git a/lib/providers/timer_provider.dart b/lib/providers/timer_provider.dart index ee34b19..f355d7d 100644 --- a/lib/providers/timer_provider.dart +++ b/lib/providers/timer_provider.dart @@ -21,5 +21,14 @@ class TimerProvider extends ChangeNotifier { void stopTimer() { started = false; _timer?.cancel(); + _timer = null; + } + + @override + void dispose() { + started = false; + _timer?.cancel(); + _timer = null; + super.dispose(); } } diff --git a/lib/providers/workout_provider.dart b/lib/providers/workout_provider.dart index 5c0a80b..0cf3804 100644 --- a/lib/providers/workout_provider.dart +++ b/lib/providers/workout_provider.dart @@ -92,11 +92,25 @@ class WorkoutProvider extends ChangeNotifier { notifyListeners(); } + void interruptWorkout() { + isWorkoutStarted = false; + isWorkoutComplete = false; + _audioPlayer.stop(); + timerProvider.stopTimer(); + notifyListeners(); + } + void saveWorkout() { Workout workout = Workout(motivationBefore, motivationAfter, DateTime.now()); globals.databaseService.addWorkout(workout); } + + @override + void dispose() { + interruptWorkout(); + super.dispose(); + } } Map> _workoutPhaseSettings = { diff --git a/lib/services/date_service.dart b/lib/services/date_service.dart index b5e254f..79bdd37 100644 --- a/lib/services/date_service.dart +++ b/lib/services/date_service.dart @@ -1,6 +1,8 @@ import 'package:smoke_cess_app/services/settings_service.dart'; import 'package:timezone/timezone.dart'; +import 'pages_service.dart'; + const int trainingTime = 40; const weekDays = { @@ -47,6 +49,45 @@ Future getTodaySleep() async { return today.isNotEmpty ? today.first : null; } +Future getTimeTillNextMood() async { + List moodDates = await getDatesforMood(); + Iterable nextDate = + moodDates.where((element) => element.isAfter(DateTime.now())); + Duration duration = nextDate.isNotEmpty + ? nextDate.first.difference(DateTime.now()) + : const Duration(seconds: 0); + return duration; +} + +Future getTimeTillNextSleep() async { + List sleepDates = await getDatesforSleep(); + Iterable nextDate = + sleepDates.where((element) => element.isAfter(DateTime.now())); + Duration duration = nextDate.isNotEmpty + ? nextDate.first.difference(DateTime.now()) + : const Duration(seconds: 0); + return duration; +} + +Future 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 getTimeTill(Pages page) { + switch (page) { + case Pages.mood: + return getTimeTillNextMood(); + case Pages.sleep: + return getTimeTillNextSleep(); + default: + return getTimeTillNextWorkout(); + } +} + List createTZDateTimes( List? selectedDays, int? selectedHours, int? selectedMinutes) { final List tzDateTimes = []; diff --git a/lib/utils/timer_util.dart b/lib/utils/timer_util.dart index 58d9690..a63030f 100644 --- a/lib/utils/timer_util.dart +++ b/lib/utils/timer_util.dart @@ -1,4 +1,14 @@ String formatTime(int 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; } diff --git a/lib/widgets/drop_down.dart b/lib/widgets/drop_down.dart index 5f06d89..ee704cf 100644 --- a/lib/widgets/drop_down.dart +++ b/lib/widgets/drop_down.dart @@ -9,6 +9,7 @@ class DropDown extends StatelessWidget { @override Widget build(BuildContext context) { var inputModel = context.watch(); + inputModel.relapseCategory = _items.isNotEmpty ? _items[0] : ''; return DropdownButtonFormField( value: _items.isEmpty ? null : _items[0], icon: const Icon(Icons.arrow_downward), diff --git a/lib/widgets/mood_view.dart b/lib/widgets/mood_view.dart new file mode 100644 index 0000000..14884b9 --- /dev/null +++ b/lib/widgets/mood_view.dart @@ -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(); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SfCartesianChart( + primaryXAxis: DateTimeAxis(), + series: [ + LineSeries( + 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()) + ], + ); + } +} diff --git a/lib/widgets/popup_for_task_done.dart b/lib/widgets/popup_for_task_done.dart new file mode 100644 index 0000000..fe63907 --- /dev/null +++ b/lib/widgets/popup_for_task_done.dart @@ -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.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) + ])); + } +} diff --git a/lib/widgets/relapse_view.dart b/lib/widgets/relapse_view.dart new file mode 100644 index 0000000..48c9829 --- /dev/null +++ b/lib/widgets/relapse_view.dart @@ -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(); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: tasksModel.relapseHistory.map((relapse) { + return Text('${relapse.date}: ${relapse.category}'); + }).toList()); + } +} diff --git a/lib/widgets/sleep_view.dart b/lib/widgets/sleep_view.dart new file mode 100644 index 0000000..90a005c --- /dev/null +++ b/lib/widgets/sleep_view.dart @@ -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(); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SfCartesianChart( + primaryXAxis: DateTimeAxis(), + series: [ + LineSeries( + 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()) + ], + ); + } +} diff --git a/lib/widgets/submit_form_button.dart b/lib/widgets/submit_form_button.dart index b5bb593..4c98c34 100644 --- a/lib/widgets/submit_form_button.dart +++ b/lib/widgets/submit_form_button.dart @@ -1,5 +1,7 @@ import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/page_provider.dart'; class SubmitFormButton extends StatelessWidget { final Future Function() submitCallback; @@ -9,21 +11,23 @@ class SubmitFormButton extends StatelessWidget { @override Widget build(BuildContext context) { + PageProvider pageProvider = context.watch(); return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: ElevatedButton( onPressed: () async { int success = await submitCallback(); if (success != 0) { - AwesomeDialog( + await AwesomeDialog( context: context, dialogType: DialogType.success, title: 'Gespeichert', desc: 'Der Eintrag wurde erfolgreich gespeichert', ).show(); updateTasks(); + pageProvider.swap(); } else { - AwesomeDialog( + await AwesomeDialog( context: context, dialogType: DialogType.error, title: 'Fehler', diff --git a/lib/widgets/view_form_page.dart b/lib/widgets/view_form_page.dart new file mode 100644 index 0000000..57b9271 --- /dev/null +++ b/lib/widgets/view_form_page.dart @@ -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(); + TasksProvider tasksProvider = context.watch(); + 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) + ]); + } +} diff --git a/lib/widgets/workout_form.dart b/lib/widgets/workout_form.dart new file mode 100644 index 0000000..a176cd0 --- /dev/null +++ b/lib/widgets/workout_form.dart @@ -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() + ], + )); + } +} diff --git a/lib/widgets/workout_timer_widget.dart b/lib/widgets/workout_timer_widget.dart index a85a09e..10f3033 100644 --- a/lib/widgets/workout_timer_widget.dart +++ b/lib/widgets/workout_timer_widget.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.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/workout_provider.dart'; import 'package:smoke_cess_app/services/pages_service.dart'; @@ -18,19 +20,29 @@ class WorkoutTimerWidget extends StatelessWidget { TimerProvider timerProvider = context.watch(); WorkoutProvider workoutProvider = context.watch(); TasksProvider tasksProvider = context.read(); + PageProvider pageProvider = context.read(); + + 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) { 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(); - tasksProvider.setTaskDone(Pages.timer); - }, 'Motivation nach dem Training')); + Timer(const Duration(milliseconds: 1), handleStopWorkout); } void handleStartStopWorkout() { @@ -40,11 +52,8 @@ class WorkoutTimerWidget extends StatelessWidget { workoutProvider.startWorkout(); }, 'Motivation vor dem Training'); } else { - workoutProvider.stopWorkout(); - showMotivationPopup( - context, - (double value) => workoutProvider.motivationAfter = value.toInt(), - 'Motivation nach dem Training'); + workoutProvider.interruptWorkout(); + handleStopWorkout(); } } diff --git a/lib/widgets/workout_view.dart b/lib/widgets/workout_view.dart new file mode 100644 index 0000000..d0deb4d --- /dev/null +++ b/lib/widgets/workout_view.dart @@ -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(); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SfCartesianChart( + primaryXAxis: DateTimeAxis(), + series: [ + LineSeries( + dataSource: tasksModel.workoutHistory, + xValueMapper: (Workout value, _) => value.date, + yValueMapper: (Workout value, _) => value.motivationBefore), + LineSeries( + dataSource: tasksModel.workoutHistory, + xValueMapper: (Workout value, _) => value.date, + yValueMapper: (Workout value, _) => value.motivationAfter) + ], + ), + ], + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 98b299a..c3f17f4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -205,6 +205,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.2" + intl: + dependency: transitive + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.18.0" js: dependency: transitive description: @@ -441,6 +448,20 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 473cf66..b3efde8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,7 @@ dependencies: mobile_scanner: ^3.0.0 flutter_local_notifications: ^13.0.0 http: ^0.13.5 + syncfusion_flutter_charts: ^20.4.52 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.