Merge branch 'main' into testing_branch

main
Hinrik Ehrenfried 2023-03-06 11:40:11 +01:00
commit baa104d118
78 changed files with 1066 additions and 993 deletions

1
.gitignore vendored
View File

@ -31,6 +31,7 @@ migrate_working_dir/
.pub-cache/
.pub/
/build/
pubspec.lock
# Symbolication related
app.*.symbols

View File

@ -1,4 +1,4 @@
image: cirrusci/flutter:latest
image: cirrusci/flutter:3.7.5
stages:
- analyze

View File

@ -1,9 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.smoke_cess_app">
<!-- Required to fetch data from the internet. -->
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="smoke_cess_app"
android:label="ZI SmokeFree"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
assets/ZI_logo.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -7,7 +7,7 @@
},
"relapse_categories": ["App stresst mich", "langeweile", "lunge braucht es"],
"mood_query": {
"days": ["Montag", "Freitag"],
"days": ["Montag", "Donnerstag"],
"hours": 10,
"minutes": 30
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 564 B

After

Width:  |  Height:  |  Size: 796 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Smoke Cess App</string>
<string>ZI SmokeFree</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -15,7 +15,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>smoke_cess_app</string>
<string>ZI SmokeFree</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/pages/main_page.dart';
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() {
@ -23,11 +25,21 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: ChangeNotifierProvider(
create: (context) => SettingsProvider(),
child: const MyHomePage(),
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => SettingsProvider()),
ChangeNotifierProxyProvider<SettingsProvider, TasksProvider>(
create: (context) => TasksProvider(null),
update: (context, value, TasksProvider? previous) =>
TasksProvider(value),
),
ChangeNotifierProvider(
create: (context) => PageProvider(),
),
],
child: const MaterialApp(
title: _title,
home: MyHomePage(),
));
}
}

View File

@ -1,8 +1,9 @@
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/models/workout.dart';
import 'package:smoke_cess_app/services/database_service.dart';
// ignore: depend_on_referenced_packages
import 'package:sqflite_common/sqlite_api.dart';
class DatabaseMock implements DatabaseService {
@ -15,7 +16,7 @@ class DatabaseMock implements DatabaseService {
final List<Mood> _moodRecords = [];
final List<Sleep> _sleepRecords = [];
final List<Relapse> _relapseRecords = [];
final List<DatabaseRecord> _workoutRecords = [];
final List<Workout> _workoutRecords = [];
@override
Future<int> addMood(Mood mood) {
@ -29,6 +30,12 @@ class DatabaseMock implements DatabaseService {
return Future.value(1);
}
@override
Future<int> addWorkout(Workout workout) {
_workoutRecords.add(workout);
return Future.value(1);
}
@override
Future<int> addRelapse(Relapse relapse) {
_relapseRecords.add(relapse);
@ -36,7 +43,6 @@ class DatabaseMock implements DatabaseService {
}
@override
// TODO: implement database
Future<Database> get database => DatabaseService.instance.database;
@override
@ -53,4 +59,9 @@ class DatabaseMock implements DatabaseService {
Future<List<Relapse>> getRelapseRecords() {
return Future.value(_relapseRecords);
}
@override
Future<List<Workout>> getWorkoutRecords() {
return Future.value(_workoutRecords);
}
}

View File

@ -1,32 +0,0 @@
import 'package:smoke_cess_app/interface/db_record.dart';
class HIITWorkout implements DatabaseRecord {
Duration _workoutDuration;
String _commentBefore;
String _commentAfter;
DateTime _workoutDate;
HIITWorkout(this._workoutDuration, this._commentBefore, this._commentAfter,
this._workoutDate);
//TODO Felder anpassen
@override
factory HIITWorkout.fromMap(Map<String, dynamic> map) {
return HIITWorkout(map['_workoutDuration'], map['_commentBefore'],
map['_commentAfter'], map['_workoutDate']);
}
@override
String toCSV() =>
"${_workoutDate.toIso8601String()}, $_workoutDuration, $_commentBefore, $_commentAfter";
@override
Map<String, dynamic> toMap() {
return {
'workoutDuration': _workoutDuration,
'commentBefore': _commentBefore,
'commentAfter': _commentAfter,
'workoutDate': _workoutDate,
};
}
}

View File

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

View File

@ -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<String, dynamic> map) {
DateTime date = DateTime.parse(map['date']);

View File

@ -11,6 +11,9 @@ class Sleep implements DatabaseRecord {
Sleep(this._sleepQualityValue, this._comment, this._date, this._sleptAt,
this._wokeUpAt);
DateTime get date => _date;
int get sleepQualitiyValue => _sleepQualityValue;
@override
factory Sleep.fromDatabase(Map<String, dynamic> map) {
DateTime date = DateTime.parse(map['date']);

View File

@ -0,0 +1,38 @@
import 'package:smoke_cess_app/interface/db_record.dart';
class Workout implements DatabaseRecord {
int _motivationBefore;
int _motivationAfter;
DateTime _workoutDate;
Workout(this._motivationBefore, this._motivationAfter, this._workoutDate);
DateTime get date => _workoutDate;
int get motivationBefore => _motivationBefore;
int get motivationAfter => _motivationAfter;
@override
factory Workout.fromDatabase(Map<String, dynamic> map) {
return Workout(map['motivationBefore'], map['motivationAfter'],
DateTime.parse(map['workoutDate']));
}
@override
factory Workout.fromMap(Map<String, dynamic> map) {
return Workout(
map['motivationBefore'], map['motivationAfter'], map['workoutDate']);
}
@override
String toCSV() =>
"${_workoutDate.toIso8601String()}, $_motivationBefore, $_motivationAfter";
@override
Map<String, dynamic> toMap() {
return {
'motivationBefore': _motivationBefore,
'motivationAfter': _motivationAfter,
'workoutDate': _workoutDate.toIso8601String(),
};
}
}

View File

@ -1,225 +1,16 @@
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 '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 '../providers/input_provider.dart';
import '../widgets/view_form_page.dart';
class IntervalTimerPage extends StatefulWidget {
const IntervalTimerPage({Key? key}) : super(key: key);
@override
_IntervalTimerPageState createState() => _IntervalTimerPageState();
}
class _IntervalTimerPageState extends State<IntervalTimerPage> {
final Duration _warmupDuration = const Duration(seconds: 5);
final Duration _cooldownDuration = const Duration(seconds: 5);
final Duration _highIntensityDuration = const Duration(seconds: 4);
final Duration _lowIntensityDuration = const Duration(seconds: 3);
late Duration _totalDuration = const Duration(minutes: 35);
AudioPlayer warmUpPlayer = AudioPlayer();
AudioPlayer workoutPlayer = AudioPlayer();
AudioPlayer coolDownPlayer = AudioPlayer();
final AudioCache _audioCache = AudioCache();
final int _numHighIntensityBlocks = 4;
final int _numLowIntensityBlocks = 3;
Timer? _timer;
int _currentBlock = 0;
Duration _currentDuration = const Duration();
bool _isPaused = true;
@override
void initState() {
_currentDuration = _warmupDuration;
super.initState();
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
void _startTimer() async {
await showDialog(
context: context,
builder: (BuildContext context) {
return ChangeNotifierProvider(
create: (context) => InputProvider(),
child: const TimerStartStopPopup(
title: 'Motivation vor dem Training',
));
},
);
_isPaused = false;
Source source = AssetSource('go.mp3');
await AudioPlayer().play(source);
_timer = Timer.periodic(const Duration(seconds: 1), (_) => _tick());
Future.delayed(const Duration(seconds: 1)).then((value) {
_playWarmUpMusic();
});
}
void _resetTimer() {
() async {
await coolDownPlayer.stop();
await warmUpPlayer.stop();
await workoutPlayer.stop();
}();
_isPaused = true;
_timer?.cancel();
_currentBlock = 0;
_currentDuration = _warmupDuration;
_totalDuration = const Duration(minutes: 35);
setState(() {});
showDialog(
context: context,
builder: (BuildContext context) {
return const TimerStartStopPopup(
title: 'Motivation nach dem Training',
);
},
);
}
Future<void> _playWarmUpMusic() async {
Source source = AssetSource('warmUp.mp3');
await warmUpPlayer.setReleaseMode(ReleaseMode.loop);
await warmUpPlayer.play(source);
}
Future<void> _playWorkoutMusic() async {
await warmUpPlayer.stop();
Future.delayed(const Duration(microseconds: 600)).then((value) async {
Source source = AssetSource('workout.mp3');
await workoutPlayer.setReleaseMode(ReleaseMode.loop);
await workoutPlayer.play(source);
});
}
Future<void> _intervalChange() async {
Source source = AssetSource('beep.mp3');
await AudioPlayer().play(source);
}
void _tick() {
setState(() {
_currentDuration = Duration(
seconds: _currentDuration.inSeconds - 1,
);
_totalDuration = Duration(
seconds: _totalDuration.inSeconds - 1,
);
if (_currentDuration.inSeconds < 1) {
if (_currentBlock < _numHighIntensityBlocks + _numLowIntensityBlocks) {
_intervalChange();
if (_currentBlock == 0) {
_playWorkoutMusic();
}
_currentBlock++;
if (_currentBlock % 2 == 1) {
_currentDuration = _highIntensityDuration;
} else {
_currentDuration = _lowIntensityDuration;
}
} else if (_currentBlock < _numHighIntensityBlocks * 2) {
_intervalChange();
_currentBlock++;
_currentDuration = _cooldownDuration;
() async {
await workoutPlayer.stop();
Source source = AssetSource('cool_down.mp3');
await coolDownPlayer.setReleaseMode(ReleaseMode.loop);
await coolDownPlayer.play(source);
}();
} else {
() async {
Future.delayed(const Duration(microseconds: 900))
.then((value) async {
Source source = AssetSource('finish.mp3');
await AudioPlayer().play(source);
});
}();
_resetTimer();
}
}
});
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = twoDigits(duration.inMinutes.remainder(60));
final seconds = twoDigits(duration.inSeconds.remainder(60));
return '$minutes:$seconds';
}
String _formatTotalDuration(Duration duration) {
final minutes = duration.inMinutes;
final seconds = duration.inSeconds.remainder(60);
return _formatDuration(Duration(minutes: minutes, seconds: seconds));
}
class IntervalTimerPage extends StatelessWidget {
const IntervalTimerPage({super.key});
@override
Widget build(BuildContext context) {
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();
}
},
),
// ),
],
),
],
)));
return ViewFormPage(
form: WorkoutForm(), view: const WorkoutView(), page: Pages.timer);
}
}

View File

@ -1,9 +1,13 @@
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';
import '../widgets/todo_icon.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@ -16,38 +20,43 @@ class MyHomePageState extends State<MyHomePage> {
bool _isConfigured = false;
void _onItemTapped(int index) {
PageProvider pageProvider = context.read<PageProvider>();
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();
});
}
@override
Widget build(BuildContext context) {
var settingsModel = context.watch<SettingsProvider>();
var group = settingsModel.settings?.group;
var tasksModel = context.watch<TasksProvider>();
_isConfigured = settingsModel.initialized;
return Scaffold(
appBar: AppBar(
title: Text(
'${pages.keys.elementAt(_selectedIndex)} ${_isConfigured ? "Gruppe $group" : ""}')),
body: Center(
child: SingleChildScrollView(
child: pages.values.elementAt(_selectedIndex)['page'])),
'${pages.values.elementAt(_selectedIndex)['title']} ${_isConfigured ? "Gruppe ${settingsModel.settings?.group}" : ""}')),
body: SingleChildScrollView(
child: pages.values.elementAt(_selectedIndex)['page'],
),
bottomNavigationBar: NavigationBar(
onDestinationSelected: _onItemTapped,
selectedIndex: _selectedIndex,
destinations: pages.keys.map((key) {
return NavigationDestination(
icon: pages[key]!['icon'] ??
const Icon(Icons.disabled_by_default),
label: key);
icon: tasksModel.tasks[key] ?? false
? MyToDoIcon(pages[key]?['icon'])
: pages[key]!['icon'],
label: pages[key]?['title']);
}).toList()),
);
}

View File

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

View File

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

View File

@ -1,31 +1,18 @@
import 'package:awesome_dialog/awesome_dialog.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/models/mood.dart';
import 'package:smoke_cess_app/models/relapse.dart';
import 'package:smoke_cess_app/services/export_service.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 '../providers/settings_provider.dart';
import '../globals.dart' as globals;
class ScannerPage extends StatelessWidget {
const ScannerPage({super.key});
void export() async {
List<Mood> moods = await globals.databaseService.getMoodRecords();
List<Sleep> sleeps = await globals.databaseService.getSleepRecords();
List<Relapse> 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());
}
ExportService exportService = ExportService();
exportService.exportData();
}
void loadJSON(BuildContext context) async {
@ -33,12 +20,14 @@ class ScannerPage extends StatelessWidget {
await loadSettingsFromLocalJSON();
settingsModel.initSettings();
NotificationService().setAllNotifications();
AwesomeDialog(
context: context,
dialogType: DialogType.success,
title: 'Geschafft',
desc: 'Die Einstellung wurden erfolgreich gespeichert',
).show();
if (context.mounted) {
AwesomeDialog(
context: context,
dialogType: DialogType.success,
title: 'Geschafft',
desc: 'Die Einstellung wurden erfolgreich gespeichert',
).show();
}
}
@override

View File

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

View File

@ -0,0 +1,47 @@
import 'dart:async';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/cupertino.dart';
class AudioProvider extends ChangeNotifier {
final AudioPlayer _audioPlayer = AudioPlayer();
bool _isMuted = false;
final Source _finishedSoundSource = AssetSource('finish.mp3');
final Source _beepSoundSource = AssetSource('beep.mp3');
StreamSubscription? _onCompleteSubscription;
bool get isMuted => _isMuted;
void stop() => _resetPlayer();
void playFinishSound() {
_resetPlayer();
_audioPlayer.play(_finishedSoundSource);
}
void mutePlayer() {
_isMuted = true;
_audioPlayer.setVolume(0);
notifyListeners();
}
void unMutePlayer() {
_isMuted = false;
_audioPlayer.setVolume(1);
notifyListeners();
}
//resets player position and delete listening subscription
void _resetPlayer() {
_audioPlayer.stop();
_onCompleteSubscription?.cancel();
}
void playSourceAfterBeep(AssetSource source) {
_resetPlayer();
_audioPlayer.play(_beepSoundSource);
_onCompleteSubscription = _audioPlayer.onPlayerComplete.listen((event) {
_audioPlayer.play(source);
});
}
}

View File

@ -4,34 +4,33 @@ import 'package:smoke_cess_app/models/relapse.dart';
import 'package:smoke_cess_app/models/sleep.dart';
import '../globals.dart' as globals;
enum SleepTimes {
wokeUpAt,
sleptAt,
}
class InputProvider extends ChangeNotifier {
double _sliderValue = 50;
final TextEditingController _textController = TextEditingController(text: '');
final Map<String, TimeOfDay> _times = {
'wokeUpAt': const TimeOfDay(hour: 8, minute: 0),
'sleptAt': const TimeOfDay(hour: 22, minute: 0),
final Map<SleepTimes, TimeOfDay> _times = {
SleepTimes.wokeUpAt: const TimeOfDay(hour: 8, minute: 0),
SleepTimes.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) {
TimeOfDay getTimeEntry(SleepTimes key) {
return _times[key] ?? const TimeOfDay(hour: 12, minute: 0);
}
void setTime(String key, TimeOfDay time) {
void setTime(SleepTimes key, TimeOfDay time) {
_times[key] = time;
notifyListeners();
}
@ -39,8 +38,8 @@ class InputProvider extends ChangeNotifier {
void _resetFields() {
_sliderValue = 50;
_textController.text = '';
setTime('wokeUpAt', const TimeOfDay(hour: 8, minute: 0));
setTime('sleptAt', const TimeOfDay(hour: 22, minute: 0));
setTime(SleepTimes.wokeUpAt, const TimeOfDay(hour: 8, minute: 0));
setTime(SleepTimes.sleptAt, const TimeOfDay(hour: 22, minute: 0));
notifyListeners();
}
@ -53,12 +52,12 @@ class InputProvider extends ChangeNotifier {
Future<int> saveRelapse() {
Relapse relapse =
Relapse(_relapseCategory, _textController.text, DateTime.now());
Relapse(relapseCategory, _textController.text, DateTime.now());
_resetFields();
return globals.databaseService.addRelapse(relapse);
}
Future<int> saveSleep(String wokeUpKey, String sleptKey) {
Future<int> saveSleep(SleepTimes wokeUpKey, SleepTimes sleptKey) {
Sleep sleep = Sleep(_sliderValue.toInt(), _textController.text,
DateTime.now(), getTimeEntry(sleptKey), getTimeEntry(wokeUpKey));
_resetFields();

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

@ -0,0 +1,74 @@
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';
import 'package:smoke_cess_app/services/date_service.dart';
import 'package:smoke_cess_app/services/pages_service.dart';
import 'package:timezone/browser.dart';
import '../globals.dart' as globals;
import '../models/mood.dart';
class TasksProvider extends ChangeNotifier {
Map<Pages, bool> tasks = {
Pages.mood: true,
Pages.sleep: true,
Pages.timer: true,
};
List<Mood> moodHistory = [];
List<Sleep> sleepHistory = [];
List<Workout> workoutHistory = [];
List<Relapse> relapseHistory = [];
TasksProvider(SettingsProvider? settingsProvider) {
initTasks();
initHistories();
}
void setTaskDone(Pages taskName) {
tasks[taskName] = false;
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<Mood> moodList = await globals.databaseService.getMoodRecords();
if (moodList.isNotEmpty) {
Mood mood = moodList.last;
tasks[Pages.mood] =
!isSameDay(moodToday, mood.date) && moodToday.isBefore(now);
}
} else {
tasks[Pages.mood] = false;
}
TZDateTime? sleepToday = await getTodaySleep();
if (sleepToday != null) {
List<Sleep> sleepList = await globals.databaseService.getSleepRecords();
if (sleepList.isNotEmpty) {
Sleep sleep = sleepList.last;
tasks[Pages.sleep] =
!isSameDay(sleepToday, sleep.date) && sleepToday.isBefore(now);
}
} else {
tasks[Pages.sleep] = false;
}
List<Workout> workoutList =
await globals.databaseService.getWorkoutRecords();
if (workoutList.isNotEmpty) {
Workout mood = workoutList.last;
tasks[Pages.timer] = !isSameDay(DateTime.now(), mood.date);
}
notifyListeners();
}
}

View File

@ -9,7 +9,6 @@ class TimerProvider extends ChangeNotifier {
void startTimer(Duration duration) {
started = true;
print('starting timer');
_timer = Timer.periodic(const Duration(seconds: 1), ((timer) {
if (timer.tick >= duration.inSeconds) {
timer.cancel();
@ -19,5 +18,17 @@ class TimerProvider extends ChangeNotifier {
}));
}
void stopTimer() => _timer?.cancel();
void stopTimer() {
started = false;
_timer?.cancel();
_timer = null;
}
@override
void dispose() {
started = false;
_timer?.cancel();
_timer = null;
super.dispose();
}
}

View File

@ -0,0 +1,125 @@
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:smoke_cess_app/models/workout.dart';
import 'package:smoke_cess_app/providers/audio_provider.dart';
import 'package:smoke_cess_app/providers/timer_provider.dart';
import '../globals.dart' as globals;
enum WorkoutPhases {
warmUp,
highIntensity,
lowIntensity,
coolDown,
}
class WorkoutProvider extends ChangeNotifier {
final TimerProvider timerProvider;
final AudioProvider audioProvider;
bool isWorkoutStarted = false;
bool isWorkoutComplete = false;
int motivationBefore = 50;
int motivationAfter = 50;
int _workoutPhaseIndex = 0;
final List<WorkoutPhases> _workoutPhases = [
WorkoutPhases.warmUp,
WorkoutPhases.highIntensity,
WorkoutPhases.lowIntensity,
WorkoutPhases.highIntensity,
WorkoutPhases.lowIntensity,
WorkoutPhases.highIntensity,
WorkoutPhases.lowIntensity,
WorkoutPhases.highIntensity,
WorkoutPhases.coolDown,
];
WorkoutProvider(this.timerProvider, this.audioProvider);
WorkoutPhases get currentPhase => _workoutPhases[_workoutPhaseIndex];
Duration get currentPhaseDuration =>
_workoutPhaseSettings[currentPhase]!['duration'];
bool get isPhaseComplete =>
timerProvider.elapsedSeconds - currentPhaseDuration.inSeconds == 0;
Color get currentPhaseColor => _workoutPhaseSettings[currentPhase]!['color'];
AssetSource get currentPhaseSource =>
_workoutPhaseSettings[currentPhase]!['source'];
String get currentPhaseTitle => _workoutPhaseSettings[currentPhase]!['title'];
void nextPhase() {
if (_workoutPhaseIndex < _workoutPhases.length - 1) {
_workoutPhaseIndex += 1;
audioProvider.playSourceAfterBeep(currentPhaseSource);
timerProvider.startTimer(currentPhaseDuration);
} else {
//workout completed
audioProvider.playFinishSound;
stopWorkout();
}
}
void startWorkout() {
isWorkoutStarted = true;
isWorkoutComplete = false;
audioProvider.playSourceAfterBeep(currentPhaseSource);
timerProvider.startTimer(currentPhaseDuration);
}
void stopWorkout() {
isWorkoutStarted = false;
isWorkoutComplete = true;
_cleanUp();
notifyListeners();
}
void interruptWorkout() {
isWorkoutStarted = false;
isWorkoutComplete = false;
_cleanUp();
notifyListeners();
}
void _cleanUp() {
audioProvider.stop();
timerProvider.stopTimer();
}
void saveWorkout() {
Workout workout =
Workout(motivationBefore, motivationAfter, DateTime.now());
globals.databaseService.addWorkout(workout);
}
@override
void dispose() {
_cleanUp();
super.dispose();
}
}
Map<WorkoutPhases, Map<String, dynamic>> _workoutPhaseSettings = {
WorkoutPhases.warmUp: {
'title': 'Warm Up',
'duration': const Duration(seconds: 5),
'source': AssetSource('warmUp.mp3'),
'color': Colors.green
},
WorkoutPhases.highIntensity: {
'title': 'High Intensity',
'duration': const Duration(seconds: 4),
'source': AssetSource('workout.mp3'),
'color': Colors.red
},
WorkoutPhases.lowIntensity: {
'title': 'Low Intensity',
'duration': const Duration(seconds: 3),
'source': AssetSource('workout.mp3'),
'color': Colors.orange
},
WorkoutPhases.coolDown: {
'title': 'Cool Down',
'duration': const Duration(seconds: 5),
'source': AssetSource('cool_down.mp3'),
'color': Colors.blue
}
};

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:path/path.dart';
import 'package:smoke_cess_app/models/mood.dart';
import 'package:smoke_cess_app/models/workout.dart';
import 'package:smoke_cess_app/models/relapse.dart';
import 'package:sqflite/sqflite.dart';
// ignore: depend_on_referenced_packages
@ -35,7 +36,6 @@ class DatabaseService {
await db.execute(_createWorkoutTable);
}
//TODO use generic function?
Future<List<Mood>> getMoodRecords() async {
Database db = await instance.database;
var moodRecords = await db.query('mood');
@ -63,6 +63,15 @@ class DatabaseService {
return relapseList;
}
Future<List<Workout>> getWorkoutRecords() async {
Database db = await instance.database;
var workoutRecords = await db.query('workout');
List<Workout> workoutList = workoutRecords.isNotEmpty
? workoutRecords.map((e) => Workout.fromDatabase(e)).toList()
: [];
return workoutList;
}
Future<int> addMood(Mood mood) async {
Database db = await instance.database;
return await db.insert('mood', mood.toMap());
@ -73,13 +82,17 @@ class DatabaseService {
return await db.insert('sleep', sleep.toMap());
}
Future<int> addWorkout(Workout workout) async {
Database db = await instance.database;
return await db.insert('workout', workout.toMap());
}
Future<int> addRelapse(Relapse relapse) async {
Database db = await instance.database;
return await db.insert('relapse', relapse.toMap());
}
}
String _createMoodTable = '''
final String _createMoodTable = '''
CREATE TABLE IF NOT EXISTS mood(
id INTEGER PRIMARY KEY,
value INTEGER,
@ -88,7 +101,7 @@ String _createMoodTable = '''
)
''';
String _createSleepTable = '''
final String _createSleepTable = '''
CREATE TABLE IF NOT EXISTS sleep(
id INTEGER PRIMARY KEY,
value INTEGER,
@ -101,7 +114,7 @@ String _createSleepTable = '''
)
''';
String _createRelapseTable = '''
final String _createRelapseTable = '''
CREATE TABLE IF NOT EXISTS relapse(
id INTEGER PRIMARY KEY,
date TEXT,
@ -110,14 +123,12 @@ String _createRelapseTable = '''
)
''';
String _createWorkoutTable = '''
final String _createWorkoutTable = '''
CREATE TABLE IF NOT EXISTS workout(
id INTEGER PRIMARY KEY,
date TEXT,
motivationBefore INTEGER,
commentBefore TEXT,
motivationAfter INTEGER,
commentAfter TEXT,
completed INTEGER
)
''';
}

View File

@ -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 = {
@ -13,6 +15,12 @@ const weekDays = {
"Sonntag": 7,
};
bool isSameDay(DateTime? dateA, DateTime? dateB) {
return dateA?.year == dateB?.year &&
dateA?.month == dateB?.month &&
dateA?.day == dateB?.day;
}
Future<List<TZDateTime>> getDatesforMood() async {
final List<String>? selectedDays = await getMoodQueryDaysCategories();
final int? selectedHours = await getMoodQueryHours();
@ -27,6 +35,59 @@ Future<List<TZDateTime>> getDatesforSleep() async {
return createTZDateTimes(selectedDays, selectedHours, selectedMinutes);
}
Future<TZDateTime?> getTodayMood() async {
List<TZDateTime> moodDates = await getDatesforMood();
Iterable<TZDateTime> today =
moodDates.where((element) => isSameDay(element, DateTime.now()));
return today.isNotEmpty ? today.first : null;
}
Future<TZDateTime?> getTodaySleep() async {
List<TZDateTime> sleepDates = await getDatesforSleep();
Iterable<TZDateTime> today =
sleepDates.where((element) => isSameDay(element, DateTime.now()));
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<String>? selectedDays, int? selectedHours, int? selectedMinutes) {
final List<TZDateTime> tzDateTimes = [];

View File

@ -0,0 +1,43 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
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/models/workout.dart';
import 'package:smoke_cess_app/services/database_service.dart';
import '../globals.dart' as globals;
class ExportService {
Uri url = Uri.parse('http://localhost:3000/data');
final DatabaseService _databaseService = globals.databaseService;
Future<Map<String, List<String>>> _loadRecords() async {
List<Mood> moodRecords = await _databaseService.getMoodRecords();
List<Sleep> sleepRecords = await _databaseService.getSleepRecords();
List<Relapse> relapseRecords = await _databaseService.getRelapseRecords();
List<Workout> workoutRecords = await _databaseService.getWorkoutRecords();
return {
'Stimmung':
moodRecords.map((Mood mood) => jsonEncode(mood.toMap())).toList(),
'Schlaf':
sleepRecords.map((Sleep sleep) => jsonEncode(sleep.toMap())).toList(),
'Rückfall': relapseRecords
.map((Relapse relapse) => jsonEncode(relapse.toMap()))
.toList(),
'Workout': workoutRecords
.map((Workout workout) => jsonEncode(workout.toMap()))
.toList()
};
}
Future<int> exportData() async {
Map<String, List<String>> body = await _loadRecords();
final response = await http.post(url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(body));
return response.statusCode >= 400 ? 0 : 1;
}
}

View File

@ -5,25 +5,38 @@ import '../pages/relapse_page.dart';
import '../pages/scanner_page.dart';
import '../pages/sleep_page.dart';
const pages = {
'Stimmung': {
enum Pages {
mood,
sleep,
relapse,
timer,
settings,
}
const Map<Pages, Map<String, dynamic>> pages = {
Pages.mood: {
'title': 'Stimmung',
'page': MoodPage(),
'icon': Icon(Icons.mood_outlined, color: Colors.black)
'icon': Icon(Icons.mood_outlined, color: Colors.black),
},
'Schlaf': {
Pages.sleep: {
'title': 'Schlaf',
'page': SleepPage(),
'icon': Icon(Icons.bedtime_outlined, color: Colors.black)
'icon': Icon(Icons.bedtime_outlined, color: Colors.black),
},
'Timer': {
Pages.timer: {
'title': 'Timer',
'page': IntervalTimerPage(),
'icon': Icon(Icons.timer_outlined, color: Colors.black)
'icon': Icon(Icons.timer_outlined, color: Colors.black),
},
'Rückfall': {
Pages.relapse: {
'title': 'Rückfall',
'page': RelapsePage(),
'icon': Icon(Icons.smoke_free_outlined, color: Colors.black),
},
'Scanner': {
Pages.settings: {
'title': 'Scanner',
'page': ScannerPage(),
'icon': Icon(Icons.camera_alt_outlined, color: Colors.black)
'icon': Icon(Icons.camera_alt_outlined, color: Colors.black),
},
};

View File

@ -23,13 +23,6 @@ Future<int?> getMoodQueryMinutes() => _getIntSetting('mood_query_minutes');
Future<int?> getChessHours() => _getIntSetting('chess_hours');
Future<int?> getChessMinutes() => _getIntSetting('chess_minutes');
void _setStringSetting(String settingKey, String settingValue) =>
SharedPreferences.getInstance()
.then((pref) => pref.setString(settingKey, settingValue));
Future<String?> _getStringSetting(String settingKey) =>
SharedPreferences.getInstance().then((pref) => pref.getString(settingKey));
void _setIntSetting(String settingKey, int settingValue) =>
SharedPreferences.getInstance()
.then((pref) => pref.setInt(settingKey, settingValue));

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/models/mood.dart';
import 'package:smoke_cess_app/services/database_service.dart';
import 'package:smoke_cess_app/providers/tasks_provider.dart';
import 'package:smoke_cess_app/services/pages_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';
@ -15,6 +15,7 @@ class MoodForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
var inputModel = context.watch<InputProvider>();
var tasksModel = context.watch<TasksProvider>();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -32,6 +33,7 @@ class MoodForm extends StatelessWidget {
),
SubmitFormButton(
submitCallback: inputModel.saveMood,
updateTasks: () => tasksModel.setTaskDone(Pages.mood),
)
],
);

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,22 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/audio_provider.dart';
class MuteButton extends StatelessWidget {
const MuteButton({super.key});
@override
Widget build(BuildContext context) {
AudioProvider workoutProvider = context.watch<AudioProvider>();
return IconButton(
onPressed: workoutProvider.isMuted
? workoutProvider.unMutePlayer
: workoutProvider.mutePlayer,
icon: Icon(workoutProvider.isMuted
? Icons.volume_off_outlined
: Icons.volume_up_outlined),
);
}
}

View File

@ -1,26 +1,52 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/providers/input_provider.dart';
import 'package:smoke_cess_app/widgets/slider.dart';
import 'package:smoke_cess_app/widgets/text_formfield.dart';
Future showMotivationPopup(
BuildContext context, Function onSave, String title) {
return showDialog(
context: context,
builder: (BuildContext context) {
return ChangeNotifierProvider(
create: (context) => InputProvider(),
child: TimerStartStopPopup(
title: title,
onSaveAction: onSave,
),
);
},
);
}
class TimerStartStopPopup extends StatelessWidget {
final String title;
const TimerStartStopPopup({Key? key, required this.title}) : super(key: key);
final Function onSaveAction;
const TimerStartStopPopup(
{Key? key, required this.title, required this.onSaveAction})
: super(key: key);
@override
Widget build(BuildContext context) {
InputProvider inputProvider = context.watch<InputProvider>();
return AlertDialog(
title: Text(title),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Padding(
padding: const EdgeInsets.only(top: 8),
children: [
const Padding(
padding: EdgeInsets.only(top: 8),
child: MySlider(),
),
SizedBox(height: 16),
MyTextFormField('Beschreibe deinen Motivation'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
onSaveAction(inputProvider.sliderValue);
Navigator.pop(context);
},
child: const Text('Speichern'))
],
),
);

View File

@ -0,0 +1,43 @@
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);
if (context.mounted) {
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

@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/providers/tasks_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 '../services/pages_service.dart';
import 'elevated_card.dart';
class RelapseForm extends StatelessWidget {
@ -15,6 +16,7 @@ class RelapseForm extends StatelessWidget {
Widget build(BuildContext context) {
var inputModel = context.watch<InputProvider>();
var settingsModel = context.watch<SettingsProvider>();
var tasksModel = context.watch<TasksProvider>();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -29,7 +31,10 @@ class RelapseForm extends StatelessWidget {
const SizedBox(
height: 80,
),
SubmitFormButton(submitCallback: inputModel.saveRelapse)
SubmitFormButton(
submitCallback: inputModel.saveRelapse,
updateTasks: () => tasksModel.setTaskDone(Pages.mood),
)
],
);
}

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

@ -37,12 +37,28 @@ class MyScannerState extends State<MyScanner> {
});
}
void handleError() {
setState(() {
scanning = false;
AwesomeDialog(
context: context,
dialogType: DialogType.error,
title: 'Fehler',
desc: 'Der QR-Code war fehlerhaft!',
).show();
});
}
void onDetect(capture) {
final List<Barcode> barcodes = capture.barcodes;
for (final barcode in barcodes) {
if (barcode.rawValue != null) {
return handleSucces(barcode.rawValue);
try {
final List<Barcode> barcodes = capture.barcodes;
for (final barcode in barcodes) {
if (barcode.rawValue != null) {
return handleSucces(barcode.rawValue);
}
}
} catch (e) {
handleError();
}
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/providers/tasks_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';
@ -7,6 +8,7 @@ import 'package:smoke_cess_app/widgets/text_formfield.dart';
import 'package:smoke_cess_app/widgets/timepicker.dart';
import '../providers/input_provider.dart';
import '../services/pages_service.dart';
class SleepForm extends StatelessWidget {
const SleepForm({Key? key}) : super(key: key);
@ -14,20 +16,19 @@ class SleepForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
InputProvider inputModel = context.watch<InputProvider>();
String wokeUpKey = 'wokeUpAt';
String sleptKey = 'sleptAt';
TasksProvider tasksModel = context.watch<TasksProvider>();
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedCard(
const ElevatedCard(
title: 'Einschlafzeit',
child: TimePicker(sleptKey),
child: TimePicker(SleepTimes.sleptAt),
),
const SizedBox(height: 16),
ElevatedCard(
const ElevatedCard(
title: 'Aufwachzeit',
child: TimePicker(wokeUpKey),
child: TimePicker(SleepTimes.wokeUpAt),
),
const SizedBox(height: 16),
const ElevatedCard(
@ -43,7 +44,9 @@ class SleepForm extends StatelessWidget {
height: 80,
),
SubmitFormButton(
submitCallback: () => inputModel.saveSleep(wokeUpKey, sleptKey),
submitCallback: () =>
inputModel.saveSleep(SleepTimes.wokeUpAt, SleepTimes.sleptAt),
updateTasks: () => tasksModel.setTaskDone(Pages.mood),
)
],
);

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,30 +1,41 @@
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<int> Function() submitCallback;
const SubmitFormButton({super.key, required this.submitCallback});
final void Function() updateTasks;
const SubmitFormButton(
{super.key, required this.submitCallback, required this.updateTasks});
@override
Widget build(BuildContext context) {
PageProvider pageProvider = context.watch<PageProvider>();
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
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();
if (context.mounted) {
if (success != 0) {
await AwesomeDialog(
context: context,
dialogType: DialogType.success,
title: 'Gespeichert',
desc: 'Der Eintrag wurde erfolgreich gespeichert',
).show();
updateTasks();
pageProvider.swap();
} else {
await AwesomeDialog(
context: context,
dialogType: DialogType.error,
title: 'Fehler',
desc: 'Der Eintrag konnte nicht gespeichert werden',
).show();
}
}
},
child: const Text('Speichern'),
),

View File

@ -3,7 +3,7 @@ import 'package:smoke_cess_app/providers/input_provider.dart';
import 'package:provider/provider.dart';
class TimePicker extends StatelessWidget {
final String keyMap;
final SleepTimes keyMap;
const TimePicker(this.keyMap, {super.key});

View File

@ -14,11 +14,6 @@ class TimerWidget extends StatelessWidget {
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'))
],
);
}

View File

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
class MyToDoIcon extends StatelessWidget {
final Icon _icon;
const MyToDoIcon(this._icon, {super.key});
@override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
_icon,
const Positioned(
// draw a red marble
top: 0.0,
right: 0.0,
child: Icon(Icons.brightness_1, size: 10.0, color: Colors.redAccent),
)
]);
}
}

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,36 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:smoke_cess_app/providers/audio_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 {
WorkoutForm({super.key});
final TimerProvider timerProvider = TimerProvider();
final AudioProvider audioProvider = AudioProvider();
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => timerProvider),
ChangeNotifierProvider(create: (context) => audioProvider),
ChangeNotifierProvider(
create: (context) =>
WorkoutProvider(timerProvider, audioProvider)),
],
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Align(
alignment: Alignment.topLeft,
child: MuteButton(),
),
WorkoutTimerWidget()
],
));
}
}

View File

@ -0,0 +1,93 @@
import 'dart:async';
import 'package:awesome_dialog/awesome_dialog.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/page_provider.dart';
import '../providers/tasks_provider.dart';
import '../providers/workout_provider.dart';
import '../services/pages_service.dart';
import '../widgets/timer_widget.dart';
import '../providers/timer_provider.dart';
import 'popup_for_start_and_stop.dart';
class WorkoutTimerWidget extends StatelessWidget {
const WorkoutTimerWidget({super.key});
@override
Widget build(BuildContext context) {
TimerProvider timerProvider = context.watch<TimerProvider>();
WorkoutProvider workoutProvider = context.watch<WorkoutProvider>();
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');
if (context.mounted) {
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), handleStopWorkout);
}
void handleStartStopWorkout() {
if (!workoutProvider.isWorkoutStarted) {
showMotivationPopup(context, (double value) {
workoutProvider.motivationBefore = value.toInt();
workoutProvider.startWorkout();
}, 'Motivation vor dem Training');
} else {
workoutProvider.interruptWorkout();
handleStopWorkout();
}
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(workoutProvider.currentPhaseTitle),
const SizedBox(
height: 20,
),
Stack(
alignment: Alignment.center,
children: [
SizedBox(
height: 100,
width: 100,
child: CircularProgressIndicator(
color: workoutProvider.currentPhaseColor,
value: (workoutProvider.currentPhaseDuration.inSeconds
.toDouble() -
timerProvider.elapsedSeconds) /
workoutProvider.currentPhaseDuration.inSeconds)),
TimerWidget(duration: workoutProvider.currentPhaseDuration),
],
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: handleStartStopWorkout,
child: Text(timerProvider.started ? 'Stop' : 'Start'))
],
);
}
}

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

@ -1,516 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.0"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.9.0"
audioplayers:
dependency: "direct main"
description:
name: audioplayers
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
audioplayers_android:
dependency: transitive
description:
name: audioplayers_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
audioplayers_darwin:
dependency: transitive
description:
name: audioplayers_darwin
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
audioplayers_linux:
dependency: transitive
description:
name: audioplayers_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
audioplayers_platform_interface:
dependency: transitive
description:
name: audioplayers_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
audioplayers_web:
dependency: transitive
description:
name: audioplayers_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
audioplayers_windows:
dependency: transitive
description:
name: audioplayers_windows
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:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
dbus:
dependency: transitive
description:
name: dbus
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.8"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.4"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
url: "https://pub.dartlang.org"
source: hosted
version: "13.0.0"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0+1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
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:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.5"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.4"
lints:
dependency: transitive
description:
name: lints
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.12"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
mobile_scanner:
dependency: "direct main"
description:
name: mobile_scanner
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:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.2"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.12"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.22"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.8"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
process:
dependency: transitive
description:
name: process
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:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.17"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.15"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.3"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.0"
sqflite:
dependency: "direct main"
description:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.4+1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.2+2"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
synchronized:
dependency: transitive
description:
name: synchronized
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.12"
timezone:
dependency: "direct main"
description:
name: timezone
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.1"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.7"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0+3"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.0"
sdks:
dart: ">=2.18.2 <3.0.0"
flutter: ">=3.3.0"

View File

@ -1,33 +1,13 @@
name: smoke_cess_app
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
publish_to: 'none'
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: '>=2.18.2 <3.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
@ -36,38 +16,28 @@ dependencies:
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.
cupertino_icons: ^1.0.2
timezone: ^0.9.0
shared_preferences: ^2.0.17
audioplayers: ^3.0.1
mobile_scanner: ^3.0.0
flutter_local_notifications: ^13.0.0
http: ^0.13.5
syncfusion_flutter_charts: ^20.4.52
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
flutter_launcher_icons: ^0.12.0
flutter_icons:
android: 'launcher_icon'
ios: true
image_path: 'assets/ZI_logo.png'
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/beep.mp3
- assets/go.mp3
@ -77,31 +47,3 @@ flutter:
- assets/finish.mp3
- assets/group1.json
- assets/group3.json
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages