Initial (redacted) commit.

This commit is contained in:
mustard 2024-08-26 00:34:20 +02:00
commit 655f8a036a
368 changed files with 20949 additions and 0 deletions

28
.gitignore vendored Normal file
View file

@ -0,0 +1,28 @@
.obsidian/
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode
app/untranslated_messages.json
app/tests.json
public/docs/*

View file

@ -0,0 +1,24 @@
### Summary
<!-- Summarize the bug encountered concisely. -->
### Steps to reproduce
<!-- Describe how one can reproduce the issue - this is very important. Please use an ordered list. -->
### What is the current _bug_ behavior?
<!-- Describe what actually happens. -->
### What is the expected _correct_ behavior?
<!-- Describe what you should see instead. -->
### Relevant logs and/or screenshots
<!-- Paste any relevant logs - please use code blocks (```) to format console output, logs, and code
as it's tough to read otherwise. -->
### Possible fixes
<!-- If you can, link to the line of code that might be responsible for the problem. -->

View file

@ -0,0 +1,22 @@
## :rocket: Description
### Problem to solve
<!-- What problem do we solve? Try to define the who/what/why of the opportunity as a user story. For example, "As a (who), I want (what), so I can (why/value)." -->
### Proposal
<!-- How are we going to solve the problem? Try to include the user journey! https://about.gitlab.com/handbook/journeys/#user-journey -->
### Further details
<!-- Include use cases, benefits, goals, or any other details that will help us understand the problem better. -->
## :art: Wireframe
## :link: Links
## :white_check_mark: Acceptance Criteria
- [ ]

View file

@ -0,0 +1,26 @@
## Summary
<!--
Please briefly describe what part of the code base needs to be refactored.
-->
## Improvements
<!--
Explain the benefits of refactoring this code.
See also https://about.gitlab.com/handbook/values/index.html#say-why-not-just-what
-->
## Risks
<!--
Please list features that can break because of this refactoring and how you intend to solve that.
-->
## Involved components
<!--
List files or directories that will be changed by the refactoring.
-->
## Links

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
README.md Normal file
View file

@ -0,0 +1 @@
Flutter app done for a uni project. Names redacted for privacy reasons.

43
app/.gitignore vendored Normal file
View file

@ -0,0 +1,43 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

45
app/.metadata Normal file
View file

@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: android
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: ios
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: linux
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: macos
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: web
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: windows
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

3
app/README.md Normal file
View file

@ -0,0 +1,3 @@
# habitrack_app
A new Flutter project.

13
app/android/.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks

View file

@ -0,0 +1,58 @@
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = "1"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = "1.0"
}
android {
namespace = "com.example.habitrack_app"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.habitrack_app"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug
}
}
}
flutter {
source = "../.."
}

View file

@ -0,0 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View file

@ -0,0 +1,48 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:label="Habitrack"
android:name="${applicationName}"
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View file

@ -0,0 +1,5 @@
package com.example.habitrack_app
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View file

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

18
app/android/build.gradle Normal file
View file

@ -0,0 +1,18 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View file

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

View file

@ -0,0 +1,25 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}
include ":app"

BIN
app/assets/icon/Logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

BIN
app/assets/icon/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

BIN
app/assets/icon/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
app/assets/icon/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<g>
<g>
<path d="M358.079,149.289c0-10.352-3.787-20.314-10.665-28.051l-55.449-62.38c-1.48-1.665-2.295-3.808-2.295-6.036V48.16
c5.093-2.829,8.551-8.26,8.551-14.489V16.569C298.221,7.432,290.789,0,281.653,0h-51.307c-9.136,0-16.568,7.432-16.568,16.568
V33.67c0,6.228,3.458,11.659,8.551,14.489v4.663c0,2.228-0.816,4.371-2.295,6.036l-55.449,62.381
c-6.877,7.737-10.665,17.699-10.665,28.051v46.853c0,6.603,2.564,12.614,6.745,17.102c-4.18,4.488-6.745,10.5-6.745,17.102v8.551
c0,6.603,2.564,12.614,6.745,17.102c-4.18,4.488-6.745,10.5-6.745,17.102v8.551c0,6.603,2.564,12.614,6.745,17.102
c-4.18,4.488-6.745,10.5-6.745,17.102v8.551c0,6.603,2.564,12.614,6.745,17.102c-4.18,4.488-6.745,10.5-6.745,17.102v8.551
c0,6.603,2.564,12.614,6.745,17.102c-4.18,4.488-6.745,10.5-6.745,17.102v8.551c0,6.603,2.564,12.614,6.745,17.102
c-4.18,4.488-6.745,10.5-6.745,17.102v25.653c0,23.281,18.941,42.221,42.221,42.221h119.716c23.281,0,42.221-18.941,42.221-42.221
v-25.653c0-6.603-2.564-12.614-6.745-17.102c4.18-4.488,6.745-10.5,6.745-17.102v-8.551c0-6.603-2.564-12.614-6.745-17.102
c4.18-4.488,6.745-10.5,6.745-17.102v-8.551c0-6.603-2.564-12.614-6.745-17.102c4.18-4.488,6.745-10.5,6.745-17.102v-8.551
c0-6.603-2.564-12.614-6.745-17.102c4.18-4.488,6.745-10.5,6.745-17.102v-8.551c0-6.603-2.564-12.614-6.745-17.102
c4.18-4.488,6.745-10.5,6.745-17.102v-8.551c0-6.603-2.564-12.614-6.745-17.102c4.18-4.488,6.745-10.5,6.745-17.102V149.289z
M229.812,16.568c0-0.295,0.239-0.534,0.534-0.534h51.307c0.295,0,0.534,0.239,0.534,0.534V33.67c0,0.295-0.239,0.534-0.534,0.534
h-51.307c-0.295,0-0.534-0.239-0.534-0.534V16.568z M176.568,131.891l55.45-62.381c4.092-4.604,6.345-10.53,6.345-16.688v-2.585
h35.273v2.584c0,6.158,2.253,12.085,6.345,16.689l55.449,62.381c1.346,1.514,2.476,3.176,3.427,4.928H173.143
C174.093,135.066,175.224,133.403,176.568,131.891z M342.046,469.779c0,14.44-11.748,26.188-26.188,26.188H196.142
c-14.44,0-26.188-11.748-26.188-26.188v-25.653c0-5.01,4.076-9.086,9.086-9.086H332.96c5.01,0,9.086,4.076,9.086,9.086V469.779z
M342.046,409.921c0,5.01-4.076,9.086-9.086,9.086H179.04c-5.01,0-9.086-4.076-9.086-9.086v-8.551c0-5.01,4.076-9.086,9.086-9.086
H332.96c5.01,0,9.086,4.076,9.086,9.086V409.921z M342.046,367.165c0,5.01-4.076,9.086-9.086,9.086H179.04
c-5.01,0-9.086-4.076-9.086-9.086v-8.551c0-5.01,4.076-9.086,9.086-9.086H332.96c5.01,0,9.086,4.076,9.086,9.086V367.165z
M342.046,324.409c0,5.01-4.076,9.086-9.086,9.086H179.04c-5.01,0-9.086-4.076-9.086-9.086v-8.551c0-5.01,4.076-9.086,9.086-9.086
H332.96c5.01,0,9.086,4.076,9.086,9.086V324.409z M342.046,281.653c0,5.01-4.076,9.086-9.086,9.086H179.04
c-5.01,0-9.086-4.076-9.086-9.086v-8.551c0-5.01,4.076-9.086,9.086-9.086H332.96c5.01,0,9.086,4.076,9.086,9.086V281.653z
M342.046,238.898c0,5.01-4.076,9.086-9.086,9.086H179.04c-5.01,0-9.086-4.076-9.086-9.086v-8.551c0-5.01,4.076-9.086,9.086-9.086
H332.96c5.01,0,9.086,4.076,9.086,9.086V238.898z M342.046,196.142c0,5.01-4.076,9.086-9.086,9.086H179.04
c-5.01,0-9.086-4.076-9.086-9.086v-43.29h172.092V196.142z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

34
app/ios/.gitignore vendored Normal file
View file

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
</dict>
</plist>

View file

@ -0,0 +1 @@
#include "Generated.xcconfig"

View file

@ -0,0 +1 @@
#include "Generated.xcconfig"

View file

@ -0,0 +1,616 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.habitrackApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.habitrackApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.habitrackApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.habitrackApp.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.habitrackApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.habitrackApp;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View file

@ -0,0 +1,13 @@
import Flutter
import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View file

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View file

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

49
app/ios/Runner/Info.plist Normal file
View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Habitrack App</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>habitrack_app</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"

View file

@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}

3
app/l10n.yaml Normal file
View file

@ -0,0 +1,3 @@
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

View file

@ -0,0 +1,465 @@
//import 'dart:ffi';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/function_widgets/widget_settings_menu/setting_entry.dart';
import 'package:habitrack_app/function_widgets/widget_settings_menu/widget_settings.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
import 'package:habitrack_app/main.dart';
import 'package:habitrack_app/sembast/timer.dart';
import 'package:logger/logger.dart';
// ignore: must_be_immutable
class CompoundWidgetTimer extends ConsumerStatefulWidget {
CompoundWidgetTimer({required this.item, super.key});
TimerItem item;
double getProgress() {
return (item.current / item.goal) / 60;
}
@override
ConsumerState<CompoundWidgetTimer> createState() =>
_CompoundWidgetTimerState();
}
class _CompoundWidgetTimerState extends ConsumerState<CompoundWidgetTimer>
with WidgetsBindingObserver {
_CompoundWidgetTimerState();
String button1text = '';
Timer _timer = Timer(Duration.zero, () => ());
DateTime calcDue() {
final now = DateTime.now();
const secondsToAdd = 10;
const duration = Duration(seconds: secondsToAdd);
final futureTime = now.add(duration);
Logger().i(futureTime);
return DateTime.now();
}
void handleButton1() {
//start timer from 0
const oneSec = Duration(seconds: 1);
if (widget.item.state == 'initial') {
widget.item = widget.item.copyWith(state: 'running');
ref.watch(homeControllerProvider).edit(widget.item);
_timer = Timer.periodic(
oneSec,
(Timer funcTimer) {
if (widget.item.current == (widget.item.goal * 60)) {
setState(() {
final now = DateTime.now();
widget.item = widget.item.copyWith(
state: 'completed',
completedOn: now.toString(),
);
ref.watch(homeControllerProvider).edit(widget.item);
funcTimer.cancel();
});
} else {
setState(() {
widget.item =
widget.item.copyWith(current: widget.item.current + 1);
ref.watch(homeControllerProvider).edit(widget.item);
});
}
},
);
}
// continue timer
else if (widget.item.state == 'paused') {
widget.item = widget.item.copyWith(state: 'running');
ref.watch(homeControllerProvider).edit(widget.item);
_timer = Timer.periodic(
const Duration(seconds: 1),
(Timer funcTimer) {
if (widget.item.current == (widget.item.goal * 60)) {
setState(() {
final now = DateTime.now();
widget.item = widget.item.copyWith(
state: 'completed',
completedOn: now.toString(),
);
ref.watch(homeControllerProvider).edit(widget.item);
funcTimer.cancel();
});
} else {
setState(() {
widget.item =
widget.item.copyWith(current: widget.item.current + 1);
ref.watch(homeControllerProvider).edit(widget.item);
});
}
},
);
} else if (widget.item.state == 'running') {
widget.item = widget.item.copyWith(state: 'paused');
ref.watch(homeControllerProvider).edit(widget.item);
_timer.cancel();
} else if (widget.item.state == 'completed') {}
}
void stopTimer() {
setState(() {
if (_timer.isActive) {
_timer.cancel();
}
widget.item = widget.item.copyWith(
current: 0,
state: 'initial',
);
ref.watch(homeControllerProvider).edit(widget.item);
});
}
String _formatCurrent() {
final hours = (widget.item.current / 3600).floor();
final minutes = ((widget.item.current - (hours * 3600)) / 60).floor();
return '$hours hours : $minutes minutes';
}
String _formattedTime() {
final minutesTotal = widget.item.goal;
final hours = (minutesTotal / 60).floor();
final minutes = minutesTotal - (hours * 60);
return '$hours hours : $minutes minutes';
}
void _toggleExpansion() {
setState(() {
widget.item = widget.item.copyWith(isExpanded: !widget.item.isExpanded);
ref.watch(homeControllerProvider).edit(widget.item);
});
}
String _getButton1Text(BuildContext context) {
if (widget.item.state == 'initial') {
return AppLocalizations.of(context)!.timerWidget_buttonStart;
} else if (widget.item.state == 'running') {
return AppLocalizations.of(context)!.timerWidget_buttonPause;
} else if (widget.item.state == 'paused') {
return AppLocalizations.of(context)!.timerWidget_buttonContinue;
}
return 'Done';
}
@override
Widget build(BuildContext context) {
ref
..watch(homeControllerProvider)
..watch(itemsProvider);
return Container(
margin: const EdgeInsets.only(left: 10, top: 10, right: 10, bottom: 10),
padding: const EdgeInsets.only(left: 10, top: 15, right: 10, bottom: 15),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: const BorderRadius.all(Radius.circular(7)),
boxShadow: [
BoxShadow(
color: const Color(0x00000000).withOpacity(0.25),
spreadRadius: 2,
blurRadius: 5,
// changes position of shadow
),
],
),
width: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!widget.item.isExpanded) ...[
Icon(
Icons.timer,
size: 20,
color: Theme.of(context).colorScheme.onPrimary,
),
Expanded(
flex: 2,
child: Container(
margin: const EdgeInsets.only(left: 10, right: 10),
height: 30,
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(5),
), // Rounded corners
child: LinearProgressIndicator(
value:
widget.getProgress(), // Progress value (0.0 - 1.0)
backgroundColor:
Colors.grey.withOpacity(0.5), // Background color
valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xffA4E8FD),
), // Progress color
),
),
),
),
Expanded(
child: Text(
widget.item.name,
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
// alignment: TextAlign.left,
),
),
),
],
if (widget.item.isExpanded) ...[
Expanded(
child: Text(
widget.item.name,
textScaler: const TextScaler.linear(2),
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
// alignment: TextAlign.left,
),
),
),
Expanded(
flex: 0,
child: IconButton(
icon: Icon(
Icons.settings,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () => _showSettingsMenu(ref),
),
),
],
Expanded(
flex: 0,
child: IconButton(
icon: Icon(
widget.item.isExpanded
? Icons.arrow_drop_up_outlined
: Icons.arrow_drop_down_circle_outlined,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: _toggleExpansion,
),
),
],
),
if (widget.item.isExpanded) ...[
// Additional child elements when expanded
SizedBox(
height: 300,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Stack(
children: <Widget>[
Container(
alignment: Alignment.center,
// color: Colors.blueAccent,
width: MediaQuery.of(context).size.width,
height: 250,
child: SizedBox(
height: 150,
// color: Colors.greenAccent,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
// ignore: lines_longer_than_80_chars
'${AppLocalizations.of(context)!.timerWidget_current}: ${_formatCurrent()} \n ${AppLocalizations.of(context)!.timerWidget_goal}: ${_formattedTime()}',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimary,
),
),
],
),
),
),
Positioned(
top: 10,
left: MediaQuery.of(context).size.width * 0.15,
width: MediaQuery.of(context).size.width * 0.60,
height: 220,
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.onPrimary,
strokeWidth: 7,
value: widget.getProgress(),
semanticsLabel: 'Circular progress indicator',
),
),
],
),
Expanded(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: handleButton1,
child: Text(
_getButton1Text(context),
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimary,
),
),
),
TextButton(
onPressed: stopTimer,
child: Text(
AppLocalizations.of(context)!
.timerWidget_buttonReset,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimary,
),
),
),
],
),
],
),
),
],
),
),
// Add more widgets here as needed
],
],
),
);
}
Future<void> _showSettingsMenu(WidgetRef ref) async {
logger.i('Opening settings');
final settingEntries = WidgetSettingsData(
entries: {
'name': SettingEntryText(
name: AppLocalizations.of(context)!.widgetSettings_name,
defaultValue: widget.item.name,
),
'duration': SettingEntryDuration(
name: AppLocalizations.of(context)!.timerWidgetSettings_duration,
defaultValue: widget.item.goal,
),
},
);
if (widget.item.state == 'running') {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
AppLocalizations.of(context)!.timerWidget_pausedForEdit,
),
),
);
} else {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
content: WidgetSettings(
entries: settingEntries,
),
actions: [
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () async {
ref.watch(itemsProvider);
final items = ref.watch(homeControllerProvider);
widget.item = widget.item.copyWith(isVisible: false);
setState(() {
items.edit(widget.item);
});
logger.i('Attempting delete');
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_deleteButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.bold,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
backgroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () {
_timer.cancel();
ref.watch(homeControllerProvider);
logger.i('Attempting edit of name and/or duration');
final name = settingEntries.getValue('name') as String;
logger.i('New name: $name');
final duration = settingEntries.getValue('duration') as int;
widget.item =
widget.item.copyWith(goal: duration, name: name);
setState(() {
ref.watch(homeControllerProvider).edit(widget.item);
});
logger.i('NAME AND DURATION SUCCESSFULLY UPDATED');
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_saveButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
),
],
);
},
);
}
}
}

View file

@ -0,0 +1,968 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/function_widgets/widget_settings_menu/setting_entry.dart';
import 'package:habitrack_app/function_widgets/widget_settings_menu/widget_settings.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
import 'package:habitrack_app/main.dart';
import 'package:habitrack_app/sembast/tasks_list.dart';
import 'package:intl/intl.dart';
const seperator = '_SEPARATOR_';
// ignore: must_be_immutable
class IndividualTodoWidget extends ConsumerStatefulWidget {
IndividualTodoWidget({
required this.todo,
required this.due,
required this.completed,
required this.item,
required this.isVisible,
required this.createdOn,
required this.completedOn,
super.key,
});
late String todo;
late bool completed;
late DateTime due;
late DateTime createdOn;
late DateTime completedOn;
late TasksItem item;
late bool isVisible;
@override
ConsumerState<IndividualTodoWidget> createState() =>
_IndividualTodoWidgetState();
String stringRepr() {
return [
todo,
due.toString().substring(0, 10),
createdOn,
completedOn,
completed,
isVisible,
].join(seperator);
}
void deleteItem(WidgetRef ref) {
final searchTerm = stringRepr();
if (completed == true) {
final completedTaskList = <String>[...item.completedTaskList];
//search in completed tasks list
for (var i = 0; i < completedTaskList.length; i++) {
if (searchTerm == completedTaskList.elementAt(i)) {
var newItem = searchTerm;
final toConvert = newItem.split(seperator);
final todo = toConvert.elementAtOrNull(0)!;
final due = DateTime.parse(toConvert.elementAtOrNull(1)!);
final createdOn = DateTime.parse(toConvert.elementAtOrNull(2)!);
final completedOn = DateTime.parse(toConvert.elementAtOrNull(3)!);
final completed =
toConvert.elementAtOrNull(4)!.toLowerCase() == 'true';
final dateFormat = DateFormat('yyyy-MM-dd');
final formattedDate = dateFormat.format(due);
newItem = [
todo,
formattedDate.substring(0, 10),
createdOn,
completedOn,
completed,
false,
].join(seperator);
completedTaskList[i] = newItem;
final controller = ref.watch(homeControllerProvider);
item = item.copyWith(completedTaskList: completedTaskList);
controller.edit(item);
}
}
} else {
final taskList = <String>[...item.taskList];
for (var i = 0; i < taskList.length; i++) {
if (searchTerm == taskList.elementAt(i)) {
var newItem = searchTerm;
final toConvert = newItem.split(seperator);
final todo = toConvert.elementAtOrNull(0)!;
final due = DateTime.parse(toConvert.elementAtOrNull(1)!);
final createdOn = DateTime.parse(toConvert.elementAtOrNull(2)!);
final completedOn = DateTime.parse(toConvert.elementAtOrNull(3)!);
final completed =
toConvert.elementAtOrNull(4)!.toLowerCase() == 'true';
final dateFormat = DateFormat('yyyy-MM-dd');
final formattedDate = dateFormat.format(due);
newItem = [
todo,
formattedDate,
createdOn,
completedOn,
completed,
false,
].join(seperator);
taskList[i] = newItem;
final controller = ref.watch(homeControllerProvider);
item = item.copyWith(taskList: taskList);
controller.edit(item);
} else {}
}
}
}
bool overdue() {
final now = DateTime.now();
if (DateTime(now.year, now.month, now.day).isAfter(due)) {
return true;
}
return false;
}
}
class _IndividualTodoWidgetState extends ConsumerState<IndividualTodoWidget> {
_IndividualTodoWidgetState();
void _toggleChecked(bool? param) {
if (widget.overdue() && !widget.completed) {
} else {
final searchTerm = widget.stringRepr();
final completedTaskList = <String>[...widget.item.completedTaskList];
final taskList = <String>[...widget.item.taskList];
if (widget.completed == true) {
//search in completed tasks list
for (var i = 0; i < completedTaskList.length; i++) {
if (searchTerm == completedTaskList.elementAt(i)) {
widget.completed = !widget.completed;
final replacementStr = widget.stringRepr();
completedTaskList.removeAt(i);
taskList.add(replacementStr);
final controller = ref.watch(homeControllerProvider);
widget.item = widget.item.copyWith(
taskList: taskList,
completedTaskList: completedTaskList,
);
setState(() {
controller.edit(widget.item);
});
}
}
} else {
for (var i = 0; i < taskList.length; i++) {
if (searchTerm == taskList.elementAt(i)) {
widget.completed = !widget.completed;
// ignore: cascade_invocations
widget.completedOn = DateTime.now();
final replacementStr = widget.stringRepr();
taskList.removeAt(i);
completedTaskList.add(replacementStr);
final controller = ref.watch(homeControllerProvider);
widget.item = widget.item.copyWith(
taskList: taskList,
completedTaskList: completedTaskList,
);
setState(
() {
controller.edit(widget.item);
},
);
} else {}
}
}
setState(() {});
}
}
void _editItem(String oldItem, String itemToAdd) {
final completedTaskList = <String>[...widget.item.completedTaskList];
final taskList = <String>[...widget.item.taskList];
if (widget.completed == true) {
//search in completed tasks list
for (var i = 0; i < completedTaskList.length; i++) {
if (oldItem == completedTaskList.elementAt(i)) {
completedTaskList.replaceRange(i, i + 1, [itemToAdd]);
final controller = ref.watch(homeControllerProvider);
widget.item =
widget.item.copyWith(completedTaskList: completedTaskList);
setState(() {
controller.edit(widget.item);
});
}
}
} else {
for (var i = 0; i < taskList.length; i++) {
if (oldItem == taskList.elementAt(i)) {
taskList.replaceRange(i, i + 1, [itemToAdd]);
final controller = ref.watch(homeControllerProvider);
widget.item = widget.item.copyWith(taskList: taskList);
setState(
() {
controller.edit(widget.item);
},
);
} else {}
}
}
}
Future<void> _editPopup(String oldItem) async {
final todoFieldEditController = TextEditingController(text: widget.todo);
DateTime? date = widget.due;
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
title: Text(
AppLocalizations.of(context)!.tasksWidget_editTask,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
scrollable: true,
content: Column(
children: <Widget>[
TextField(
controller: todoFieldEditController,
decoration: InputDecoration(hintText: widget.todo),
autofillHints: null,
),
TextButton(
child: Text(
AppLocalizations.of(context)!.taskSettings_duePicker,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
onPressed: () async {
final pickedDate = await showDatePicker(
context: context,
initialDate: widget.due,
firstDate: DateTime(2024, 0, 0),
lastDate: DateTime(2101),
);
date = pickedDate;
},
),
],
),
actions: <Widget>[
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_cancelButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
date ??= widget.due;
if (todoFieldEditController.text.isEmpty) {
todoFieldEditController.text = widget.todo;
}
Navigator.of(context).pop();
final dateFormat = DateFormat('yyyy-MM-dd');
final formattedDate = dateFormat.format(date!);
final newItem = [
todoFieldEditController.text,
formattedDate,
widget.createdOn,
widget.completedOn,
widget.completed,
widget.isVisible,
].join(seperator);
_editItem(oldItem, newItem);
todoFieldEditController.clear();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_saveButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
final df = DateFormat('dd.MM.yyyy');
var dateText =
// ignore: lines_longer_than_80_chars
'${AppLocalizations.of(context)!.taskSettings_due}: ${df.format(widget.due)}';
var backgroundColor = Theme.of(context).colorScheme.secondary;
if (widget.overdue() && !widget.completed) {
dateText = AppLocalizations.of(context)!.tasksWidget_overdue;
backgroundColor = Theme.of(context).colorScheme.tertiary;
}
return Container(
margin: const EdgeInsets.symmetric(vertical: 2),
padding: const EdgeInsets.all(3),
width: 300,
decoration: BoxDecoration(
border: Border.all(
width: 1.5,
),
color: backgroundColor,
borderRadius: const BorderRadius.all(Radius.circular(7)),
boxShadow: [
BoxShadow(
color: const Color(0x00000000).withOpacity(0.25),
spreadRadius: 1,
blurRadius: 2,
),
],
),
child: Row(
children: [
Expanded(
flex: 0,
child: Checkbox(
activeColor: Colors.green,
fillColor: WidgetStateProperty.resolveWith(
(states) {
if (states.contains(WidgetState.selected)) {
return null;
}
return Theme.of(context).colorScheme.onSecondary;
},
),
value: widget.completed,
onChanged: _toggleChecked,
),
),
Expanded(
flex: 2,
child: Column(
children: [
Text(
widget.todo,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
),
),
Text(
dateText,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
),
),
],
),
),
Expanded(
flex: 0,
child: IconButton(
onPressed: () => _editPopup(widget.stringRepr()),
icon: Icon(
Icons.edit,
color: Theme.of(context).colorScheme.onSecondary,
),
),
),
Expanded(
flex: 0,
child: IconButton(
onPressed: () => setState(() {
widget.deleteItem(ref);
}),
icon: Icon(
Icons.delete,
color: Theme.of(context).colorScheme.onSecondary,
),
),
),
],
),
);
}
}
// ignore: must_be_immutable
class CompoundWidgetTasks extends ConsumerStatefulWidget {
CompoundWidgetTasks({required this.item, super.key});
TasksItem item;
double getProgress() {
if (item.completedTaskList.isNotEmpty) {
return item.completedTaskList.length /
(item.taskList.length + item.completedTaskList.length);
}
return 0;
}
@override
ConsumerState<CompoundWidgetTasks> createState() =>
_CompoundWidgetTasksState();
}
class _CompoundWidgetTasksState extends ConsumerState<CompoundWidgetTasks> {
final TextEditingController _todoFieldController = TextEditingController();
void _toggleExpansion() {
setState(() {
widget.item = widget.item.copyWith(isExpanded: !widget.item.isExpanded);
ref.watch(homeControllerProvider).edit(widget.item);
});
}
IndividualTodoWidget _todoFromString(String input) {
final toConvert = input.split(seperator);
final todo = toConvert.elementAtOrNull(0)!;
final due = DateTime.parse(toConvert.elementAtOrNull(1)!);
final createdOn = DateTime.parse(toConvert.elementAtOrNull(2)!);
final completedOn = DateTime.parse(toConvert.elementAtOrNull(3)!);
final completed = toConvert.elementAtOrNull(4)!.toLowerCase() == 'true';
final isVisible = toConvert.elementAt(5).toLowerCase() == 'true';
final newItem = IndividualTodoWidget(
todo: todo,
due: due,
completed: completed,
completedOn: completedOn,
item: widget.item,
isVisible: isVisible,
createdOn: createdOn,
);
return newItem;
}
List<Widget> _buildTodoList() {
final items = <IndividualTodoWidget>[];
final taskList = <String>[...widget.item.taskList];
final completedTaskList = <String>[...widget.item.completedTaskList];
taskList.sort(
(a, b) =>
DateTime.parse(a.split(seperator).elementAtOrNull(1)!).compareTo(
DateTime.parse(b.split(seperator).elementAtOrNull(1)!),
),
);
completedTaskList.sort(
(a, b) =>
DateTime.parse(a.split(seperator).elementAtOrNull(1)!).compareTo(
DateTime.parse(b.split(seperator).elementAtOrNull(1)!),
),
);
for (var i = 0; i < taskList.length; i++) {
final todoAsString = taskList.elementAt(i);
final newItem = _todoFromString(todoAsString);
if (newItem.isVisible) {
items.add(newItem);
}
}
for (var i = 0; i < completedTaskList.length; i++) {
final todoAsString = completedTaskList.elementAt(i);
final newItem = _todoFromString(todoAsString);
if (newItem.isVisible) {
items.add(newItem);
}
}
return items;
}
void _addItem(String toAdd) {
logger.i('ITEM TO ADD: $toAdd');
final controller = ref.watch(homeControllerProvider);
final toCopy = <String>[...widget.item.taskList];
// ignore: cascade_invocations
toCopy.add(toAdd);
widget.item = widget.item.copyWith(taskList: toCopy);
setState(() {
controller.edit(widget.item);
});
}
@override
Widget build(BuildContext context) {
ref.watch(itemsProvider);
return Container(
margin: const EdgeInsets.only(left: 10, top: 10, right: 10, bottom: 10),
padding: const EdgeInsets.only(left: 10, top: 15, right: 10, bottom: 15),
//height: 100,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: const BorderRadius.all(Radius.circular(7)),
boxShadow: [
BoxShadow(
color: const Color(0x00000000).withOpacity(0.25),
spreadRadius: 2,
blurRadius: 5,
// changes position of shadow
),
],
),
width: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!widget.item.isExpanded) ...[
Icon(
Icons.task,
size: 20,
color: Theme.of(context).colorScheme.onPrimary,
),
Expanded(
flex: 2,
child: Container(
margin: const EdgeInsets.only(left: 10, right: 10),
// Width of the progress bar
height: 30, // Height of the progress bar
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(5),
), // Rounded corners
child: LinearProgressIndicator(
value:
widget.getProgress(), // Progress value (0.0 - 1.0)
backgroundColor:
Colors.grey.withOpacity(0.5), // Background color
valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xffA4E8FD),
), // Progress color
),
),
),
),
Expanded(
child: Text(
widget.item.name,
style: TextStyle(
// fontSize: 12,
// fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
if (widget.item.isExpanded) ...[
Expanded(
child: Text(
widget.item.name,
textScaler: const TextScaler.linear(2),
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
Expanded(
flex: 0,
child: IconButton(
icon: Icon(
Icons.settings,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () => _showSettingsMenu(ref, context),
),
),
],
Expanded(
flex: 0,
child: IconButton(
icon: Icon(
widget.item.isExpanded
? Icons.arrow_drop_up_outlined
: Icons.arrow_drop_down_circle_outlined,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: _toggleExpansion,
),
//child: Icon(Icons.arrow_drop_down_circle_outlined),
),
],
),
if (widget.item.isExpanded) ...[
// Additional child elements when expanded
SizedBox(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Column(
children: [
OutlinedButton(
onPressed: _addPopup,
child: Row(
children: [
Icon(
Icons.add_task_outlined,
color: Theme.of(context).colorScheme.onPrimary,
),
Text(
// ignore: lines_longer_than_80_chars
' ${AppLocalizations.of(context)!.tasksWidget_addTaskButtonLabel}',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
Theme.of(context).colorScheme.onPrimary,
),
),
],
),
),
ListView(
padding: const EdgeInsets.symmetric(vertical: 5),
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
children: _buildTodoList(),
),
if (anyTasksCompleted()) ...[
OutlinedButton(
onPressed: _deleteCompletedTasks,
child: Text(
AppLocalizations.of(context)!
.tasksWidget_deleteDone,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
Theme.of(context).colorScheme.onPrimary,
),
),
),
],
/* if (anyTasksOverdue()) ...[
OutlinedButton(
onPressed: _rescheduleOverdueTasks,
child: Text(
AppLocalizations.of(context)!
.tasksWidget_rescheduleOverdue,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color:
Theme.of(context).colorScheme.onPrimary,
),
),
),
],*/
],
),
],
),
),
],
],
),
);
}
bool anyTasksCompleted() {
for (final item in widget.item.completedTaskList) {
final toConvert = item.split(seperator);
final isVisible = toConvert.elementAt(5).toLowerCase() == 'true';
if (isVisible) {
return true;
}
}
return false;
}
void _deleteCompletedTasks() {
final controller = ref.watch(homeControllerProvider);
final newCompletedTaskList = <String>[]; // widget.item.completedTaskList;
for (final itemString in widget.item.completedTaskList) {
final oldItem = _todoFromString(itemString);
final dateFormat = DateFormat('yyyy-MM-dd');
final newItem = [
oldItem.todo,
dateFormat.format(oldItem.due),
oldItem.createdOn,
oldItem.completedOn,
oldItem.completed,
false,
].join(seperator);
newCompletedTaskList.add(newItem);
}
widget.item = widget.item.copyWith(completedTaskList: newCompletedTaskList);
controller.edit(widget.item);
}
bool anyTasksOverdue() {
final taskList = <String>[...widget.item.taskList];
for (final taskString in taskList) {
if (_todoFromString(taskString).overdue()) {
return true;
}
}
return false;
}
// Used for the Reschedule Task Button
// ignore: unused_element
void _rescheduleOverdueTasks() {
final taskList = <String>[...widget.item.taskList];
for (var i = 0; i < taskList.length; i++) {
final taskString = taskList[i];
if (_todoFromString(taskString).overdue()) {
final oldItem = _todoFromString(taskString);
final now = DateTime.now();
final newItem = IndividualTodoWidget(
todo: oldItem.todo,
due: DateTime(now.year, now.month, now.day),
createdOn: DateTime.now(),
completedOn: DateTime.now(),
completed: oldItem.completed,
item: widget.item,
isVisible: true,
);
taskList.replaceRange(i, i + 1, [newItem.stringRepr()]);
final controller = ref.watch(homeControllerProvider);
widget.item = widget.item.copyWith(taskList: taskList);
setState(
() {
controller.edit(widget.item);
},
);
}
}
}
Future<void> _addPopup() async {
DateTime? date = DateTime.now();
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
title: Text(
AppLocalizations.of(context)!.tasksWidget_addTaskButtonLabel,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
scrollable: true,
content: Column(
children: <Widget>[
TextField(
controller: _todoFieldController,
autofillHints: null,
decoration: InputDecoration(
hintText: AppLocalizations.of(context)!.taskSettings_name,
),
),
TextButton(
child: Text(
AppLocalizations.of(context)!.taskSettings_duePicker,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
onPressed: () async {
final pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(), //get today's date
firstDate: DateTime.now(),
//DateTime.now() - not to allow to choose before today
lastDate: DateTime(2101),
);
date = pickedDate;
},
),
],
),
actions: <Widget>[
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_cancelButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
//DateTime due = DateTime.parse(formattedDate);
// ignore: cascade_invocations
if (_todoFieldController.text.isEmpty) {
_todoFieldController.text = 'todo';
}
if (date != null && _todoFieldController.text.isNotEmpty) {
final dateFormat = DateFormat('yyyy-MM-dd');
final formattedDate = dateFormat.format(date!);
Navigator.of(context).pop();
final itemToAdd = [
_todoFieldController.text,
formattedDate,
DateTime.now(),
DateTime.now(),
false,
true,
].join(seperator);
_addItem(itemToAdd);
_todoFieldController.clear();
}
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_saveButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
);
},
);
}
Future<void> _showSettingsMenu(WidgetRef ref, BuildContext context) async {
final settingEntries = WidgetSettingsData(
entries: {
'name': SettingEntryText(
name: AppLocalizations.of(context)!.widgetSettings_name,
defaultValue: widget.item.name,
),
},
);
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
content: WidgetSettings(
entries: settingEntries,
),
actions: [
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
ref
.watch(homeControllerProvider)
.edit(widget.item.copyWith(isVisible: false));
// await controller.delete(widget.item.id);
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_deleteButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.bold,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
final controller = ref.watch(homeControllerProvider);
final name = settingEntries.getValue('name') as String;
widget.item = widget.item.copyWith(name: name);
setState(() {
widget.item = widget.item.copyWith(name: name);
controller.edit(widget.item);
});
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_saveButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
),
],
);
},
);
}
}

View file

@ -0,0 +1,491 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/function_widgets/widget_settings_menu/setting_entry.dart';
import 'package:habitrack_app/function_widgets/widget_settings_menu/widget_settings.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
import 'package:habitrack_app/main.dart';
import 'package:habitrack_app/sembast/hydration.dart';
import 'package:liquid_progress_indicator_v2/liquid_progress_indicator.dart';
// ignore: must_be_immutable
class CompoundWidgetWater extends ConsumerStatefulWidget {
CompoundWidgetWater({required this.item, super.key});
late Hydration item;
double getProgress() {
return item.current / item.goal;
}
@override
ConsumerState<CompoundWidgetWater> createState() =>
_CompoundWidgetWaterState();
}
class _CompoundWidgetWaterState extends ConsumerState<CompoundWidgetWater> {
void _toggleExpansion() {
setState(() {
widget.item = widget.item.copyWith(isExpanded: !widget.item.isExpanded);
ref.watch(homeControllerProvider).edit(widget.item);
});
}
void _addQuantity(int toAdd) {
setState(() {
final controller = ref.watch(homeControllerProvider);
final oldval = widget.item.current;
logger.i('Old item: ${widget.item}');
widget.item = widget.item.copyWith(current: oldval + toAdd);
logger.i('New item: ${widget.item}');
controller.edit(widget.item);
});
}
@override
Widget build(BuildContext context) {
ref.watch(itemsProvider);
return Container(
margin: const EdgeInsets.only(left: 10, top: 10, right: 10, bottom: 10),
padding: const EdgeInsets.only(left: 10, top: 15, right: 10, bottom: 15),
//height: 100,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: const BorderRadius.all(Radius.circular(7)),
boxShadow: [
BoxShadow(
color: const Color(0x00000000).withOpacity(0.25),
spreadRadius: 2,
blurRadius: 5,
// changes position of shadow
),
],
),
width: double.infinity,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!widget.item.isExpanded) ...[
Icon(
Icons.local_drink,
size: 20,
color: Theme.of(context).colorScheme.onPrimary,
),
Expanded(
flex: 2,
child: Container(
margin: const EdgeInsets.only(left: 10, right: 10),
// Width of the progress bar
height: 30, // Height of the progress bar
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(5),
), // Rounded corners
child: LinearProgressIndicator(
value:
widget.getProgress(), // Progress value (0.0 - 1.0)
backgroundColor:
Colors.grey.withOpacity(0.5), // Background color
valueColor: const AlwaysStoppedAnimation<Color>(
Color(0xffA4E8FD),
), // Progress color
),
),
),
),
Expanded(
child: Text(
widget.item.name,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
if (widget.item.isExpanded) ...[
Expanded(
child: Text(
widget.item.name,
textScaler: const TextScaler.linear(2),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
Expanded(
flex: 0,
child: IconButton(
icon: Icon(
Icons.settings,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () => _showSettingsMenu(ref),
),
//child: Icon(Icons.arrow_drop_down_circle_outlined),
),
],
Expanded(
flex: 0,
child: IconButton(
icon: Icon(
widget.item.isExpanded
? Icons.arrow_drop_up_outlined
: Icons.arrow_drop_down_circle_outlined,
color: Theme.of(context).colorScheme.onPrimary,
),
onPressed: _toggleExpansion,
),
//child: Icon(Icons.arrow_drop_down_circle_outlined),
),
],
),
if (widget.item.isExpanded) ...[
// Additional child elements when expanded
SizedBox(
height: 300,
width: MediaQuery.of(context).size.width,
child: Column(
children: [
//INSERT WIDGET SPECIFIC STUFF HERE
SizedBox(
height: 300,
width: MediaQuery.of(context).size.width,
child: Row(
children: [
Expanded(
child: OvershootLiquidLinearProgressIndicator(
current: widget.item.current,
goal: widget.item.goal,
),
),
Expanded(
child: Column(
children: [
Text(
'${widget.item.current / 1000} / ${widget.item.goal / 1000} L',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimary,
),
),
Expanded(
child: OutlinedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(
Theme.of(context).colorScheme.secondary,
),
),
onPressed: () => {
_addQuantity(
widget.item.button1Amount,
),
},
child: Text(
'${widget.item.button1Amount} mL',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.onSecondary,
),
),
),
),
Expanded(
child: OutlinedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(
Theme.of(context).colorScheme.secondary,
),
),
onPressed: () => {
_addQuantity(
widget.item.button2Amount,
),
},
child: Text(
'${widget.item.button2Amount} mL',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimary,
),
),
),
),
Expanded(
child: OutlinedButton(
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(
Theme.of(context).colorScheme.secondary,
),
),
onPressed: _showPopupCustomAmount,
child: Text(
AppLocalizations.of(context)!
.waterWidget_customAmountButton,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimary,
),
),
),
),
],
),
),
],
),
),
],
),
),
// Add more widgets here as needed
],
],
),
);
}
Future<void> _showSettingsMenu(WidgetRef ref) async {
final settingEntries = WidgetSettingsData(
entries: {
'name': SettingEntryText(
name: AppLocalizations.of(context)!.widgetSettings_name,
defaultValue: widget.item.name,
),
'button1Amount': SettingEntryNumeric(
name: AppLocalizations.of(context)!.waterWidgetSettings_button1,
defaultValue: widget.item.button1Amount,
),
'button2Amount': SettingEntryNumeric(
name: AppLocalizations.of(context)!.waterWidgetSettings_button2,
defaultValue: widget.item.button2Amount,
),
'targetGoal': SettingEntrySlider(
name: AppLocalizations.of(context)!.waterWidgetSettings_goal,
defaultValue: double.parse(widget.item.goal.toString()),
divisions: 80,
topValue: 4000,
),
'currentAmount': SettingEntryNumeric(
name: AppLocalizations.of(context)!.waterWidgetSettings_current,
defaultValue: widget.item.current,
),
},
);
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
content: WidgetSettings(
entries: settingEntries,
),
actions: [
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Colors.red,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
//widget.parent.delete(ref);
ref.watch(itemsProvider);
final controller = ref.watch(homeControllerProvider);
widget.item = widget.item.copyWith(isVisible: false);
controller.edit(widget.item);
logger.i('Attempting delete');
// ignore: use_build_context_synchronously
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_deleteButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.bold,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.onPrimary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
final controller = ref.watch(homeControllerProvider);
logger.i('Attempting edit of water widget stuff');
// widget.settingEntries.notify();
final name = settingEntries.getValue('name') as String;
final currentAmount =
settingEntries.getValue('currentAmount') as int;
final targetGoal =
(settingEntries.getValue('targetGoal') as double).round();
final button1Amount =
settingEntries.getValue('button1Amount') as int;
final button2Amount =
settingEntries.getValue('button2Amount') as int;
widget.item = widget.item.copyWith(
name: name,
current: currentAmount,
goal: targetGoal,
button1Amount: button1Amount,
button2Amount: button2Amount,
);
controller.edit(widget.item);
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_saveButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
),
),
),
],
);
},
);
}
Future<void> _showPopupCustomAmount() async {
final customFieldController = TextEditingController();
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
title: Text(
AppLocalizations.of(context)!.waterWidget_customAmountMessage,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
content: Column(
children: <Widget>[
TextField(
controller: customFieldController,
keyboardType: TextInputType.number,
autofocus: true,
),
],
),
actions: <Widget>[
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
backgroundColor: Theme.of(context).colorScheme.primary,
),
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_cancelButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
Navigator.of(context).pop();
final result = int.tryParse(customFieldController.text) ?? 0;
_addQuantity(result);
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_saveButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
);
},
);
}
}
class OvershootLiquidLinearProgressIndicator extends StatelessWidget {
const OvershootLiquidLinearProgressIndicator({
required this.current,
required this.goal,
super.key,
});
final int current;
final int goal;
@override
Widget build(BuildContext context) {
var mainColor = Colors.blue;
var backColor = Colors.white;
var value = 0.0;
if (current < goal) {
mainColor = Colors.blue;
backColor = Colors.white;
value = (current - goal * 0) / goal;
} else if (current < goal * 2) {
mainColor = Colors.orange;
backColor = Colors.blue;
value = (current - goal * 1) / goal;
} else {
mainColor = Colors.red;
backColor = Colors.orange;
value = (current - goal * 2) / goal;
}
return LiquidLinearProgressIndicator(
value: value,
valueColor: AlwaysStoppedAnimation(
mainColor,
),
backgroundColor: backColor,
borderColor: Colors.black,
borderWidth: 3,
borderRadius: 12,
direction: Axis.vertical,
);
}
}

View file

@ -0,0 +1,375 @@
import 'package:duration_picker/duration_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:habitrack_app/main.dart';
import 'package:intl/intl.dart';
abstract class SettingEntry extends StatefulWidget {
const SettingEntry({required this.name, super.key});
final String name;
@override
State<StatefulWidget> createState() => _SettingEntryState();
dynamic getValue() {
throw UnimplementedError();
}
void setValue(dynamic newValue) {
throw UnimplementedError();
}
}
class _SettingEntryState extends State<SettingEntry> {
@override
Widget build(BuildContext context) {
return const Text('Abstract, not implemented');
}
}
// ###############################TEXT#########################################
class SettingEntryText extends SettingEntry {
SettingEntryText({
required super.name,
this.defaultValue = 'Some Text',
super.key,
}) {
setValue(defaultValue);
}
final TextEditingController valueController = TextEditingController();
final String defaultValue;
@override
State<SettingEntryText> createState() => _SettingEntryTextState();
@override
String getValue() {
logger.i('GETTING VALUE ${valueController.text}');
return valueController.text;
}
@override
void setValue(dynamic newValue) {
if (newValue is! String) {
throw Exception('Value of SettingEntryText can only be a String!');
}
valueController.text = newValue;
}
}
class _SettingEntryTextState extends State<SettingEntryText> {
void monitorTextChange() {
final text = widget.valueController.text;
logger.i('TEXT: $text');
}
@override
void initState() {
super.initState();
// Start listening to changes.
widget.valueController.addListener(monitorTextChange);
}
@override
void dispose() {
// Clean up the controller when the widget is removed from the widget tree.
// This also removes the _printLatestValue listener.
widget.valueController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
alignment: AlignmentDirectional.bottomStart,
margin: const EdgeInsets.only(top: 7.5, bottom: 7.5),
// color: Colors.black,
child: Text(
widget.name,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
TextField(
keyboardType: TextInputType.text,
controller: widget.valueController,
),
],
);
}
}
// ###############################NUMERIC#######################################
class SettingEntryNumeric extends SettingEntry {
SettingEntryNumeric({required super.name, int defaultValue = 0, super.key}) {
setValue(defaultValue);
}
final TextEditingController valueController = TextEditingController();
@override
State<SettingEntryNumeric> createState() => _SettingEntryNumericState();
@override
int getValue() {
return int.parse(valueController.text);
}
@override
void setValue(dynamic newValue) {
if (newValue is! int) {
throw Exception('Value of SettingEntryNumeric can only be an integer!');
}
valueController.text = newValue.toString();
}
}
class _SettingEntryNumericState extends State<SettingEntryNumeric> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(widget.name),
TextField(
keyboardType: TextInputType.number,
controller: widget.valueController,
),
],
);
}
}
// ###############################SLIDER########################################
class SettingEntrySlider extends SettingEntry {
SettingEntrySlider({
required this.topValue,
required this.divisions,
required super.name,
double defaultValue = 0.0,
super.key,
}) {
setValue(defaultValue);
}
final double topValue;
final int divisions;
final value = DoubleSaver();
@override
State<SettingEntrySlider> createState() => _SettingEntrySliderState();
@override
double getValue() {
return value.v;
}
@override
void setValue(dynamic newValue) {
if (newValue is! double) {
throw Exception('Value of SettingEntrySlider can only be a Double!');
}
value.v = newValue;
}
}
class DoubleSaver {
double v = 0;
}
class _SettingEntrySliderState extends State<SettingEntrySlider> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
//Expanded(flex: 2, child: Text(widget.name)),
Text(widget.name),
Text(' ${widget.getValue()}'),
],
),
Row(
children: [
const Text('0 '),
Expanded(
child: Slider(
value: widget.getValue(),
divisions: widget.divisions,
max: widget.topValue,
onChanged: sliderChange,
),
),
Text(' ${widget.topValue}'),
],
),
],
);
}
void sliderChange(double newValue) {
setState(() {
widget.setValue(newValue);
});
}
}
// #############################DURATION#######################################
class IntSaver {
IntSaver({this.v = 30});
int v;
}
class SettingEntryDuration extends SettingEntry {
SettingEntryDuration({
required super.name,
required this.defaultValue,
super.key,
}) {
val = IntSaver(v: defaultValue);
}
final int defaultValue;
late final IntSaver val;
@override
State<SettingEntryDuration> createState() => _SettingEntryDurationState();
@override
int getValue() {
return val.v;
}
@override
void setValue(dynamic newValue) {
if (newValue is! int) {
throw Exception('Value of SettingEntryDuration can only be a Double!');
}
val.v = newValue;
}
}
class _SettingEntryDurationState extends State<SettingEntryDuration> {
@override
Widget build(BuildContext context) {
final textCurrent =
AppLocalizations.of(context)!.widgetSettings_durationPickerCurrent;
return Column(
children: [
Container(
alignment: AlignmentDirectional.bottomStart,
margin: const EdgeInsets.only(top: 7.5, bottom: 7.5),
child: Text(
'$textCurrent ${widget.getValue()}m',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
),
onPressed: () async {
var resultingDuration = await showDurationPicker(
context: context,
initialTime: Duration(minutes: widget.val.v),
);
resultingDuration ??= Duration(minutes: widget.defaultValue);
// ignore: use_build_context_synchronously
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Chose duration: ${resultingDuration.inMinutes}'),
),
);
widget.setValue(resultingDuration.inMinutes);
setState(() {});
},
child: Text(
AppLocalizations.of(context)!.widgetSettings_durationPickerButton,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
);
}
}
// ###############################DATE#########################################
class DateSaver {
DateTime v = DateTime.now();
}
class SettingEntryDate extends SettingEntry {
SettingEntryDate({required super.name, DateTime? defaultValue, super.key}) {
this.defaultValue = defaultValue ?? DateTime.now();
setValue(defaultValue);
}
final date = DateSaver();
late final DateTime defaultValue;
@override
State<SettingEntryDate> createState() => _SettingEntryDateState();
@override
DateTime getValue() {
return date.v;
}
@override
void setValue(dynamic newValue) {
if (newValue is! DateTime) {
throw Exception('Value of SettingEntryNumeric can only be a DateTime!');
}
date.v = newValue;
}
}
class _SettingEntryDateState extends State<SettingEntryDate> {
@override
Widget build(BuildContext context) {
final dateFormat = DateFormat('dd. MMMM');
return Column(
children: [
Text(
'${widget.name}: ${dateFormat.format(widget.date.v)} ',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.primaryContainer,
),
),
TextButton(
onPressed: datePicker,
child: const Text('Choose a date'),
),
],
);
}
Future<void> datePicker() async {
final pickedDate = await showDatePicker(
context: context,
initialDate: widget.date.v, //get today's date
firstDate: DateTime.now(),
//DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101),
);
final cleanDate = pickedDate ?? widget.date.v;
widget.setValue(cleanDate);
setState(() {});
}
}

View file

@ -0,0 +1,79 @@
// ignore_for_file: public_member_api_docs
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:habitrack_app/function_widgets/widget_settings_menu/setting_entry.dart';
class WidgetSettingsData {
WidgetSettingsData({required this.entries});
final Map<State, void Function(void Function())> listeners = {};
final Map<String, SettingEntry> entries;
void addListener(State toAdd, void Function(void Function()) updateCall) {
listeners[toAdd] = updateCall;
}
void removeListener(State toRemove) {
listeners.remove(toRemove);
}
void notify() {
for (final listener in listeners.keys) {
listeners[listener]!(() => ());
}
}
dynamic getValue(String key) {
return entries[key]?.getValue();
}
void setValue(String key, dynamic value) {
entries[key]?.setValue(value);
notify();
}
List<SettingEntry> asList() {
return entries.values.toList();
}
}
class WidgetSettings extends StatefulWidget {
const WidgetSettings({
required this.entries,
super.key,
});
final WidgetSettingsData entries;
@override
State<WidgetSettings> createState() => _WidgetSettingsState();
}
class _WidgetSettingsState extends State<WidgetSettings> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: Theme.of(context).colorScheme.onPrimaryContainer,),
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
title: Text(
AppLocalizations.of(context)!.settingsHeader,
textScaler: const TextScaler.linear(1.5),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
body: ColoredBox(
color: Theme.of(context).colorScheme.primaryContainer,
child: ListView(
children: widget.entries.asList(),
),
),
);
}
}

View file

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class ScaffoldWithBottomNavigationBar extends StatelessWidget {
const ScaffoldWithBottomNavigationBar({
required this.navigationShell,
Key? key,
}) : super(key: key ?? const ValueKey<String>('ScaffoldWithNavBar'));
final StatefulNavigationShell navigationShell;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(child: navigationShell),
bottomNavigationBar: BottomNavigationBar(
fixedColor: Theme.of(context).colorScheme.onPrimary,
backgroundColor: Theme.of(context).colorScheme.secondary,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.list),
label: 'Widget Wall',
),
BottomNavigationBarItem(
icon: Icon(Icons.dashboard),
label: 'Dashboard',
),
],
currentIndex: navigationShell.currentIndex,
onTap: (int index) => _onTap(context, index),
),
);
}
void _onTap(BuildContext context, int index) {
navigationShell.goBranch(
index,
initialLocation: index == navigationShell.currentIndex,
);
}
}

View file

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:habitrack_app/infrastructure/bottom_navigation.dart';
import 'package:habitrack_app/pages/dashboard_page.dart';
import 'package:habitrack_app/pages/widget_page.dart';
// navigators, root and each destination of bottom navigation bar
final _rootNavigatorKey = GlobalKey<NavigatorState>();
final _shellNavigatorWidgetWallKey =
GlobalKey<NavigatorState>(debugLabel: 'widgetWall');
final _shellNavigatorDashboardKey =
GlobalKey<NavigatorState>(debugLabel: 'dashboard');
const String _widgetWallPath = '/widgetWall';
const String _dashboardPath = '/dashboard';
final goRouter = GoRouter(
initialLocation: _widgetWallPath,
navigatorKey: _rootNavigatorKey,
debugLogDiagnostics: true,
routes: [
StatefulShellRoute.indexedStack(
builder: (context, state, navigationShell) {
return ScaffoldWithBottomNavigationBar(
navigationShell: navigationShell,
);
},
branches: [
StatefulShellBranch(
navigatorKey: _shellNavigatorWidgetWallKey,
routes: [
GoRoute(
path: _widgetWallPath,
pageBuilder: (context, state) => const NoTransitionPage(
child: WidgetPage(),
),
),
],
),
StatefulShellBranch(
navigatorKey: _shellNavigatorDashboardKey,
routes: [
GoRoute(
path: _dashboardPath,
pageBuilder: (context, state) => const NoTransitionPage(
child: DashboardPage(),
),
),
],
),
],
),
],
);

View file

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:habitrack_app/infrastructure/widget_wall/add_widget_menu.dart';
class AddWidgetButton extends StatelessWidget {
const AddWidgetButton({super.key});
@override
Widget build(BuildContext context) {
return OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
),
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (context) => const AddWidgetMenu(),
),
),
},
child: Icon(
Icons.add,
color: Theme.of(context).colorScheme.onPrimary,
),
);
}
}

View file

@ -0,0 +1,150 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
import 'package:habitrack_app/main.dart';
import 'package:habitrack_app/sembast/hydration.dart';
import 'package:habitrack_app/sembast/tasks_list.dart';
import 'package:habitrack_app/sembast/timer.dart';
//Add Widget to List- Button Class ######################################
class _AddWidgetToList extends ConsumerWidget {
const _AddWidgetToList({
required this.toAdd,
required this.buttonText,
required this.iconData,
});
final dynamic toAdd;
final String buttonText;
final IconData iconData;
void _buttonFunc(BuildContext context, WidgetRef ref) {
//ref.read(widgetListNotifierProvider.notifier).addWidget(toAdd());
//ref.read(homeControllerProvider).add()
ref.watch(homeControllerProvider).add(toAdd);
Navigator.pop(context);
logger.i('Button Func Called');
}
@override
Widget build(BuildContext context, WidgetRef ref) {
return Container(
alignment: Alignment.center,
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
boxShadow: [
BoxShadow(
color: const Color(0x00000000).withOpacity(0.25),
spreadRadius: 1,
blurRadius: 2,
),
],
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
width: 300,
child: TextButton(
onPressed: () => {_buttonFunc(context, ref)},
child: Row(
children: [
Icon(
iconData,
color: Theme.of(context).colorScheme.onPrimary,
),
Text(
buttonText,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
],
),
),
);
}
}
// AddWidgetMenu ##########################################################
class AddWidgetMenu extends StatelessWidget {
const AddWidgetMenu({super.key});
@override
Widget build(BuildContext context) {
//AddWidgetToList - Button-Instances #######################################
final addWaterWidget = _AddWidgetToList(
toAdd: Hydration(
createdOn: DateTime.now().toString(),
completedOn: '',
isVisible: true,
widgetType: 'Hydration',
name: AppLocalizations.of(context)!.waterWidget_defaultName,
button1Amount: 100,
button2Amount: 250,
goal: 2500,
current: 0,
isExpanded: false,
),
buttonText: AppLocalizations.of(context)!.addWidget_water,
iconData: Icons.local_drink,
);
final addCompoundTimerWidget = _AddWidgetToList(
toAdd: TimerItem(
widgetType: 'Timer',
name: AppLocalizations.of(context)!.timerWidget_defaultName,
current: 0,
goal: 90,
isExpanded: false,
createdOn: DateTime.now().toString(),
completedOn: '',
isVisible: true,
state: 'initial',
),
buttonText: AppLocalizations.of(context)!.addWidget_timer,
iconData: Icons.timer,
);
final addTaskWidget = _AddWidgetToList(
toAdd: TasksItem(
isVisible: true,
widgetType: 'TODO',
name: AppLocalizations.of(context)!.tasksWidget_defaultName,
isExpanded: false,
taskList: [],
completedTaskList: [],
),
buttonText: AppLocalizations.of(context)!.addWidget_tasks,
iconData: Icons.task,
);
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: Theme.of(context).colorScheme.onPrimary,
),
title: Text(
AppLocalizations.of(context)!.addWidgetHeader,
textScaler: const TextScaler.linear(1.5),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Theme.of(context).colorScheme.primary,
),
body: Container(
color: Theme.of(context).colorScheme.primaryContainer,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
addWaterWidget,
addTaskWidget,
addCompoundTimerWidget,
],
),
),
);
}
}

View file

@ -0,0 +1,109 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
class DataPoint {
DataPoint({required this.date, required this.progress});
DateTime date;
double progress;
}
class GraphWidget extends ConsumerStatefulWidget {
GraphWidget({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _GraphWidgetState();
final entries = <DataPoint>[
DataPoint(date: DateTime(2024, 7, 4), progress: 1),
DataPoint(date: DateTime(2024, 7, 5), progress: 0.5),
DataPoint(date: DateTime(2024, 7, 6), progress: 1.25),
DataPoint(date: DateTime(2024, 7, 7), progress: 0.75),
];
}
class _GraphWidgetState extends ConsumerState<GraphWidget> {
@override
Widget build(BuildContext context) {
final firstDate = widget.entries[0].date;
final flSpots = <FlSpot>[];
for (final dataPoint in widget.entries) {
final xValue = _daysBetween(firstDate, dataPoint.date).toDouble();
final lcbd = FlSpot(xValue, dataPoint.progress);
flSpots.add(lcbd);
}
final df = DateFormat('dd. MMMM');
return Column(
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.85,
height: MediaQuery.of(context).size.height * 0.5,
child: LineChart(
LineChartData(
minY: 0,
maxY: 1.5,
lineBarsData: [
LineChartBarData(spots: flSpots, isCurved: true),
],
gridData: const FlGridData(show: false),
extraLinesData: ExtraLinesData(
horizontalLines: [
HorizontalLine(
y: 1,
color: Colors.red,
),
],
),
titlesData: FlTitlesData(
rightTitles: const AxisTitles(
sideTitles: SideTitles(
reservedSize: 30,
interval: 20,
),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(
interval: 20,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
getTitlesWidget: (value, meta) => Text(value.toString()),
interval: 0.25,
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
getTitlesWidget: (value, meta) => Text(
df.format(
DateTime(
firstDate.year,
firstDate.month,
firstDate.day,
).add(Duration(days: value.round())),
),
),
showTitles: true,
interval: 1,
),
),
),
),
),
),
const Text('You worked for X hours this past week'),
const Text('Out of a total of Y hours planned'),
],
);
}
int _daysBetween(DateTime from, DateTime to) {
final d1 = DateTime(from.year, from.month, from.day);
final d2 = DateTime(to.year, to.month, to.day);
return (d2.difference(d1).inHours / 24).round();
}
}

View file

@ -0,0 +1,27 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/sembast/item_repository.dart';
import 'package:habitrack_app/sembast/sembast_item_repository.dart';
final homeControllerProvider = Provider(
(ref) => HomeController(
itemRepository: ref.watch(itemRepositoryProvider),
),
);
class HomeController {
HomeController({required this.itemRepository});
final ItemRepository itemRepository;
Future<void> delete(int id) async {
await itemRepository.deleteItem(id);
}
Future<void> edit(dynamic item) async {
await itemRepository.updateItem(item);
}
Future<void> add(dynamic newItem) async {
await itemRepository.insertItem(newItem);
}
}

View file

@ -0,0 +1,6 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/sembast/sembast_item_repository.dart';
final itemsProvider = StreamProvider(
(ref) => ref.watch(itemRepositoryProvider).getAllItemsStream(),
);

View file

@ -0,0 +1,111 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/function_widgets/compound_widgets/compound_timer_widget.dart';
import 'package:habitrack_app/function_widgets/compound_widgets/compound_widget_tasks.dart';
import 'package:habitrack_app/function_widgets/compound_widgets/compound_widget_water.dart';
import 'package:habitrack_app/infrastructure/widget_wall/add_widget_button.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
import 'package:habitrack_app/main.dart';
import 'package:habitrack_app/sembast/hydration.dart';
import 'package:habitrack_app/sembast/tasks_list.dart';
import 'package:habitrack_app/sembast/timer.dart';
/// Displays detailed information about a SampleItem.
class WidgetWall extends ConsumerStatefulWidget {
const WidgetWall({super.key});
@override
ConsumerState<WidgetWall> createState() => _WidgetWallState();
}
class _WidgetWallState extends ConsumerState<WidgetWall> {
Future<List<Widget>> buildList() async {
ref.watch(homeControllerProvider);
//var itemCount = 0;
final items = ref.watch(itemsProvider);
// ignore: unused_local_variable
final val = items.value;
return <Widget>[];
}
@override
Widget build(BuildContext context) {
// this.buildList();
final controller = ref.watch(itemsProvider);
switch (controller) {
case AsyncError(:final error):
return Text('Error: $error');
case AsyncData(:final value):
final allItems = value;
final items = <dynamic>[];
for (var i = 0; i < allItems.length; i++) {
// ignore: avoid_dynamic_calls
if (allItems.elementAt(i).isVisible == true) {
logger.i('INSERTING VISIBLE ITEM');
items.add(allItems.elementAt(i));
}
}
final itemWidgets = <Widget>[];
final itemCount = items.length;
for (var i = 0; i < itemCount; i++) {
final item = items.elementAt(i);
if (item is Hydration && item.isVisible) {
final itemwidget = CompoundWidgetWater(item: item);
itemWidgets.insert(i, itemwidget);
} else if (item is TimerItem && item.isVisible) {
if (item.isVisible == true) {
logger.i('VISIBLE');
final itemwidget = CompoundWidgetTimer(item: item);
itemWidgets.insert(i, itemwidget);
} else {
logger.i('IS NOT VISIBLE');
}
} else if (item is TasksItem && item.isVisible) {
final itemwidget = CompoundWidgetTasks(item: item);
itemWidgets.insert(i, itemwidget);
}
}
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(
color: Theme.of(context).colorScheme.onPrimary,
),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Theme.of(context).colorScheme.primary,
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
textScaler: const TextScaler.linear(2),
'Habitrack ',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
// fontStyle: FontStyle.italic,
),
),
const Align(
alignment: Alignment.topRight,
child: AddWidgetButton(),
),
],
),
),
body: ColoredBox(
color: Theme.of(context).colorScheme.primaryContainer,
child: ListView(
padding: const EdgeInsets.all(8),
children: itemWidgets,
),
),
);
default:
return const CircularProgressIndicator();
}
}
}

59
app/lib/l10n/app_de.arb Normal file
View file

@ -0,0 +1,59 @@
{
"settingsHeader": "Einstellungen",
"widgetSettings_durationPickerCurrent": "Derzeitige Laufzeit:",
"widgetSettings_durationPickerButton": "Laufzeit auswählen",
"addWidgetHeader": "Widget Hinzufügen",
"addWidget_water": "Wasser-Buddy hinzufügen",
"addWidget_timer": "Fokus-Timer hinzufügen",
"addWidget_tasks": "Tasks Widget hinzufügen",
"widgetSettings_name": "Name des Widgets",
"widgetSettings_saveButton": "Speichern",
"widgetSettings_deleteButton": "Löschen",
"widgetSettings_cancelButton": "Abbrechen",
"waterWidget_defaultName": "Trink-Buddy",
"waterWidgetSettings_button1": "Button 1 in ml",
"waterWidgetSettings_button2": "Button 2 in ml",
"waterWidgetSettings_goal": "Tagesziel",
"waterWidgetSettings_current": "Derzeit getrunken",
"waterWidget_customAmountButton": "+ Beliebig",
"waterWidget_customAmountMessage": "Geben sie die Geünschte Wassermenge ein:",
"tasksWidget_defaultName": "Tasks Widget",
"tasksWidget_addTaskButtonLabel": "Aufgabe Hinzufügen",
"tasksWidget_editTask": "Aufgabe bearbeiten",
"taskDefaultName": "Neue Aufgabe",
"taskSettings_name": "Aufgabenname",
"taskSettings_due": "Fällig am",
"taskSettings_duePicker": "Fälligkeitsdatum auswählen",
"tasksWidget_overdue": "Überfällig!",
"tasksWidget_deleteDone": "Erledigte Aufgaben löschen",
"tasksWidget_rescheduleOverdue": "Überfällige Aufgaben neu planen",
"timerWidget_defaultName": "Fokus-Timer",
"timerWidget_buttonStart": "Starten",
"timerWidget_buttonPause": "Anhalten",
"timerWidget_buttonContinue": "Weiter",
"timerWidget_buttonReset": "Zurücksetzen",
"timerWidgetSettings_duration": "Tägliches Ziel",
"timerWidget_current": "Jetzt",
"timerWidget_goal": "Ziel",
"timerWidget_pausedForEdit": "Halte den Timer an, um ihn bearbeiten zu können!"
}

64
app/lib/l10n/app_en.arb Normal file
View file

@ -0,0 +1,64 @@
{
"settingsHeader": "Settings",
"widgetSettings_durationPickerCurrent": "Current Duration:",
"widgetSettings_durationPickerButton": "Choose Duration",
"addWidgetHeader": "Widget Picker",
"addWidget_water": "Add Hydration Tracker",
"addWidget_timer": "Add Focus Timer",
"addWidget_tasks": "Add Tasks Widget",
"widgetSettings_name": "Widget Name",
"widgetSettings_saveButton": "Confirm",
"widgetSettings_deleteButton": "Delete",
"widgetSettings_cancelButton": "Cancel",
"waterWidget_defaultName": "Hydration Tracker",
"waterWidgetSettings_button1": "Button 1 in ml",
"waterWidgetSettings_button2": "Button 2 in ml",
"waterWidgetSettings_goal": "Daily Goal",
"waterWidgetSettings_current": "Current Amount",
"waterWidget_customAmountButton": "+ custom",
"waterWidget_customAmountMessage": "Enter an amount as desired:",
"tasksWidget_defaultName": "Tasks Widget",
"tasksWidget_addTaskButtonLabel": "Add Task",
"tasksWidget_editTask": "Edit Task",
"taskDefaultName": "Examplar Task",
"taskSettings_name": "Task Name",
"taskSettings_due": "Due Date",
"taskSettings_duePicker": "Choose a due date",
"tasksWidget_overdue": "Overdue!",
"tasksWidget_deleteDone": "Delete completed tasks",
"tasksWidget_rescheduleOverdue": "Reschedule overdue tasks",
"timerWidget_defaultName": "Focus Timer",
"timerWidget_buttonStart": "Start",
"timerWidget_buttonPause": "Stop",
"timerWidget_buttonContinue": "Continue",
"timerWidget_buttonReset": "Reset",
"timerWidgetSettings_duration": "Daily Goal",
"timerWidget_current": "Current",
"timerWidget_goal": "Goal",
"timerWidget_pausedForEdit": "Timer must be paused before editing!"
}

86
app/lib/main.dart Normal file
View file

@ -0,0 +1,86 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background/flutter_background.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/infrastructure/routing.dart';
import 'package:habitrack_app/sembast/global_providers.dart';
import 'package:logger/logger.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sembast/sembast_io.dart';
import 'package:sembast_web/sembast_web.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (kIsWeb) {
final factory = databaseFactoryWeb;
final db = await factory.openDatabase('test');
runApp(
ProviderScope(
overrides: [databaseProvider.overrideWithValue(db)],
child: const MainApp(),
),
);
// running on the web!
} else {
final appPath = await getApplicationDocumentsDirectory();
appPath.createSync(recursive: true);
final dbPath = join(appPath.path, 'widgets.db');
final database = await databaseFactoryIo.openDatabase(dbPath);
const androidConfig = FlutterBackgroundAndroidConfig(
notificationTitle: 'flutter_background example app',
notificationText:
// ignore: lines_longer_than_80_chars
'Background notification for keeping the example app running in the background',
notificationIcon: AndroidResource(
name: 'background_icon',
// ignore: avoid_redundant_argument_values
defType: 'drawable',
), // Default is ic_launcher from folder mipmap
);
await FlutterBackground.initialize(androidConfig: androidConfig);
await FlutterBackground.enableBackgroundExecution();
runApp(
ProviderScope(
overrides: [
databaseProvider.overrideWithValue(database),
],
child: const MainApp(),
),
);
}
}
Logger logger = Logger();
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Habitrack',
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.purple,
),
textTheme: const TextTheme(
displayLarge: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.amber,
),
),
),
routerConfig: goRouter,
);
}
}

View file

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:habitrack_app/pages/hydration_graph_widget.dart';
class SubpageHydrationButton extends StatelessWidget {
const SubpageHydrationButton({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 300,
margin: const EdgeInsets.only(top: 20, bottom: 7.5),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
minimumSize: const Size(10, 70),
),
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (context) => const DashboardHydrationSubpage(),
),
),
},
child: Row(
children: [
Icon(
Icons.local_drink,
color: Theme.of(context).colorScheme.onPrimary,
),
Text(
' Hydration Widgets',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
],
),
),
);
}
}
class DashboardHydrationSubpage extends StatefulWidget {
const DashboardHydrationSubpage({super.key});
@override
State<StatefulWidget> createState() => _DashboardHydrationSubpageState();
}
class _DashboardHydrationSubpageState extends State<DashboardHydrationSubpage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
appBar: AppBar(
iconTheme: IconThemeData(
color: Theme.of(context).colorScheme.onPrimary,
),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Theme.of(context).colorScheme.primary,
title: Text(
'Statistics: Hydration Widgets',
textScaler: const TextScaler.linear(1.2),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
body: const HydrationGraphWidget(),
);
}
}

View file

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/pages/dashboard_hydration_subpage.dart';
import 'package:habitrack_app/pages/dashboard_task_subpage.dart';
import 'package:habitrack_app/pages/dashboard_timer_subpage.dart';
import 'package:habitrack_app/pages/reset_subpage.dart';
class DashboardPage extends ConsumerStatefulWidget {
const DashboardPage({super.key});
@override
ConsumerState<DashboardPage> createState() => _DashboardPageState();
}
class _DashboardPageState extends ConsumerState<DashboardPage> {
@override
Widget build(BuildContext context) {
//final items = ref.watch(itemsProvider);
//final len = items.value!.length;
return Scaffold(
appBar: AppBar(
title: Text(
'Dashboard',
textScaler: const TextScaler.linear(1.4),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onSecondary,
),
),
backgroundColor: Theme.of(context).colorScheme.secondary,
),
body: ColoredBox(
color: Theme.of(context).colorScheme.primaryContainer,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: const Column(
children: [
SubpageHydrationButton(),
SubpageTaskButton(),
SubpageTimerButton(),
ResetSubpageButton(),
],
),
),
),
);
}
}

View file

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:habitrack_app/pages/tasks_graph_widget.dart';
class SubpageTaskButton extends StatelessWidget {
const SubpageTaskButton({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 300,
margin: const EdgeInsets.only(top: 7.5, bottom: 7.5),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
minimumSize: const Size(10, 70),
),
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (context) => const DashboardTaskSubpage(),
),
),
},
child: Row(
children: [
Icon(
Icons.task,
color: Theme.of(context).colorScheme.onPrimary,
),
Text(
' Task Widgets',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
],
),
),
);
}
}
class DashboardTaskSubpage extends StatefulWidget {
const DashboardTaskSubpage({super.key});
@override
State<StatefulWidget> createState() => _DashboardTaskSubpageState();
}
class _DashboardTaskSubpageState extends State<DashboardTaskSubpage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
appBar: AppBar(
iconTheme: IconThemeData(
color: Theme.of(context).colorScheme.onPrimary,
),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Theme.of(context).colorScheme.primary,
title: Text(
'Statistics: Tasks Widgets',
textScaler: const TextScaler.linear(1.2),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
body: TasksGraphWidget(),
);
}
}

View file

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:habitrack_app/pages/timer_graph_widget.dart';
class SubpageTimerButton extends StatelessWidget {
const SubpageTimerButton({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: 300,
margin: const EdgeInsets.only(top: 7.5, bottom: 7.5),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
minimumSize: const Size(10, 70),
),
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (context) => const DashboardTimerSubpage(),
),
),
},
child: Row(
children: [
Icon(
Icons.timer,
color: Theme.of(context).colorScheme.onPrimary,
),
Text(
' Timer Widgets',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
],
),
),
);
}
}
class DashboardTimerSubpage extends StatefulWidget {
const DashboardTimerSubpage({super.key});
@override
State<StatefulWidget> createState() => _DashboardTimerSubpageState();
}
class _DashboardTimerSubpageState extends State<DashboardTimerSubpage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
appBar: AppBar(
iconTheme: IconThemeData(
color: Theme.of(context).colorScheme.onPrimary,
),
backgroundColor: Theme.of(context).colorScheme.secondary,
foregroundColor: Theme.of(context).colorScheme.primary,
title: Text(
'Statistics: Timer Widgets',
textScaler: const TextScaler.linear(1.2),
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
body: TimerGraphWidget(),
);
}
}

View file

@ -0,0 +1,478 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
import 'package:habitrack_app/main.dart';
import 'package:habitrack_app/sembast/hydration.dart';
import 'package:intl/intl.dart';
class DataPoint {
DataPoint({required this.date, required this.progress});
DateTime date;
double progress;
}
class HydrationGraphWidget extends ConsumerStatefulWidget {
const HydrationGraphWidget({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() =>
_TasksGraphWidgetState();
}
class _TasksGraphWidgetState extends ConsumerState<HydrationGraphWidget> {
void _buildList(List<dynamic> value) {
final items = value;
_thisWeekCompleted = 0;
_thisWeekPlanned = 0;
_maxAmount = 0;
setState(() {
_todayCompleted = 0;
_todayPlanned = 0;
_weeklyPlannedEntries = [];
_weeklyWorkedEntries = [];
for (var i = 0; i <= 6; i++) {
final itemToInsert = DataPoint(
date: _latestDate.subtract(Duration(days: 6 - i)),
progress: 0,
);
_weeklyPlannedEntries.add(itemToInsert);
}
for (var i = 0; i <= 6; i++) {
final itemToInsert = DataPoint(
date: _latestDate.subtract(Duration(days: 6 - i)),
progress: 0,
);
_weeklyWorkedEntries.add(itemToInsert);
}
});
_thisWeekPlanned = 0;
for (final item in items) {
if (_selectedValue == 'weekly') {
var alreadyAdded = false;
for (var i = 0; i <= 6; i++) {
DateTime parsedDate;
if (item is Hydration) {
if (item.current / item.goal >= 1 && item.completedOn != '') {
//item is completed
parsedDate = DateTime.parse(item.completedOn);
} else {
parsedDate = DateTime.parse(item.createdOn);
logger.i('GOAL: $item.goal');
}
logger
.i('BEFORE $i days ago and AFTER ${i + 1} days ago, element at '
'index ${6 - i} will be updated.');
if (parsedDate.isBefore(_latestDate.subtract(Duration(days: i))) &&
parsedDate
.isAfter(_latestDate.subtract(Duration(days: i + 1)))) {
logger.i('LOOPING');
if (!alreadyAdded) {
alreadyAdded = true;
_thisWeekCompleted += item.current / 1000;
_thisWeekPlanned += item.goal / 1000;
}
if (item.goal > _maxAmount) {
_maxAmount = (item.goal.toDouble() / 1000).ceilToDouble();
}
if (item.current > _maxAmount && item.current > item.goal) {
_maxAmount = (item.current.toDouble() / 1000).ceilToDouble();
}
// Update maxAmount
setState(() {
_weeklyPlannedEntries.elementAt(6 - i).progress =
item.goal.toDouble() / 1000;
_weeklyWorkedEntries.elementAt(6 - i).progress =
item.current.toDouble() / 1000;
});
}
}
}
} else if (_selectedValue == 'daily' && item is Hydration) {
logger.i('DATES');
final parsedDate = DateTime.parse(item.createdOn);
logger.i('LATEST DATE: $_latestDate');
final oneDayAgo = _latestDate.subtract(const Duration(days: 1));
if (parsedDate.isBefore(oneDayAgo)) {
logger.i('More than a day old');
} else if (parsedDate.isAfter(oneDayAgo) &&
parsedDate.isBefore(_latestDate)) {
logger.i('TOday');
_todayCompleted = item.current / 1000;
_todayPlanned = item.goal / 1000;
}
}
}
}
void _showPreviousWeek() {
setState(() {
_todayPlanned = 0;
_todayCompleted = 0;
_thisWeekPlanned = 0;
_thisWeekCompleted = 0;
});
if (_selectedValue == 'weekly') {
_latestDate = _latestDate.subtract(const Duration(days: 7));
} else if (_selectedValue == 'daily') {
logger.i('HMMM');
_latestDate = _latestDate.subtract(const Duration(days: 1));
}
final items = ref.watch(itemsProvider);
switch (items) {
case AsyncError(:final error):
logger.i('Error: $error');
case AsyncData(:final value):
final allItems = value;
_buildList(allItems);
default:
logger.i('Hmmm, how can we help?');
} // get current date
// show past 7 days starting 14 days ago
}
void _showNextWeek() {
if (_selectedValue == 'weekly') {
_latestDate = _latestDate.add(const Duration(days: 7));
} else if (_selectedValue == 'daily') {
logger.i('HMMM');
_latestDate = _latestDate.add(const Duration(days: 1));
}
if (!_latestDate.isAfter(DateTime.now())) {
final items = ref.watch(itemsProvider);
switch (items) {
case AsyncError(:final error):
logger.i('Error: $error');
case AsyncData(:final value):
final allItems = value;
_buildList(allItems);
default:
logger.i('Hmmm, how can we help?');
} // get current date
}
// show past 7 days starting 14 days ago
}
String _getText() {
final dateFormat = DateFormat('dd. MMM yyyy');
if (_selectedValue == 'weekly' &&
_latestDate.isAfter(DateTime.now().subtract(const Duration(days: 6)))) {
return ' this past week';
} else if (_selectedValue == 'weekly' &&
!_latestDate
.isAfter(DateTime.now().subtract(const Duration(days: 6)))) {
return ' '
'from'
' ${dateFormat.format(_latestDate.subtract(const Duration(days: 6)))}'
' to ${dateFormat.format(_latestDate)}';
}
final formattedDate = dateFormat.format(_latestDate);
// return ' on ${_latestDate.toString().substring(6, 10)}';
return ' on $formattedDate';
}
List<dynamic> thisWeekItems = [];
List<dynamic> todayItems = [];
String? _selectedValue = 'weekly';
double _thisWeekCompleted = 0;
double _thisWeekPlanned = 0;
double _maxAmount = 0;
DateTime _latestDate = DateTime.now();
double _todayCompleted = 0;
double _todayPlanned = 0;
List<DataPoint> _weeklyPlannedEntries = <DataPoint>[];
List<DataPoint> _weeklyWorkedEntries = <DataPoint>[];
@override
Widget build(BuildContext context) {
final items = ref.watch(itemsProvider);
switch (items) {
case AsyncError(:final error):
logger.i('Error: $error');
case AsyncData(:final value):
final allItems = value;
_buildList(allItems);
default:
logger.i('Hmmm, how can we help?');
}
final firstDate = _weeklyPlannedEntries.elementAtOrNull(0)!.date;
logger.i('HMMM $firstDate');
final flSpots = <FlSpot>[];
for (final dataPoint in _weeklyPlannedEntries) {
final xValue = _daysBetween(_weeklyPlannedEntries[0].date, dataPoint.date)
.toDouble();
final lcbd = FlSpot(
xValue,
dataPoint.progress,
);
flSpots.add(lcbd);
}
final weeklyWorkedSpots = <FlSpot>[];
for (final dataPoint in _weeklyWorkedEntries) {
final xValue =
_daysBetween(_weeklyWorkedEntries[0].date, dataPoint.date).toDouble();
final lcbd = FlSpot(xValue, dataPoint.progress);
weeklyWorkedSpots.add(lcbd);
}
final gradientColors = [
Colors.cyan,
Colors.blueAccent,
];
final gradient2Colors = [
Colors.amber,
Colors.amberAccent,
];
final df = DateFormat('dd. MMMM');
return Column(
children: [
Container(
height: 75,
color: Theme.of(context).colorScheme.primaryContainer,
margin: const EdgeInsets.only(bottom: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _showPreviousWeek,
style: ButtonStyle(
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
child: const Text('Previous'),
),
DropdownButton<String>(
value: _selectedValue,
borderRadius: BorderRadius.circular(10),
onChanged: (value) {
setState(() {
_latestDate = DateTime.now();
_selectedValue = value;
});
},
items: const [
DropdownMenuItem(
value: 'daily',
child: Text('Daily'),
),
DropdownMenuItem(
value: 'weekly',
child: Text('Weekly'),
),
],
),
ElevatedButton(
onPressed: _showNextWeek,
style: ButtonStyle(
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
child: const Text('Next'),
),
],
),
),
if (_selectedValue == 'weekly') ...[
Container(
padding:
const EdgeInsets.only(bottom: 15, top: 15, right: 15, left: 5),
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.90,
height: MediaQuery.of(context).size.height * 0.5,
child: LineChart(
LineChartData(
minY: 0,
maxY: _maxAmount,
lineBarsData: [
LineChartBarData(
isCurved: true,
preventCurveOverShooting: true,
spots: flSpots,
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: gradientColors
.map((color) => color.withOpacity(0.6))
.toList(),
),
),
),
LineChartBarData(
color: Colors.amber,
isCurved: true,
isStrokeCapRound: true,
preventCurveOverShooting: true,
spots: weeklyWorkedSpots,
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: gradient2Colors
.map((color) => color.withOpacity(0.6))
.toList(),
),
),
),
],
titlesData: FlTitlesData(
rightTitles: const AxisTitles(
sideTitles: SideTitles(
reservedSize: 30,
interval: 20,
),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(
interval: 20,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
getTitlesWidget: (value, meta) => Text(
'$value l',
style: const TextStyle(
fontSize: 10,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
),
// interval: 0.25,
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
getTitlesWidget: (value, meta) => Text(
style: const TextStyle(
fontSize: 10,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
df.format(
DateTime(
firstDate.year,
firstDate.month,
firstDate.day,
).add(Duration(days: value.round())),
),
),
showTitles: true,
interval: 1,
),
),
),
),
),
),
),
],
if (_selectedValue == 'daily') ...[
SizedBox(
width: MediaQuery.of(context).size.width * 0.90,
height: MediaQuery.of(context).size.height * 0.5,
child: PieChart(
PieChartData(
sectionsSpace: 0,
sections: [
PieChartSectionData(
value: _todayCompleted, // Progress
color: Theme.of(context).colorScheme.primaryFixedDim,
radius: 60,
title: (_todayPlanned > 0)
// ignore: lines_longer_than_80_chars
? '${((_todayCompleted / _todayPlanned) * 100).floorToDouble()} %'
: '0 %',
titleStyle: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.secondary,
),
),
PieChartSectionData(
titleStyle: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primaryFixedDim,
),
value: (_todayPlanned > 0 && _todayCompleted > 0)
? (_todayPlanned - _todayCompleted)
: 1, // Total - progress
color: Theme.of(context).colorScheme.secondary,
radius: 60,
showTitle: _todayPlanned == 0 ||
(_todayPlanned > 0 && _todayCompleted == 0),
title: '0 %',
),
],
),
),
),
],
SizedBox(
width: MediaQuery.of(context).size.width * 0.90,
child: Column(
children: [
Text(
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontSize: 15,
fontWeight: FontWeight.bold,
),
// ignore: lines_longer_than_80_chars
'${(_selectedValue == 'weekly') ? _thisWeekCompleted : _todayCompleted} liters drunk${_getText()}',
),
Text(
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontSize: 15,
fontWeight: FontWeight.bold,
),
// ignore: lines_longer_than_80_chars
'Out of a total goal of ${(_selectedValue == 'weekly') ? _thisWeekPlanned : _todayPlanned} liters',
),
],
),
),
],
);
}
int _daysBetween(DateTime from, DateTime to) {
final d1 = DateTime(from.year, from.month, from.day);
final d2 = DateTime(to.year, to.month, to.day);
return (d2.difference(d1).inHours / 24).round();
}
}

View file

@ -0,0 +1,123 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_controller.dart';
import 'package:habitrack_app/infrastructure/widget_wall/items_state.dart';
import 'package:habitrack_app/sembast/hydration.dart';
import 'package:habitrack_app/sembast/tasks_list.dart';
import 'package:habitrack_app/sembast/timer.dart';
class ResetSubpageButton extends ConsumerStatefulWidget {
const ResetSubpageButton({super.key});
@override
ConsumerState<ResetSubpageButton> createState() => _ResetSubpageButtonState();
}
class _ResetSubpageButtonState extends ConsumerState<ResetSubpageButton> {
Future<void> _confirmPopup() async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Theme.of(context).colorScheme.onPrimary,
title: Text(
'Are you sure?',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
scrollable: true,
actions: <Widget>[
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Cancel',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () {
final controller = ref.watch(homeControllerProvider);
final items = ref.watch(itemsProvider);
switch (items) {
case AsyncData(:final value):
final items = value;
for (var i = 0; i < items.length; i++) {
final item = items.elementAt(i);
if (item is Hydration) {
controller.delete(item.id);
} else if (item is TasksItem) {
controller.delete(item.id);
} else if (item is TimerItem) {
controller.delete(item.id);
}
}
Navigator.of(context).pop();
}
//DateTime due = DateTime.parse(formattedDate);
// ignore: cascade_invocations
},
child: Text(
'Yes',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Container(
width: 300,
margin: const EdgeInsets.only(top: 7.5, bottom: 7.5),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
minimumSize: const Size(10, 70),
),
onPressed: _confirmPopup,
child: Row(
children: [
Icon(
Icons.delete,
color: Theme.of(context).colorScheme.onPrimary,
),
Text(
'Clear Database',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
],
),
),
);
}
}

Some files were not shown because too many files have changed in this diff Show more