diff --git a/lib/globals.dart b/lib/globals.dart new file mode 100644 index 0000000..58229d9 --- /dev/null +++ b/lib/globals.dart @@ -0,0 +1,7 @@ +library app.globals; + +import 'package:smoke_cess_app/mock/db_mock.dart'; +import 'package:smoke_cess_app/services/database_service.dart'; + +DatabaseService databaseService = DatabaseMock(); +// DatabaseService databaseService = DatabaseService.instance; diff --git a/lib/main.dart b/lib/main.dart index bf9da73..a1cf5ac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,14 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:smoke_cess_app/pages/main_page.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; -import 'package:smoke_cess_app/service/notification_service.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/settings_provider.dart'; void main() { // to ensure all the widgets are initialized. WidgetsFlutterBinding.ensureInitialized(); //init database - DatabaseService.instance; + globals.databaseService; tz.initializeTimeZones(); NotificationService().initNotification(); runApp(const MyApp()); @@ -21,6 +23,11 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp(title: _title, home: MyHomePage()); + return MaterialApp( + title: _title, + home: ChangeNotifierProvider( + create: (context) => SettingsProvider(), + child: const MyHomePage(), + )); } } diff --git a/lib/mock/db_mock.dart b/lib/mock/db_mock.dart index 1a92cf9..e7c60c3 100644 --- a/lib/mock/db_mock.dart +++ b/lib/mock/db_mock.dart @@ -1,23 +1,56 @@ import 'package:smoke_cess_app/interface/db_record.dart'; import 'package:smoke_cess_app/models/mood.dart'; +import 'package:smoke_cess_app/models/relapse.dart'; import 'package:smoke_cess_app/models/sleep.dart'; +import 'package:smoke_cess_app/services/database_service.dart'; +import 'package:sqflite_common/sqlite_api.dart'; -class DatabaseMock { - final List _moodRecords = []; - final List _sleepRecords = []; +class DatabaseMock implements DatabaseService { + static final DatabaseMock _databaseMock = DatabaseMock._internal(); + factory DatabaseMock() { + return _databaseMock; + } + DatabaseMock._internal(); + + final List _moodRecords = []; + final List _sleepRecords = []; + final List _relapseRecords = []; final List _workoutRecords = []; - void saveRecord(DatabaseRecord record) { - if (record is Mood) { - _moodRecords.add(record); - } else if (record is Sleep) { - _sleepRecords.add(record); - } else { - _workoutRecords.add(record); - } + @override + Future addMood(Mood mood) { + _moodRecords.add(mood); + return Future.value(1); } - List getMoodRecords() => _moodRecords; - List getSleepRecords() => _sleepRecords; - List getWorkoutRecords() => _workoutRecords; + @override + Future addSleep(Sleep sleep) { + _sleepRecords.add(sleep); + return Future.value(1); + } + + @override + Future addRelapse(Relapse relapse) { + _relapseRecords.add(relapse); + return Future.value(1); + } + + @override + // TODO: implement database + Future get database => DatabaseService.instance.database; + + @override + Future> getMoodRecords() { + return Future.value(_moodRecords); + } + + @override + Future> getSleepRecords() { + return Future.value(_sleepRecords); + } + + @override + Future> getRelapseRecords() { + return Future.value(_relapseRecords); + } } diff --git a/lib/models/relapse.dart b/lib/models/relapse.dart new file mode 100644 index 0000000..0271d31 --- /dev/null +++ b/lib/models/relapse.dart @@ -0,0 +1,29 @@ +import 'package:smoke_cess_app/interface/db_record.dart'; + +class Relapse implements DatabaseRecord { + final String _category; + final String _comment; + final DateTime _date; + + Relapse(this._category, this._comment, this._date); + + @override + factory Relapse.fromDatabase(Map map) { + DateTime date = DateTime.parse(map['date']); + return Relapse(map['category'], map['comment'], date); + } + + @override + String toCSV() { + return "${_date.toIso8601String()}, $_category, $_comment"; + } + + @override + Map toMap() { + return { + 'category': _category, + 'comment': _comment, + 'date': _date.toIso8601String(), + }; + } +} diff --git a/lib/models/settings.dart b/lib/models/settings.dart index 6691e91..83fad43 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -1,10 +1,10 @@ -import 'package:smoke_cess_app/service/json_service.dart'; +import 'package:smoke_cess_app/services/json_service.dart'; class Settings { final int group; final List? relapseCategories; - final QueryConfig moodQuery; - final QueryConfig sleepQuery; + final QueryConfig? moodQuery; + final QueryConfig? sleepQuery; final TimeConfig? chessTime; Settings(this.group, this.relapseCategories, this.moodQuery, this.sleepQuery, @@ -21,8 +21,8 @@ class Settings { } class QueryConfig { - final int hours; - final int minutes; + final int? hours; + final int? minutes; final List? days; QueryConfig(this.hours, this.minutes, this.days); @@ -34,8 +34,8 @@ class QueryConfig { } class TimeConfig { - final int hours; - final int minutes; + final int? hours; + final int? minutes; TimeConfig(this.hours, this.minutes); diff --git a/lib/models/sleep.dart b/lib/models/sleep.dart index d5335a8..3474b03 100644 --- a/lib/models/sleep.dart +++ b/lib/models/sleep.dart @@ -5,25 +5,25 @@ class Sleep implements DatabaseRecord { final int _sleepQualityValue; final String _comment; final DateTime _date; - final TimeOfDay _sleepedAt; + final TimeOfDay _sleptAt; final TimeOfDay _wokeUpAt; - Sleep(this._sleepQualityValue, this._comment, this._date, this._sleepedAt, + Sleep(this._sleepQualityValue, this._comment, this._date, this._sleptAt, this._wokeUpAt); @override factory Sleep.fromDatabase(Map map) { DateTime date = DateTime.parse(map['date']); - TimeOfDay sleepedAt = - TimeOfDay(hour: map['sleepedAtHour'], minute: map['sleepedAtMinute']); + TimeOfDay sleptAt = + TimeOfDay(hour: map['sleptAtHour'], minute: map['sleptAtMinute']); TimeOfDay wokeUpAt = TimeOfDay(hour: map['wokeUpAtHour'], minute: map['wokeUpAtMinute']); - return Sleep(map['value'], map['comment'], date, sleepedAt, wokeUpAt); + return Sleep(map['value'], map['comment'], date, sleptAt, wokeUpAt); } @override String toCSV() { - return "${_date.toIso8601String()}, $_sleepQualityValue, ${_sleepedAt.hour}:${_sleepedAt.minute}, ${_wokeUpAt.hour}:${_wokeUpAt.minute}, $_comment"; + return "${_date.toIso8601String()}, $_sleepQualityValue, ${_sleptAt.hour}:${_sleptAt.minute}, ${_wokeUpAt.hour}:${_wokeUpAt.minute}, $_comment"; } @override @@ -32,8 +32,8 @@ class Sleep implements DatabaseRecord { 'value': _sleepQualityValue, 'comment': _comment, 'date': _date.toIso8601String(), - 'sleepedAtHour': _sleepedAt.hour, - 'sleepedAtMinute': _sleepedAt.minute, + 'sleptAtHour': _sleptAt.hour, + 'sleptAtMinute': _sleptAt.minute, 'wokeUpAtHour': _wokeUpAt.hour, 'wokeUpAtMinute': _wokeUpAt.minute, }; diff --git a/lib/pages/interval_page.dart b/lib/pages/interval_page.dart index 619d969..f6d2fcd 100644 --- a/lib/pages/interval_page.dart +++ b/lib/pages/interval_page.dart @@ -1,7 +1,12 @@ import 'dart:async'; import 'package:audioplayers/audioplayers.dart'; 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/popup_for_start_and_stop.dart'; +import 'package:smoke_cess_app/widgets/timer_widget.dart'; + +import '../providers/input_provider.dart'; class IntervalTimerPage extends StatefulWidget { const IntervalTimerPage({Key? key}) : super(key: key); @@ -44,9 +49,11 @@ class _IntervalTimerPageState extends State { await showDialog( context: context, builder: (BuildContext context) { - return const TimerStartStopPopup( - title: 'Motivation vor dem Training', - ); + return ChangeNotifierProvider( + create: (context) => InputProvider(), + child: const TimerStartStopPopup( + title: 'Motivation vor dem Training', + )); }, ); _isPaused = false; @@ -161,51 +168,58 @@ class _IntervalTimerPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( - body: Center( + return ChangeNotifierProvider( + create: (context) => TimerProvider(), + child: TimerWidget( + duration: Duration(seconds: 5), + )); + return Center( + child: ChangeNotifierProvider( + create: (context) => InputProvider(), child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - _currentBlock == 0 - ? 'Warm-up' - : _currentBlock % 2 == 1 - ? '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(); - } - }, - ), - // ), - ], - ), - ], - ))); + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + _currentBlock == 0 + ? 'Warm-up' + : _currentBlock % 2 == 1 + ? '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(); + } + }, + ), + // ), + ], + ), + ], + ))); } } diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart index ca3c95d..b45b479 100644 --- a/lib/pages/main_page.dart +++ b/lib/pages/main_page.dart @@ -1,11 +1,8 @@ +import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; -import 'package:smoke_cess_app/pages/mood_page.dart'; -import 'package:smoke_cess_app/pages/relapse_page.dart'; -import 'package:smoke_cess_app/pages/scanner_page.dart'; -import 'package:smoke_cess_app/pages/sleep_page.dart'; -import 'package:smoke_cess_app/pages/interval_page.dart'; -import 'package:smoke_cess_app/service/settings_service.dart'; -import 'package:smoke_cess_app/widgets/missing_config_popup.dart'; +import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/services/pages_service.dart'; +import 'package:smoke_cess_app/providers/settings_provider.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @@ -16,73 +13,42 @@ class MyHomePage extends StatefulWidget { class MyHomePageState extends State { int _selectedIndex = 4; - int? _gruppe; + bool _isConfigured = false; - final List _titles = [ - 'Stimmung', - 'Schlaf', - 'Timer', - 'Rückfall', - 'Scanner' - ]; - static const List _widgetOptions = [ - MoodPage(), - SleepPage(), - IntervalTimerPage(), - RelapsePage(), - ScannerPage(), - ]; - - Future _onItemTapped(int index) async { - _gruppe = await getGroup(); - bool isConfigured = _gruppe != null; + void _onItemTapped(int index) { setState(() { - isConfigured + _isConfigured ? _selectedIndex = index - : showDialog( + : AwesomeDialog( context: context, - builder: (BuildContext context) { - return const MissingConfigPopup( - title: 'Fehlende Konfiguration', - text: 'Bitte QR Code Scannen!', - ); - }); + dialogType: DialogType.info, + title: 'Fehlende Konfiguration', + desc: 'Bitte QR Code Scannen!', + ).show(); }); } @override Widget build(BuildContext context) { + var settingsModel = context.watch(); + var group = settingsModel.settings?.group; + _isConfigured = settingsModel.initialized; return Scaffold( appBar: AppBar( title: Text( - '${_titles[_selectedIndex]} ${_gruppe != null ? "Gruppe $_gruppe" : ""}')), - body: _widgetOptions.elementAt(_selectedIndex), + '${pages.keys.elementAt(_selectedIndex)} ${_isConfigured ? "Gruppe $group" : ""}')), + body: Center( + child: SingleChildScrollView( + child: pages.values.elementAt(_selectedIndex)['page'])), bottomNavigationBar: NavigationBar( - onDestinationSelected: _onItemTapped, - selectedIndex: _selectedIndex, - destinations: const [ - NavigationDestination( - icon: Icon(Icons.mood_outlined, color: Colors.black), - label: 'Stimmung'), - NavigationDestination( - icon: Icon(Icons.bedtime_outlined, color: Colors.black), - label: 'Schlaf'), - NavigationDestination( - icon: Icon( - Icons.timer_outlined, - color: Colors.black, - ), - label: 'Timer'), - NavigationDestination( - icon: Icon(Icons.smoke_free_outlined, color: Colors.black), - label: 'Rückfall'), - NavigationDestination( - icon: Icon(Icons.camera_alt_outlined, color: Colors.black), - label: 'Settings'), - ], - - //onTap: _onItemTapped, - ), + onDestinationSelected: _onItemTapped, + selectedIndex: _selectedIndex, + destinations: pages.keys.map((key) { + return NavigationDestination( + icon: pages[key]!['icon'] ?? + const Icon(Icons.disabled_by_default), + label: key); + }).toList()), ); } } diff --git a/lib/pages/mood_page.dart b/lib/pages/mood_page.dart index bd1b36b..546c6f1 100644 --- a/lib/pages/mood_page.dart +++ b/lib/pages/mood_page.dart @@ -1,4 +1,6 @@ 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/mood_form.dart'; class MoodPage extends StatelessWidget { @@ -6,8 +8,10 @@ class MoodPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center( - child: MoodForm(), - ); + return Center( + child: ChangeNotifierProvider( + create: (context) => InputProvider(), + child: const MoodForm(), + )); } } diff --git a/lib/pages/relapse_page.dart b/lib/pages/relapse_page.dart index 7710e10..75e007d 100644 --- a/lib/pages/relapse_page.dart +++ b/lib/pages/relapse_page.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/widgets/relapse_form.dart'; +import '../providers/input_provider.dart'; class RelapsePage extends StatelessWidget { const RelapsePage({super.key}); @override Widget build(BuildContext context) { - return const Center(child: Text('Hier werden Rückfälle dokumentiert')); + return Center( + child: ChangeNotifierProvider( + create: (context) => InputProvider(), + child: const RelapseForm(), + )); } } diff --git a/lib/pages/scanner_page.dart b/lib/pages/scanner_page.dart index c382990..29cf8a7 100644 --- a/lib/pages/scanner_page.dart +++ b/lib/pages/scanner_page.dart @@ -1,24 +1,45 @@ +import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; -import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:provider/provider.dart'; import 'package:smoke_cess_app/models/mood.dart'; -import 'package:smoke_cess_app/models/settings.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; -import 'package:smoke_cess_app/service/json_service.dart'; -import 'package:smoke_cess_app/service/settings_service.dart'; -import 'package:smoke_cess_app/service/notification_service.dart'; - +import 'package:smoke_cess_app/models/relapse.dart'; +import 'package:smoke_cess_app/services/settings_service.dart'; +import 'package:smoke_cess_app/services/notification_service.dart'; +import 'package:smoke_cess_app/widgets/scanner.dart'; import '../models/sleep.dart'; -import '../widgets/missing_config_popup.dart'; +import '../providers/settings_provider.dart'; +import '../globals.dart' as globals; -class ScannerPage extends StatefulWidget { +class ScannerPage extends StatelessWidget { const ScannerPage({super.key}); - @override - State createState() => ScannerPageState(); -} + void export() async { + List moods = await globals.databaseService.getMoodRecords(); + List sleeps = await globals.databaseService.getSleepRecords(); + List relapses = await globals.databaseService.getRelapseRecords(); + for (Mood mood in moods) { + print(mood.toCSV()); + } + for (Sleep sleep in sleeps) { + print(sleep.toCSV()); + } + for (Relapse relapse in relapses) { + print(relapse.toCSV()); + } + } -class ScannerPageState extends State { - bool scanning = false; + void loadJSON(BuildContext context) async { + var settingsModel = context.read(); + await loadSettingsFromLocalJSON(); + settingsModel.initSettings(); + NotificationService().setAllNotifications(); + AwesomeDialog( + context: context, + dialogType: DialogType.success, + title: 'Geschafft', + desc: 'Die Einstellung wurden erfolgreich gespeichert', + ).show(); + } @override Widget build(BuildContext context) { @@ -26,70 +47,19 @@ class ScannerPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - scanning - ? Expanded( - child: MobileScanner( - fit: BoxFit.contain, - controller: MobileScannerController( - detectionTimeoutMs: 2000, - ), - onDetect: (capture) { - //TODO Errorhandling!! - final List barcodes = capture.barcodes; - for (final barcode in barcodes) { - if (barcode.rawValue != null) { - String qrText = barcode.rawValue!; - Map json = stringToJSON(qrText); - Settings settings = Settings.fromJson(json); - saveSettings(settings); - setState(() { - scanning = false; - showDialog( - context: context, - builder: (BuildContext context) { - return MissingConfigPopup( - title: 'Konfiguration erfolgreich', - text: 'Du gehörst zu Gruppe ${settings.group}', - ); - }); - }); - } - } - }, - )) - : ElevatedButton( - style: ElevatedButton.styleFrom( - textStyle: const TextStyle(fontSize: 20)), - onPressed: () { - setState(() => scanning = true); - }, - child: const Text('Scan QR Code'), - ), + const MyScanner(), const SizedBox(height: 30), ElevatedButton( style: ElevatedButton.styleFrom( textStyle: const TextStyle(fontSize: 20)), - onPressed: () { - loadSettingsFromLocalJSON(); - NotificationService().setAllNotifications(); - }, + onPressed: () => loadJSON(context), child: const Text('Read JSON'), ), const SizedBox(height: 30), ElevatedButton( style: ElevatedButton.styleFrom( textStyle: const TextStyle(fontSize: 20)), - onPressed: () async { - List moods = await DatabaseService.instance.getMoodRecords(); - List sleeps = - await DatabaseService.instance.getSleepRecords(); - for (Mood mood in moods) { - print(mood.toCSV()); - } - for (Sleep sleep in sleeps) { - print(sleep.toCSV()); - } - }, + onPressed: export, child: const Text('Export'), ) ], diff --git a/lib/pages/sleep_page.dart b/lib/pages/sleep_page.dart index e736f44..22221f3 100644 --- a/lib/pages/sleep_page.dart +++ b/lib/pages/sleep_page.dart @@ -1,4 +1,6 @@ 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/sleep_form.dart'; class SleepPage extends StatelessWidget { @@ -6,6 +8,10 @@ class SleepPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center(child: SleepForm()); + return Center( + child: ChangeNotifierProvider( + create: (context) => InputProvider(), + child: const SleepForm(), + )); } } diff --git a/lib/providers/input_provider.dart b/lib/providers/input_provider.dart new file mode 100644 index 0000000..a61514f --- /dev/null +++ b/lib/providers/input_provider.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:smoke_cess_app/models/mood.dart'; +import 'package:smoke_cess_app/models/relapse.dart'; +import 'package:smoke_cess_app/models/sleep.dart'; +import '../globals.dart' as globals; + +class InputProvider extends ChangeNotifier { + double _sliderValue = 50; + final TextEditingController _textController = TextEditingController(text: ''); + final Map _times = { + 'wokeUpAt': const TimeOfDay(hour: 8, minute: 0), + 'sleptAt': const TimeOfDay(hour: 22, minute: 0), + }; + 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); + } + + void setTime(String key, TimeOfDay time) { + _times[key] = time; + notifyListeners(); + } + + void _resetFields() { + _sliderValue = 50; + _textController.text = ''; + setTime('wokeUpAt', const TimeOfDay(hour: 8, minute: 0)); + setTime('sleptAt', const TimeOfDay(hour: 22, minute: 0)); + notifyListeners(); + } + + Future saveMood() { + Mood mood = + Mood(_sliderValue.toInt(), _textController.text, DateTime.now()); + _resetFields(); + return globals.databaseService.addMood(mood); + } + + Future saveRelapse() { + Relapse relapse = + Relapse(_relapseCategory, _textController.text, DateTime.now()); + _resetFields(); + return globals.databaseService.addRelapse(relapse); + } + + Future saveSleep(String wokeUpKey, String sleptKey) { + Sleep sleep = Sleep(_sliderValue.toInt(), _textController.text, + DateTime.now(), getTimeEntry(sleptKey), getTimeEntry(wokeUpKey)); + _resetFields(); + return globals.databaseService.addSleep(sleep); + } +} diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart new file mode 100644 index 0000000..f51c5e1 --- /dev/null +++ b/lib/providers/settings_provider.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:smoke_cess_app/services/settings_service.dart'; + +import '../models/settings.dart'; + +class SettingsProvider extends ChangeNotifier { + Settings? _settings; + bool _initialized = false; + + Settings? get settings => _settings; + bool get initialized => _initialized; + + SettingsProvider() { + initSettings(); + } + + void initSettings() async { + _settings = await loadSettings(); + _initialized = _settings != null ? true : false; + notifyListeners(); + } +} diff --git a/lib/providers/timer_provider.dart b/lib/providers/timer_provider.dart new file mode 100644 index 0000000..0601b39 --- /dev/null +++ b/lib/providers/timer_provider.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class TimerProvider extends ChangeNotifier { + Timer? _timer; + bool started = false; + int get elapsedSeconds => _timer != null ? _timer!.tick : 0; + + void startTimer(Duration duration) { + started = true; + print('starting timer'); + _timer = Timer.periodic(const Duration(seconds: 1), ((timer) { + if (timer.tick >= duration.inSeconds) { + timer.cancel(); + started = false; + } + notifyListeners(); + })); + } + + void stopTimer() => _timer?.cancel(); +} diff --git a/lib/service/query_service.dart b/lib/service/query_service.dart deleted file mode 100644 index fc4f5a3..0000000 --- a/lib/service/query_service.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:smoke_cess_app/interface/db_record.dart'; -import 'package:smoke_cess_app/mock/db_mock.dart'; - -class QueryService { - final DatabaseMock _database; - - QueryService(this._database); - - void addRecord(DatabaseRecord record) => _database.saveRecord(record); - List getWorkoutRecords() => _database.getWorkoutRecords(); - List getMoodRecords() => _database.getMoodRecords(); - List getSleepRecords() => _database.getSleepRecords(); - - String recordsToCSV(List records) { - String csv = ""; - for (DatabaseRecord record in records) { - csv += '${record.toCSV()}\n'; - } - return csv; - } -} diff --git a/lib/service/database_service.dart b/lib/services/database_service.dart similarity index 83% rename from lib/service/database_service.dart rename to lib/services/database_service.dart index 26973f4..030a5af 100644 --- a/lib/service/database_service.dart +++ b/lib/services/database_service.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:path/path.dart'; import 'package:smoke_cess_app/models/mood.dart'; +import 'package:smoke_cess_app/models/relapse.dart'; import 'package:sqflite/sqflite.dart'; // ignore: depend_on_referenced_packages import 'package:path_provider/path_provider.dart'; @@ -53,6 +54,15 @@ class DatabaseService { return sleepList; } + Future> getRelapseRecords() async { + Database db = await instance.database; + var relapseRecords = await db.query('relapse'); + List relapseList = relapseRecords.isNotEmpty + ? relapseRecords.map((e) => Relapse.fromDatabase(e)).toList() + : []; + return relapseList; + } + Future addMood(Mood mood) async { Database db = await instance.database; return await db.insert('mood', mood.toMap()); @@ -62,6 +72,11 @@ class DatabaseService { Database db = await instance.database; return await db.insert('sleep', sleep.toMap()); } + + Future addRelapse(Relapse relapse) async { + Database db = await instance.database; + return await db.insert('relapse', relapse.toMap()); + } } String _createMoodTable = ''' @@ -79,8 +94,8 @@ String _createSleepTable = ''' value INTEGER, date TEXT, comment TEXT, - sleepedAtHour INTEGER, - sleepedAtMinute INTEGER, + sleptAtHour INTEGER, + sleptAtMinute INTEGER, wokeUpAtHour INTEGER, wokeUpAtMinute INTEGER ) diff --git a/lib/service/date_service.dart b/lib/services/date_service.dart similarity index 83% rename from lib/service/date_service.dart rename to lib/services/date_service.dart index b422509..0fd108e 100644 --- a/lib/service/date_service.dart +++ b/lib/services/date_service.dart @@ -1,4 +1,4 @@ -import 'package:smoke_cess_app/service/settings_service.dart'; +import 'package:smoke_cess_app/services/settings_service.dart'; import 'package:timezone/timezone.dart'; const int trainingTime = 40; @@ -13,15 +13,6 @@ const weekDays = { "Sonntag": 7, }; -Future> getDatesforAll() async { - List allDates = []; - List moodDates = await getDatesforMood(); - List sleepDates = await getDatesforSleep(); - allDates.addAll(moodDates); - allDates.addAll(sleepDates); - return allDates; -} - Future> getDatesforMood() async { final List? selectedDays = await getMoodQueryDaysCategories(); final int? selectedHours = await getMoodQueryHours(); diff --git a/lib/service/json_service.dart b/lib/services/json_service.dart similarity index 100% rename from lib/service/json_service.dart rename to lib/services/json_service.dart diff --git a/lib/service/notification_service.dart b/lib/services/notification_service.dart similarity index 79% rename from lib/service/notification_service.dart rename to lib/services/notification_service.dart index 2f564ce..2de386c 100644 --- a/lib/service/notification_service.dart +++ b/lib/services/notification_service.dart @@ -1,5 +1,5 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:smoke_cess_app/service/date_service.dart'; +import 'package:smoke_cess_app/services/date_service.dart'; import 'package:timezone/timezone.dart'; class NotificationService { @@ -36,29 +36,6 @@ class NotificationService { await flutterLocalNotificationsPlugin.initialize(initializationSettings); } - Future showNotification() async { - await flutterLocalNotificationsPlugin.show( - 0, - 'test', - 'test', -//schedule the notification to show after 2 seconds. - const NotificationDetails( - // Android details - android: AndroidNotificationDetails('main_channel', 'Main Channel', - channelDescription: "ashwin", - importance: Importance.max, - priority: Priority.max), - // iOS details - iOS: DarwinNotificationDetails( - sound: 'default.wav', - presentAlert: true, - presentBadge: true, - presentSound: true, - ), - ), - ); - } - Future setAllNotifications() async { List moodDates = await getDatesforMood(); List sleepDates = await getDatesforSleep(); diff --git a/lib/services/pages_service.dart b/lib/services/pages_service.dart new file mode 100644 index 0000000..8f3ab37 --- /dev/null +++ b/lib/services/pages_service.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import '../pages/interval_page.dart'; +import '../pages/mood_page.dart'; +import '../pages/relapse_page.dart'; +import '../pages/scanner_page.dart'; +import '../pages/sleep_page.dart'; + +const pages = { + 'Stimmung': { + 'page': MoodPage(), + 'icon': Icon(Icons.mood_outlined, color: Colors.black) + }, + 'Schlaf': { + 'page': SleepPage(), + 'icon': Icon(Icons.bedtime_outlined, color: Colors.black) + }, + 'Timer': { + 'page': IntervalTimerPage(), + 'icon': Icon(Icons.timer_outlined, color: Colors.black) + }, + 'Rückfall': { + 'page': RelapsePage(), + 'icon': Icon(Icons.smoke_free_outlined, color: Colors.black), + }, + 'Scanner': { + 'page': ScannerPage(), + 'icon': Icon(Icons.camera_alt_outlined, color: Colors.black) + }, +}; diff --git a/lib/service/settings_service.dart b/lib/services/settings_service.dart similarity index 61% rename from lib/service/settings_service.dart rename to lib/services/settings_service.dart index dca2839..8c7e7a7 100644 --- a/lib/service/settings_service.dart +++ b/lib/services/settings_service.dart @@ -1,6 +1,6 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:smoke_cess_app/models/settings.dart'; -import 'package:smoke_cess_app/service/json_service.dart'; +import 'package:smoke_cess_app/services/json_service.dart'; //access group setting which was saved in local storage Future getGroup() => _getIntSetting('group'); @@ -54,14 +54,37 @@ Future loadSettingsFromLocalJSON() async { void saveSettings(Settings settings) { _setIntSetting('group', settings.group); _setStringListSetting('relapse_categories', settings.relapseCategories!); - _setStringListSetting('mood_query_days', settings.moodQuery.days!); - _setIntSetting('mood_query_hours', settings.moodQuery.hours); - _setIntSetting('mood_query_minutes', settings.moodQuery.minutes); - _setStringListSetting('sleep_query_days', settings.sleepQuery.days!); - _setIntSetting('sleep_query_hours', settings.sleepQuery.hours); - _setIntSetting('sleep_query_minutes', settings.sleepQuery.minutes); + _setStringListSetting('mood_query_days', settings.moodQuery!.days!); + _setIntSetting('mood_query_hours', settings.moodQuery!.hours!); + _setIntSetting('mood_query_minutes', settings.moodQuery!.minutes!); + _setStringListSetting('sleep_query_days', settings.sleepQuery!.days!); + _setIntSetting('sleep_query_hours', settings.sleepQuery!.hours!); + _setIntSetting('sleep_query_minutes', settings.sleepQuery!.minutes!); if (settings.chessTime != null) { - _setIntSetting('chess_hours', settings.chessTime!.hours); - _setIntSetting('chess_minutes', settings.chessTime!.minutes); + _setIntSetting('chess_hours', settings.chessTime!.hours!); + _setIntSetting('chess_minutes', settings.chessTime!.minutes!); } } + +Future loadSettings() async { + int? group = await getGroup(); + List? relapseCategories = await getRelapseCategories(); + int? moodHours = await getMoodQueryHours(); + int? moodMinutes = await getMoodQueryMinutes(); + List? moodDays = await getMoodQueryDaysCategories(); + int? sleepHours = await getSleepQueryHours(); + int? sleepMinutes = await getSleepQueryMinutes(); + List? sleepDays = await getSleepQueryDaysCategories(); + int? chessHours = await getChessHours(); + int? chessMinutes = await getChessMinutes(); + + if (group != null) { + return Settings( + group, + relapseCategories, + QueryConfig(moodHours, moodMinutes, moodDays), + QueryConfig(sleepHours, sleepMinutes, sleepDays), + TimeConfig(chessHours, chessMinutes)); + } + return null; +} diff --git a/lib/utils/timer_util.dart b/lib/utils/timer_util.dart new file mode 100644 index 0000000..58d9690 --- /dev/null +++ b/lib/utils/timer_util.dart @@ -0,0 +1,4 @@ +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')}'; +} diff --git a/lib/widgets/drop_down.dart b/lib/widgets/drop_down.dart new file mode 100644 index 0000000..5f06d89 --- /dev/null +++ b/lib/widgets/drop_down.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import '../providers/input_provider.dart'; + +class DropDown extends StatelessWidget { + final List _items; + const DropDown(this._items, {super.key}); + + @override + Widget build(BuildContext context) { + var inputModel = context.watch(); + return DropdownButtonFormField( + value: _items.isEmpty ? null : _items[0], + icon: const Icon(Icons.arrow_downward), + elevation: 16, + onChanged: (String? value) { + inputModel.relapseCategory = value ?? ''; + }, + items: _items.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ); + } +} diff --git a/lib/widgets/mood_form.dart b/lib/widgets/mood_form.dart index f6fd0e0..b9a0e05 100644 --- a/lib/widgets/mood_form.dart +++ b/lib/widgets/mood_form.dart @@ -1,54 +1,39 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:smoke_cess_app/models/mood.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; +import 'package:smoke_cess_app/services/database_service.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/text_formfield.dart'; +import '../providers/input_provider.dart'; import 'elevated_card.dart'; -class MoodForm extends StatefulWidget { +class MoodForm extends StatelessWidget { const MoodForm({super.key}); - @override - State createState() => _MoodFormState(); -} - -class _MoodFormState extends State { - final GlobalKey _moodFormKey = GlobalKey(); - MySlider slider = MySlider(); - String _textInput = ""; - - void submitForm() { - if (_moodFormKey.currentState!.validate()) { - _moodFormKey.currentState?.save(); //call every onSave Method - Mood mood = Mood(slider.sliderValue.toInt(), _textInput, DateTime.now()); - DatabaseService.instance.addMood(mood); - _moodFormKey.currentState?.reset(); - } - } - - void onFormFieldSave(String? newValue) => _textInput = newValue!; - @override Widget build(BuildContext context) { - return Form( - key: _moodFormKey, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedCard( - title: 'Stimmungsbewertung', - child: slider, - ), - const SizedBox(height: 16), - ElevatedCard( - title: 'Beschreibe deine Stimmung', - child: - MyTextFormField('Beschreibe deine Stimmung', onFormFieldSave), - ), - SubmitFormButton(submitForm) - ], - )); + var inputModel = context.watch(); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const ElevatedCard( + title: 'Stimmungsbewertung', + child: MySlider(), + ), + const SizedBox(height: 16), + const ElevatedCard( + title: 'Beschreibe deine Stimmung', + child: MyTextFormField('Beschreibe deine Stimmung'), + ), + const SizedBox( + height: 80, + ), + SubmitFormButton( + submitCallback: inputModel.saveMood, + ) + ], + ); } } diff --git a/lib/widgets/popup_for_start_and_stop.dart b/lib/widgets/popup_for_start_and_stop.dart index b8ed90a..e1af268 100644 --- a/lib/widgets/popup_for_start_and_stop.dart +++ b/lib/widgets/popup_for_start_and_stop.dart @@ -1,41 +1,26 @@ import 'package:flutter/material.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/text_formfield.dart'; -class TimerStartStopPopup extends StatefulWidget { +class TimerStartStopPopup extends StatelessWidget { final String title; const TimerStartStopPopup({Key? key, required this.title}) : super(key: key); - @override - TimerStartStopPopupState createState() => TimerStartStopPopupState(); -} - -class TimerStartStopPopupState extends State { - final MySlider slider = MySlider(); - - void submitForm(BuildContext context) { - Navigator.of(context).pop(); - } - - void onFormFieldSave(String? newValue) => newValue!; - @override Widget build(BuildContext context) { return AlertDialog( - title: Text(widget.title), + title: Text(title), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: const [ Padding( padding: const EdgeInsets.only(top: 8), child: MySlider(), ), - const SizedBox(height: 16), - MyTextFormField('Beschreibe deinen Motivation', onFormFieldSave), - SubmitFormButton(() => submitForm(context)), + SizedBox(height: 16), + MyTextFormField('Beschreibe deinen Motivation'), ], ), ); diff --git a/lib/widgets/relapse_form.dart b/lib/widgets/relapse_form.dart new file mode 100644 index 0000000..88e555c --- /dev/null +++ b/lib/widgets/relapse_form.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/widgets/drop_down.dart'; +import 'package:smoke_cess_app/widgets/submit_form_button.dart'; +import 'package:smoke_cess_app/widgets/text_formfield.dart'; + +import '../providers/input_provider.dart'; +import '../providers/settings_provider.dart'; +import 'elevated_card.dart'; + +class RelapseForm extends StatelessWidget { + const RelapseForm({super.key}); + + @override + Widget build(BuildContext context) { + var inputModel = context.watch(); + var settingsModel = context.watch(); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedCard( + title: 'Rückfallkategorie', + child: DropDown(settingsModel.settings?.relapseCategories ?? []), + ), + const ElevatedCard( + title: 'Beschreibe deinen Rückfall', + child: MyTextFormField('Beschreibe deinen Rückfall'), + ), + const SizedBox( + height: 80, + ), + SubmitFormButton(submitCallback: inputModel.saveRelapse) + ], + ); + } +} diff --git a/lib/widgets/scanner.dart b/lib/widgets/scanner.dart new file mode 100644 index 0000000..46f8c8a --- /dev/null +++ b/lib/widgets/scanner.dart @@ -0,0 +1,68 @@ +import 'package:awesome_dialog/awesome_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/models/settings.dart'; +import 'package:smoke_cess_app/services/json_service.dart'; +import 'package:smoke_cess_app/services/settings_service.dart'; +import '../providers/settings_provider.dart'; +import '../services/notification_service.dart'; + +class MyScanner extends StatefulWidget { + const MyScanner({super.key}); + + @override + State createState() => MyScannerState(); +} + +class MyScannerState extends State { + bool scanning = false; + + void handleSucces(String? rawValue) { + String qrText = rawValue!; + Map json = stringToJSON(qrText); + Settings settings = Settings.fromJson(json); + saveSettings(settings); + var settingsModel = context.read(); + settingsModel.initSettings(); + NotificationService().setAllNotifications(); + setState(() { + scanning = false; + AwesomeDialog( + context: context, + dialogType: DialogType.success, + title: 'Geschafft', + desc: 'Der Code wurde erfolgreich gescannt!', + ).show(); + }); + } + + void onDetect(capture) { + final List barcodes = capture.barcodes; + for (final barcode in barcodes) { + if (barcode.rawValue != null) { + return handleSucces(barcode.rawValue); + } + } + } + + @override + Widget build(BuildContext context) { + return scanning + ? Expanded( + child: MobileScanner( + fit: BoxFit.contain, + controller: MobileScannerController( + detectionTimeoutMs: 2000, + ), + onDetect: onDetect)) + : ElevatedButton( + style: ElevatedButton.styleFrom( + textStyle: const TextStyle(fontSize: 20)), + onPressed: () { + setState(() => scanning = true); + }, + child: const Text('Scan QR Code'), + ); + } +} diff --git a/lib/widgets/sleep_form.dart b/lib/widgets/sleep_form.dart index eb86e89..8b4389c 100644 --- a/lib/widgets/sleep_form.dart +++ b/lib/widgets/sleep_form.dart @@ -1,94 +1,51 @@ import 'package:flutter/material.dart'; -import 'package:smoke_cess_app/models/sleep.dart'; -import 'package:smoke_cess_app/service/database_service.dart'; +import 'package:provider/provider.dart'; import 'package:smoke_cess_app/widgets/elevated_card.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/text_formfield.dart'; import 'package:smoke_cess_app/widgets/timepicker.dart'; -class SleepForm extends StatefulWidget { +import '../providers/input_provider.dart'; + +class SleepForm extends StatelessWidget { const SleepForm({Key? key}) : super(key: key); - @override - State createState() => _SleepFormState(); -} - -class _SleepFormState extends State { - final GlobalKey _sleepFormKey = GlobalKey(); - MySlider slider = MySlider(); - String _textInput = ""; - TimePicker sleepTimePicker = TimePicker( - const TimeOfDay(hour: 22, minute: 00), - ); - TimePicker wakeUpTimePicker = TimePicker( - const TimeOfDay(hour: 8, minute: 00), - ); - - void submitForm() { - if (_sleepFormKey.currentState!.validate()) { - _sleepFormKey.currentState?.save(); //call every onSave Method - Sleep sleep = Sleep( - slider.sliderValue.toInt(), - _textInput, - DateTime.now(), - sleepTimePicker.getCurrentTime, - wakeUpTimePicker.getCurrentTime); - DatabaseService.instance.addSleep(sleep); - _sleepFormKey.currentState?.reset(); - } - } - - void onFormFieldSave(String? newValue) => _textInput = newValue!; - @override Widget build(BuildContext context) { - return Form( - key: _sleepFormKey, - child: Stack( - children: [ - SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ElevatedCard( - title: 'Einschlafzeit', - child: sleepTimePicker, - ), - const SizedBox(height: 16), - ElevatedCard( - title: 'Aufwachzeit', - child: wakeUpTimePicker, - ), - const SizedBox(height: 16), - ElevatedCard( - title: 'Schlafbewertung', - child: slider, - ), - const SizedBox(height: 16), - ElevatedCard( - title: 'Schlafbeschreibung', - child: MyTextFormField( - 'Beschreibe deinen Schlaf', onFormFieldSave), - ), - const SizedBox( - height: 80, - ), - ], - ), - ), - Positioned( - bottom: 0, - right: 0, - child: SizedBox( - width: 140, - height: 80, - child: SubmitFormButton(submitForm), - ), - ), - ], - ), + InputProvider inputModel = context.watch(); + String wokeUpKey = 'wokeUpAt'; + String sleptKey = 'sleptAt'; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedCard( + title: 'Einschlafzeit', + child: TimePicker(sleptKey), + ), + const SizedBox(height: 16), + ElevatedCard( + title: 'Aufwachzeit', + child: TimePicker(wokeUpKey), + ), + const SizedBox(height: 16), + const ElevatedCard( + title: 'Schlafbewertung', + child: MySlider(), + ), + const SizedBox(height: 16), + const ElevatedCard( + title: 'Schlafbeschreibung', + child: MyTextFormField('Beschreibe deinen Schlaf'), + ), + const SizedBox( + height: 80, + ), + SubmitFormButton( + submitCallback: () => inputModel.saveSleep(wokeUpKey, sleptKey), + ) + ], ); } } diff --git a/lib/widgets/slider.dart b/lib/widgets/slider.dart index b642916..fee5177 100644 --- a/lib/widgets/slider.dart +++ b/lib/widgets/slider.dart @@ -1,25 +1,19 @@ +import 'package:provider/provider.dart'; import 'package:flutter/material.dart'; +import 'package:smoke_cess_app/providers/input_provider.dart'; -// ignore: must_be_immutable -class MySlider extends StatefulWidget { - double _currentSliderValue = 50; - MySlider({Key? key}) : super(key: key); +class MySlider extends StatelessWidget { + const MySlider({super.key}); - @override - State createState() => SliderState(); - - double get sliderValue => _currentSliderValue; -} - -class SliderState extends State { @override Widget build(BuildContext context) { + InputProvider inputModel = context.watch(); return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 16), - Text('${widget._currentSliderValue.toInt()}', + Text('${inputModel.sliderValue.toInt()}', style: const TextStyle(fontSize: 22)), Row( mainAxisAlignment: MainAxisAlignment.center, @@ -27,31 +21,25 @@ class SliderState extends State { IconButton( icon: const Icon(Icons.remove_outlined), onPressed: () { - setState(() { - widget._currentSliderValue -= 1; - }); + inputModel.sliderValue -= 1; }, ), Expanded( child: Slider( - value: widget._currentSliderValue, + value: inputModel.sliderValue, min: 0, max: 100, divisions: 100, - label: widget._currentSliderValue.round().toString(), + label: '${inputModel.sliderValue.toInt()}', onChanged: (double value) { - setState(() { - widget._currentSliderValue = value; - }); + inputModel.sliderValue = value; }, ), ), IconButton( icon: const Icon(Icons.add_outlined), onPressed: () { - setState(() { - widget._currentSliderValue += 1; - }); + inputModel.sliderValue += 1; }, ), ], diff --git a/lib/widgets/submit_form_button.dart b/lib/widgets/submit_form_button.dart index 58f28c8..3242b18 100644 --- a/lib/widgets/submit_form_button.dart +++ b/lib/widgets/submit_form_button.dart @@ -1,15 +1,31 @@ +import 'package:awesome_dialog/awesome_dialog.dart'; import 'package:flutter/material.dart'; class SubmitFormButton extends StatelessWidget { - final VoidCallback submitCallback; - const SubmitFormButton(this.submitCallback, {super.key}); + final Future Function() submitCallback; + const SubmitFormButton({super.key, required this.submitCallback}); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: ElevatedButton( - onPressed: submitCallback, + onPressed: () async { + int success = await submitCallback(); + success != 0 + ? AwesomeDialog( + context: context, + dialogType: DialogType.success, + title: 'Gespeichert', + desc: 'Der Eintrag wurde erfolgreich gespeichert', + ).show() + : AwesomeDialog( + context: context, + dialogType: DialogType.error, + title: 'Fehler', + desc: 'Der Eintrag konnte nicht gespeichert werden', + ).show(); + }, child: const Text('Speichern'), ), ); diff --git a/lib/widgets/text_formfield.dart b/lib/widgets/text_formfield.dart index 102a350..1e6e25c 100644 --- a/lib/widgets/text_formfield.dart +++ b/lib/widgets/text_formfield.dart @@ -1,18 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/input_provider.dart'; class MyTextFormField extends StatelessWidget { final String _description; - final Function(String?) onSaveAction; const MyTextFormField( - this._description, - this.onSaveAction, { + this._description, { super.key, }); @override Widget build(BuildContext context) { + var inputProvider = context.watch(); return TextFormField( - onSaved: onSaveAction, + controller: inputProvider.textController, decoration: InputDecoration(hintText: _description), validator: (String? value) => value == null || value.isEmpty ? 'Text eingeben' : null, diff --git a/lib/widgets/timepicker.dart b/lib/widgets/timepicker.dart index 5e97af5..0b0926b 100644 --- a/lib/widgets/timepicker.dart +++ b/lib/widgets/timepicker.dart @@ -1,34 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:smoke_cess_app/providers/input_provider.dart'; +import 'package:provider/provider.dart'; -class TimePicker extends StatefulWidget { - TimeOfDay _initialTime; - TimePicker(this._initialTime, {Key? key}); +class TimePicker extends StatelessWidget { + final String keyMap; - TimeOfDay get getCurrentTime => _initialTime; - - @override - State createState() => TimePickerState(); -} - - -class TimePickerState extends State { - TimePickerState(); + const TimePicker(this.keyMap, {super.key}); @override Widget build(BuildContext context) { + InputProvider inputModel = context.watch(); + return Center( child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - '${widget._initialTime.hour.toString().padLeft(2, '0')}:${widget._initialTime.minute.toString().padLeft(2, '0')}', + '${inputModel.getTimeEntry(keyMap).hour.toString().padLeft(2, '0')}:${inputModel.getTimeEntry(keyMap).minute.toString().padLeft(2, '0')}', style: const TextStyle(fontSize: 22), ), const SizedBox(width: 16), ElevatedButton( onPressed: () async { - //TODO auslagern TimeOfDay? newTime = await showTimePicker( context: context, - initialTime: widget._initialTime, + initialTime: inputModel.getTimeEntry(keyMap), builder: (context, child) { return MediaQuery( data: MediaQuery.of(context) @@ -38,9 +32,7 @@ class TimePickerState extends State { }, ); if (newTime == null) return; - setState(() { - widget._initialTime = newTime; - }); + inputModel.setTime(keyMap, newTime); }, child: const Text('Zeit einstellen')) ]), diff --git a/lib/widgets/timer_widget.dart b/lib/widgets/timer_widget.dart new file mode 100644 index 0000000..875697f --- /dev/null +++ b/lib/widgets/timer_widget.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:smoke_cess_app/providers/timer_provider.dart'; +import 'package:smoke_cess_app/utils/timer_util.dart'; + +class TimerWidget extends StatelessWidget { + final Duration duration; + const TimerWidget({super.key, required this.duration}); + + @override + Widget build(BuildContext context) { + TimerProvider timerProvider = context.watch(); + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(formatTime(duration.inSeconds - timerProvider.elapsedSeconds)), + ElevatedButton( + onPressed: () => timerProvider.started + ? timerProvider.stopTimer() + : timerProvider.startTimer(duration), + child: Text(timerProvider.started ? 'Stop' : 'Start')) + ], + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 04c064a..08b0b74 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -64,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.3" + awesome_dialog: + dependency: "direct main" + description: + name: awesome_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" boolean_selector: dependency: transitive description: @@ -177,6 +184,13 @@ packages: description: flutter source: sdk version: "0.0.0" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" http: dependency: transitive description: @@ -233,6 +247,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" + nested: + dependency: transitive + description: + name: nested + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" path: dependency: "direct main" description: @@ -310,6 +331,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.2.4" + provider: + dependency: "direct main" + description: + name: provider + url: "https://pub.dartlang.org" + source: hosted + version: "6.0.5" + rive: + dependency: transitive + description: + name: rive + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.1" shared_preferences: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c4a8789..5c61553 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,8 @@ dependencies: sqflite: path: path_provider: ^2.0.12 + provider: ^6.0.5 + awesome_dialog: ^3.0.2 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.