Compare commits

..

10 Commits

Author SHA1 Message Date
c12ba48e15 Used Flutter Migrate tool and fixed flutter_picker in pubspec.yaml 2024-03-15 04:34:08 +01:00
Andreas Fahrecker
d2b4b6ee71 Created Project readme
Signed-off-by: Andreas Fahrecker <AndreasFahrecker@gmail.com>
2021-03-12 00:29:58 +01:00
Andreas Fahrecker
40bdcc44f9
Feature/code cleanup (#9)
* Implemented hasSettingsLoaded reducer
* Added Padding to Progress List View
* Created Settings and Time Progress List Store Connector
* Rewritten Home Active Tab
* Fixed missing onTap in Progress List Tile
* Started using new Store Connectors in Inactive and Settings Tab
* Created Time Progress Store Connector
* Rewritten ProgressDetailScreen with new Store Connectors
* Rewritten DatePickerBtn with TextButton
* Deleted unused widget
* Changed Foreground Color behaviour in ColorPicker BTN
* Created Select Duration Button
* Rewritten Duration Setting Widget
* Updated Version Number

Signed-off-by: Andreas Fahrecker <AndreasFahrecker@gmail.com>
2021-03-08 20:48:45 +01:00
Andreas Fahrecker
fc35476503
Feature/widget testing (#8)
* Configured iOS Build
* Created ProgressListTile widget
* Created MaterialTesterWidget
* Created String Methods for Testing in ProgressListTile
* Created ProgressListTileStrings class
* Using Progress List Tile
* Created Progress List View
* Created Progress List Tile currently, future and past test.
* Created Progress List View one and five Time Progresses test.

Signed-off-by: Andreas Fahrecker <AndreasFahrecker@gmail.com>
2021-03-08 16:19:31 +01:00
Andreas Fahrecker
90f2998088
Feature/default duration setting (#7)
* Implemented Basic Duration Settings into AppSettings Model

* Implemented Basic Duration Settings into AppSettings Model

* Created Duration Settings Widget and Started using ViewModel in HomeSettingsTab

* Updated Version Number
2021-03-03 19:59:33 +01:00
Andreas Fahrecker
b520d56d1a
Feature/change progress colors (#6)
* Added Settings Actions
* Created App Settings and Repo + Entity
* Code cleanup Time Progress
* Created App Settings Middleware
* Has Progresses ad has Settings loaded
* Created Load and Update Settings reducers
* Added Settings store middleware to renamed store middleware
* Load Default Settings if not Saved. Use Redux AppState to showprogress colors.
Colors are not yet changeable.
* Added ColorPicker for Done and Left Color
Fixed Loading App Settings Bug
* Fixed Version Number
* Fixed Android App Logo
* Extracted Color Settings into Widget
* Fixed Home Settings Tab Layout and Color Settings Button now show Text in complementary color
2021-03-03 16:35:08 +01:00
Andreas Fahrecker
c580e45361 Fixed App Logo
Signed-of-by : Andreas Fahrecker <AndreasFahrecker@gmail.com>
2021-02-24 02:06:34 +01:00
Andreas Fahrecker
238fd6bc6a Merge remote-tracking branch 'origin/main' into main 2021-02-23 23:15:49 +01:00
Andreas Fahrecker
478ea644a9 Added new App Icon
Signed-of-by : Andreas Fahrecker <AndreasFahrecker@gmail.com>
2021-02-23 23:15:31 +01:00
Andreas Fahrecker
82fc8c5fbb Added new App Icon
Signed-of-by : Andreas Fahrecker <AndreasFahrecker@gmail.com>
2021-02-23 23:14:51 +01:00
104 changed files with 1869 additions and 701 deletions

104
.gitignore vendored
View File

@ -1,6 +1,5 @@
# Miscellaneous # Miscellaneous
*.class *.class
*.lock
*.log *.log
*.pyc *.pyc
*.swp *.swp
@ -9,6 +8,7 @@
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
migrate_working_dir/
# IntelliJ related # IntelliJ related
*.iml *.iml
@ -16,102 +16,28 @@
*.iws *.iws
.idea/ .idea/
# Visual Studio Code related # The .vscode folder contains launch configuration and tasks you configure in
.classpath # VS Code which you may wish to be included in version control, so this line
.project # is commented out by default.
.settings/ #.vscode/
.vscode/
# Flutter repo-specific
/bin/cache/
/bin/internal/bootstrap.bat
/bin/internal/bootstrap.sh
/bin/mingit/
/dev/benchmarks/mega_gallery/
/dev/bots/.recipe_deps
/dev/bots/android_tools/
/dev/devicelab/ABresults*.json
/dev/docs/doc/
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
analysis_benchmark.json
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related # Flutter/Dart/Pub related
**/doc/api/ **/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/ .dart_tool/
.flutter-plugins .flutter-plugins
.flutter-plugins-dependencies .flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-cache/ .pub-cache/
.pub/ .pub/
build/ /build/
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds
# Android related # Symbolication related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# macOS
**/macos/Flutter/GeneratedPluginRegistrant.swift
# Coverage
coverage/
# Symbols
app.*.symbols app.*.symbols
# Exceptions to above rules. # Obfuscation related
!**/ios/**/default.mode1v3 app.*.map.json
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser # Android Studio will place build artifacts here
!**/ios/**/default.perspectivev3 /android/app/debug
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages /android/app/profile
!/dev/ci/**/Gemfile.lock /android/app/release

View File

@ -4,7 +4,42 @@
# This file should be version controlled and should not be manually edited. # This file should be version controlled and should not be manually edited.
version: version:
revision: fba99f6cf9a14512e461e3122c8ddfaa25394e89 revision: "ba393198430278b6595976de84fe170f553cc728"
channel: stable channel: "stable"
project_type: app project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: android
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: ios
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: linux
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: macos
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: web
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
- platform: windows
create_revision: ba393198430278b6595976de84fe170f553cc728
base_revision: ba393198430278b6595976de84fe170f553cc728
# 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'

View File

@ -1,8 +1,32 @@
# time_progress_tracker # Time Progress Tracker
A Flutter Application to create Timers with a percentage indicator. A Flutter Application to create Timers with a percentage indicator.
The Idea for this Application came to me while, I was doing my civil service.
It is a really simple app at this state. You can enter Time Progresses, which are made up of
a name, a start date, and a end date.
Then you can see a list of all currently active and a list of all currently inactive progresses,
including their current percentages.
## Getting Started ## Current State of the repo.
Currently, the code in this branch is pretty messed up. Since I wrote that application from a
prototype which, was developed as my first flutter project and in a very short time.
Since, then most of the work that went into this project was cleanup work.
Currently I am working on a clean codebase in the feature/platform-widget branch.
At this state, the base screens of the app are located in lib/screens,
all other ui widgets are located in li/widgets,
the model classes are located in lib/model,
files related to persisting the data are in lib/persistence,
and redux related files are spread over lib/actions lib/middleware lib/reducers and lib/reducers.
- [Google Play](https://play.google.com/store/apps/details?id=com.fahrecker.time_progress_calculator)
## Original Readme
### Getting Started
This project is a starting point for a Flutter application. This project is a starting point for a Flutter application.

28
analysis_options.yaml Normal file
View File

@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

2
android/.gitignore vendored
View File

@ -9,3 +9,5 @@ GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore. # Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties key.properties
**/*.keystore
**/*.jks

View File

@ -1,3 +1,9 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties() def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties') def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) { if (localPropertiesFile.exists()) {
@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
} }
} }
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) { if (flutterVersionCode == null) {
flutterVersionCode = '1' flutterVersionCode = '1'
@ -21,32 +22,31 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0' flutterVersionName = '1.0'
} }
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android { android {
compileSdkVersion 28 namespace "com.fahrecker.time_progress_calculator"
compileSdk flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
lintOptions {
disable 'InvalidPackage'
}
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.fahrecker.time_progress_calculator" applicationId "com.fahrecker.time_progress_calculator"
minSdkVersion 16 // You can update the following values to match your application needs.
targetSdkVersion 29 // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
} }
@ -71,6 +71,4 @@ flutter {
source '../..' source '../..'
} }
dependencies { dependencies {}
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fahrecker.time_progress_calculator"> package="com.fahrecker.time_progress_calculator">
<!-- Flutter needs it to communicate with the running application <!-- 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. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1,16 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fahrecker.time_progress_calculator"> package="com.fahrecker.time_progress_calculator">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application <application
android:name="io.flutter.app.FlutterApplication" android:label="time_progress_tracker"
android:label="Time Progress Tracker" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
@ -24,15 +20,6 @@
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
@ -44,4 +31,15 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility?hl=en 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> </manifest>

View File

@ -0,0 +1,6 @@
package com.fahrecker.time_progress_tracker
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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 19 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

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

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fahrecker.time_progress_calculator"> package="com.fahrecker.time_progress_calculator">
<!-- Flutter needs it to communicate with the running application <!-- 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. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1,20 +1,7 @@
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects { allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }
@ -26,6 +13,6 @@ subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(':app')
} }
task clean(type: Delete) { tasks.register("clean", Delete) {
delete rootProject.buildDir delete rootProject.buildDir
} }

View File

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx4G
android.enableR8=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

View File

@ -1,6 +1,5 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

View File

@ -1,11 +1,26 @@
include ':app' 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
}
settings.ext.flutterSdkPath = flutterSdkPath()
def localPropertiesFile = new File(rootProject.projectDir, "local.properties") includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
def properties = new Properties()
assert localPropertiesFile.exists() repositories {
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } google()
mavenCentral()
gradlePluginPortal()
}
}
def flutterSdkPath = properties.getProperty("flutter.sdk") plugins {
assert flutterSdkPath != null, "flutter.sdk not set in local.properties" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}
include ":app"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

BIN
assets/logo/android_1024.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
assets/logo/ios_1024.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@ -1,4 +1,5 @@
flutter_icons: flutter_icons:
android: true android: true
ios: true ios: true
image_path: "assets/icons/launcher_icon.png" image_path_android: "assets/logo/android_1024.png"
image_path_ios: "assets/logo/ios_1024.png"

2
ios/.gitignore vendored
View File

@ -1,3 +1,4 @@
**/dgph
*.mode1v3 *.mode1v3
*.mode2v3 *.mode2v3
*.moved-aside *.moved-aside
@ -18,6 +19,7 @@ Flutter/App.framework
Flutter/Flutter.framework Flutter/Flutter.framework
Flutter/Flutter.podspec Flutter/Flutter.podspec
Flutter/Generated.xcconfig Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx Flutter/app.flx
Flutter/app.zip Flutter/app.zip
Flutter/flutter_assets/ Flutter/flutter_assets/

View File

@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>App</string> <string>App</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>8.0</string> <string>12.0</string>
</dict> </dict>
</plist> </plist>

View File

@ -1,2 +1,2 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"

View File

@ -1,2 +1,3 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
#include "Generated.xcconfig" #include "Generated.xcconfig"

41
ios/Podfile Normal file
View File

@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View File

@ -3,15 +3,14 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 46; objectVersion = 51;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
4D431DF94830EB4E4109ECB2 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A0182DC80C66D064A11FDF0B /* libPods-Runner.a */; }; 6186AFFAE7FCA76C81CF360E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D5EAE1ADE1FFBE7D23EE84E /* Pods_Runner.framework */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -33,22 +32,21 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 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>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
37CBB71D7FE5A5197D6A6BF7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; 1DBA7F16BF734A3CE98E5546 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 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>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; 7D5EAE1ADE1FFBE7D23EE84E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.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>"; }; 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; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A0182DC80C66D064A11FDF0B /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B4DFA246891E5346CCC8628F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
F9B8D838B24E4D784CD9D717 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; DC45E65269FD602449E1FEBD /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
FF252FCCD702699EBF6FC287 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -56,19 +54,21 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
4D431DF94830EB4E4109ECB2 /* libPods-Runner.a in Frameworks */, 6186AFFAE7FCA76C81CF360E /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
79DE0847F7F4B7CF08AEE47D /* Frameworks */ = { 7425B1E33E3BCBE464E2CBB5 /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A0182DC80C66D064A11FDF0B /* libPods-Runner.a */, B4DFA246891E5346CCC8628F /* Pods-Runner.debug.xcconfig */,
1DBA7F16BF734A3CE98E5546 /* Pods-Runner.release.xcconfig */,
DC45E65269FD602449E1FEBD /* Pods-Runner.profile.xcconfig */,
); );
name = Frameworks; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
@ -88,8 +88,8 @@
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
9DBFAAD22EAE0F654D097AD5 /* Pods */, 7425B1E33E3BCBE464E2CBB5 /* Pods */,
79DE0847F7F4B7CF08AEE47D /* Frameworks */, E3A24E042363B3BCA7910470 /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -104,36 +104,24 @@
97C146F01CF9000F007C117D /* Runner */ = { 97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */, 97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
); );
path = Runner; path = Runner;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
97C146F11CF9000F007C117D /* Supporting Files */ = { E3A24E042363B3BCA7910470 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
97C146F21CF9000F007C117D /* main.m */, 7D5EAE1ADE1FFBE7D23EE84E /* Pods_Runner.framework */,
); );
name = "Supporting Files"; name = Frameworks;
sourceTree = "<group>";
};
9DBFAAD22EAE0F654D097AD5 /* Pods */ = {
isa = PBXGroup;
children = (
37CBB71D7FE5A5197D6A6BF7 /* Pods-Runner.debug.xcconfig */,
FF252FCCD702699EBF6FC287 /* Pods-Runner.release.xcconfig */,
F9B8D838B24E4D784CD9D717 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
@ -143,13 +131,14 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
B3E7CCA0AF60658FE8F4BF7D /* [CP] Check Pods Manifest.lock */, A60954191C254DCAC69F1735 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
555CAADD55712EED12802136 /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -171,6 +160,7 @@
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
}; };
}; };
}; };
@ -221,6 +211,23 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
555CAADD55712EED12802136 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -235,7 +242,7 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
}; };
B3E7CCA0AF60658FE8F4BF7D /* [CP] Check Pods Manifest.lock */ = { A60954191C254DCAC69F1735 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@ -264,8 +271,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
97C146F31CF9000F007C117D /* main.m in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -333,7 +339,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -347,20 +353,19 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = RD9K843SK5;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.fahrecker.timeProgressCalculator; PRODUCT_BUNDLE_IDENTIFIER = com.fahrecker.timeProgressTracker;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Profile; name = Profile;
@ -412,7 +417,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -461,10 +466,12 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
@ -475,20 +482,20 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = RD9K843SK5;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.fahrecker.timeProgressCalculator; PRODUCT_BUNDLE_IDENTIFIER = com.fahrecker.timeProgressTracker;
PRODUCT_NAME = "$(TARGET_NAME)"; 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"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
@ -498,20 +505,19 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = RD9K843SK5;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Flutter", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.fahrecker.timeProgressCalculator; PRODUCT_BUNDLE_IDENTIFIER = com.fahrecker.timeProgressTracker;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1020" LastUpgradeVersion = "1510"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
@ -38,8 +36,19 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <Testables>
</AdditionalOptions> <TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
@ -61,8 +70,6 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Profile" buildConfiguration = "Profile"

View File

@ -1,6 +0,0 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end

View File

@ -1,13 +0,0 @@
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

View File

@ -0,0 +1,13 @@
import UIKit
import Flutter
@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)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -4,6 +4,8 @@
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Time Progress Tracker</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@ -11,7 +13,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>time_progress_calculator</string> <string>time_progress_tracker</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
@ -39,7 +41,9 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<false/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

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

View File

@ -1,9 +0,0 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

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.
}
}

View File

@ -1,7 +1,24 @@
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
class LoadSettingsAction {}
class AppSettingsLoadedActions {
final AppSettings appSettings;
AppSettingsLoadedActions(this.appSettings);
}
class UpdateAppSettingsActions {
final AppSettings appSettings;
UpdateAppSettingsActions(this.appSettings);
}
class AppSettingsNotLoadedAction {}
class LoadTimeProgressListAction {} class LoadTimeProgressListAction {}
class TimeProgressListLoadedAction { class TimeProgressListLoadedAction {
@ -32,7 +49,10 @@ class DeleteTimeProgressAction {
} }
void loadTimeProgressListIfUnloaded(Store<AppState> store) { void loadTimeProgressListIfUnloaded(Store<AppState> store) {
if (!store.state.hasLoaded) { if (!store.state.hasProgressesLoaded)
store.dispatch(LoadTimeProgressListAction()); store.dispatch(LoadTimeProgressListAction());
} }
void loadSettingsIfUnloaded(Store<AppState> store) {
if (!store.state.hasSettingsLoaded) store.dispatch(LoadSettingsAction());
} }

17
lib/helper_functions.dart Normal file
View File

@ -0,0 +1,17 @@
import 'dart:ui';
import 'package:time_progress_tracker/models/time_progress.dart';
TimeProgress selectProgressById(List<TimeProgress> tpList, String id) =>
tpList.firstWhere((tp) => tp.id == id, orElse: null);
List<TimeProgress> selectActiveProgresses(List<TimeProgress> tpList) =>
tpList.where((tp) => tp.hasStarted() && !tp.hasEnded()).toList();
List<TimeProgress> selectInactiveProgresses(List<TimeProgress> tpList) =>
tpList.where((tp) => !tp.hasStarted() || tp.hasEnded()).toList();
bool useBrightBackground(Color bC) {
double yiq = ((bC.red * 299) + (bC.green * 587) + (bC.blue * 114)) / 1000;
return yiq >= 186 || (bC.red == 0 && bC.green == 0 && bC.blue == 0);
}

View File

@ -1,23 +1,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:package_info/package_info.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:time_progress_tracker/app.dart'; import 'package:time_progress_tracker/app.dart';
import 'package:time_progress_tracker/middleware/store_time_progress_middleware.dart'; import 'package:time_progress_tracker/middleware/store_middleware.dart';
import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/persistence/app_settings.dart';
import 'package:time_progress_tracker/persistence/time_progress_repository.dart'; import 'package:time_progress_tracker/persistence/time_progress_repository.dart';
import 'package:time_progress_tracker/reducers/app_state_reducer.dart'; import 'package:time_progress_tracker/reducers/app_state_reducer.dart';
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
runApp(TimeProgressTrackerApp( runApp(TimeProgressTrackerApp(
store: Store<AppState>( store: Store<AppState>(
appStateReducer, appStateReducer,
initialState: AppState.initial(), initialState: AppState.initial(),
middleware: createStoreTimeProgressListMiddleware( middleware: createStoreMiddleware(
TimeProgressRepository(await SharedPreferences.getInstance()), TimeProgressRepository(prefs), AppSettingsRepository(prefs)),
),
), ),
)); ));
} }

View File

@ -1,21 +1,28 @@
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart'; import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/persistence/app_settings.dart';
import 'package:time_progress_tracker/persistence/time_progress_entity.dart'; import 'package:time_progress_tracker/persistence/time_progress_entity.dart';
import 'package:time_progress_tracker/persistence/time_progress_repository.dart'; import 'package:time_progress_tracker/persistence/time_progress_repository.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
List<Middleware<AppState>> createStoreTimeProgressListMiddleware( List<Middleware<AppState>> createStoreMiddleware(
TimeProgressRepository repository) { TimeProgressRepository progressRepo, AppSettingsRepository settingsRepo) {
final saveTimeProgressList = _createSaveTimeProgressList(repository); final saveTimeProgressList = _createSaveTimeProgressList(progressRepo);
final loadTimeProgressList = _createLoadTimeProgressList(repository); final loadTimeProgressList = _createLoadTimeProgressList(progressRepo);
final saveSettings = _createSaveAppSettings(settingsRepo);
final loadSettings = _createLoadAppSettings(settingsRepo);
return [ return [
TypedMiddleware<AppState, LoadTimeProgressListAction>(loadTimeProgressList), TypedMiddleware<AppState, LoadTimeProgressListAction>(loadTimeProgressList),
TypedMiddleware<AppState, AddTimeProgressAction>(saveTimeProgressList), TypedMiddleware<AppState, AddTimeProgressAction>(saveTimeProgressList),
TypedMiddleware<AppState, UpdateTimeProgressAction>(saveTimeProgressList), TypedMiddleware<AppState, UpdateTimeProgressAction>(saveTimeProgressList),
TypedMiddleware<AppState, DeleteTimeProgressAction>(saveTimeProgressList), TypedMiddleware<AppState, DeleteTimeProgressAction>(saveTimeProgressList),
TypedMiddleware<AppState, LoadSettingsAction>(loadSettings),
TypedMiddleware<AppState, UpdateAppSettingsActions>(saveSettings)
]; ];
} }
@ -47,3 +54,17 @@ Middleware<AppState> _createLoadTimeProgressList(
}).catchError((_) => store.dispatch(TimeProgressListNotLoadedAction())); }).catchError((_) => store.dispatch(TimeProgressListNotLoadedAction()));
}; };
} }
Middleware<AppState> _createSaveAppSettings(AppSettingsRepository repo) =>
(Store<AppState> store, dynamic action, NextDispatcher next) {
next(action);
repo.saveAppSettings(store.state.appSettings.toEntity());
};
Middleware<AppState> _createLoadAppSettings(AppSettingsRepository repo) =>
(Store<AppState> store, dynamic action, NextDispatcher next) {
repo.loadAppSettings().then((appSettings) {
store.dispatch(
AppSettingsLoadedActions(AppSettings.fromEntity(appSettings)));
});
};

View File

@ -14,5 +14,18 @@ class TimeProgressStartTimeIsNotBeforeEndTimeException implements Exception {
this.startTime, this.endTime); this.startTime, this.endTime);
String errMsg() => String errMsg() =>
"The Start Time has to be before the end time. Therefore these values are invalid: Start Time: $startTime EndTime: $endTime"; "The Start Time has to be before the end time. Therefore these values are"
" invalid: Start Time: $startTime EndTime: $endTime";
}
class TimeProgressHasStartedException implements Exception {
String errMsg() =>
"This TimeProgress has started. Therefore all calculation, which assume, "
"that the progress hasn't started yet can't be performed";
}
class TimeProgressHasNotEndedException implements Exception {
String errMsg() =>
"This TimeProgress hasn't ended. Therefore all calculation, which assume,"
" that the progress has ended already can't be performed";
} }

View File

@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:time_progress_tracker/persistence/app_settings.dart';
@immutable
class AppSettings {
final Color doneColor;
final Color leftColor;
final Duration duration;
AppSettings({
this.doneColor,
this.leftColor,
this.duration,
});
factory AppSettings.defaults() => AppSettings(
doneColor: Colors.green,
leftColor: Colors.red,
duration: Duration(days: 365),
);
AppSettings copyWith({
Color doneColor,
Color leftColor,
Duration duration,
}) =>
AppSettings(
doneColor: doneColor ?? this.doneColor,
leftColor: leftColor ?? this.leftColor,
duration: duration ?? this.duration,
);
@override
int get hashCode =>
doneColor.hashCode ^ leftColor.hashCode ^ duration.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppSettings &&
runtimeType == other.runtimeType &&
doneColor == other.doneColor &&
leftColor == other.leftColor &&
duration == other.duration;
AppSettingsEntity toEntity() =>
AppSettingsEntity(doneColor.value, leftColor.value, duration.inDays);
static AppSettings fromEntity(AppSettingsEntity entity) => AppSettings(
doneColor: Color(entity.doneColorValue),
leftColor: Color(entity.leftColorValue),
duration: Duration(days: entity.durationDays),
);
}

View File

@ -1,36 +1,40 @@
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
@immutable @immutable
class AppState { class AppState {
final bool hasLoaded; final bool hasProgressesLoaded, hasSettingsLoaded;
final List<TimeProgress> timeProgressList; final List<TimeProgress> timeProgressList;
final AppSettings appSettings;
AppState({ AppState(
this.hasLoaded = false, {this.hasProgressesLoaded = false,
this.timeProgressList = const [], this.hasSettingsLoaded = false,
}); this.timeProgressList = const [],
this.appSettings});
factory AppState.initial() => AppState(hasLoaded: false); factory AppState.initial() =>
AppState(hasProgressesLoaded: false, appSettings: AppSettings.defaults());
AppState copyWith({ AppState copyWith({
bool hasLoaded, bool hasLoaded,
List<TimeProgress> timeProgressList, List<TimeProgress> timeProgressList,
}) { }) {
return AppState( return AppState(
hasLoaded: hasLoaded ?? this.hasLoaded, hasProgressesLoaded: hasLoaded ?? this.hasProgressesLoaded,
timeProgressList: timeProgressList ?? this.timeProgressList, timeProgressList: timeProgressList ?? this.timeProgressList,
); );
} }
@override @override
int get hashCode => hasLoaded.hashCode ^ timeProgressList.hashCode; int get hashCode => hasProgressesLoaded.hashCode ^ timeProgressList.hashCode;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is AppState && other is AppState &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&
hasLoaded == other.hasLoaded && hasProgressesLoaded == other.hasProgressesLoaded &&
timeProgressList == other.timeProgressList; timeProgressList == other.timeProgressList;
} }

View File

@ -19,27 +19,23 @@ class TimeProgress {
"Initial Name", DateTime(thisYear - 1), DateTime(thisYear + 1)); "Initial Name", DateTime(thisYear - 1), DateTime(thisYear + 1));
} }
factory TimeProgress.defaultFromDuration(Duration duration) =>
TimeProgress("", DateTime.now(), DateTime.now().add(duration));
TimeProgress copyWith( TimeProgress copyWith(
{String id, String name, DateTime startTime, DateTime endTime}) { {String id, String name, DateTime startTime, DateTime endTime}) =>
return TimeProgress( TimeProgress(
name ?? this.name, name ?? this.name,
startTime ?? this.startTime, startTime ?? this.startTime,
endTime ?? this.endTime, endTime ?? this.endTime,
id: id ?? this.id, id: id ?? this.id,
); );
}
int daysBehind() { int daysBehind() => DateTime.now().difference(startTime).inDays;
return DateTime.now().difference(startTime).inDays;
}
int daysLeft() { int daysLeft() => endTime.difference(DateTime.now()).inDays;
return endTime.difference(DateTime.now()).inDays;
}
int allDays() { int allDays() => endTime.difference(startTime).inDays;
return endTime.difference(startTime).inDays;
}
double percentDone() { double percentDone() {
double percent = this.daysBehind() / (this.allDays() / 100) / 100; double percent = this.daysBehind() / (this.allDays() / 100) / 100;
@ -48,14 +44,20 @@ class TimeProgress {
return percent; return percent;
} }
bool hasStarted() { bool hasStarted() =>
return DateTime.now().millisecondsSinceEpoch > DateTime.now().millisecondsSinceEpoch > startTime.millisecondsSinceEpoch;
startTime.millisecondsSinceEpoch;
int daysTillStart() {
if (hasStarted()) throw new TimeProgressHasStartedException();
return startTime.difference(DateTime.now()).inDays;
} }
bool hasEnded() { bool hasEnded() =>
return DateTime.now().millisecondsSinceEpoch > DateTime.now().millisecondsSinceEpoch > endTime.millisecondsSinceEpoch;
endTime.millisecondsSinceEpoch;
int daysSinceEnd() {
if (!hasEnded()) throw new TimeProgressHasNotEndedException();
return DateTime.now().difference(endTime).inDays;
} }
@override @override
@ -73,9 +75,8 @@ class TimeProgress {
endTime == other.endTime; endTime == other.endTime;
@override @override
String toString() { String toString() =>
return "TimeProgress{id: $id, name: $name, startTime: $startTime, endTime: $endTime}"; "TimeProgress{id: $id, name: $name, startTime: $startTime, endTime: $endTime}";
}
TimeProgressEntity toEntity() { TimeProgressEntity toEntity() {
if (!TimeProgress.isNameValid(name)) if (!TimeProgress.isNameValid(name))
@ -86,25 +87,17 @@ class TimeProgress {
return TimeProgressEntity(id, name, startTime, endTime); return TimeProgressEntity(id, name, startTime, endTime);
} }
static TimeProgress fromEntity(TimeProgressEntity entity) { static TimeProgress fromEntity(TimeProgressEntity entity) =>
return TimeProgress( TimeProgress(entity.name, entity.startTime, entity.endTime,
entity.name, id: entity.id ?? Uuid().generateV4());
entity.startTime,
entity.endTime,
id: entity.id ?? Uuid().generateV4(),
);
}
static bool isValid(TimeProgress tp) { static bool isValid(TimeProgress tp) =>
return TimeProgress.isNameValid(tp.name) && TimeProgress.isNameValid(tp.name) &&
TimeProgress.areTimesValid(tp.startTime, tp.endTime); TimeProgress.areTimesValid(tp.startTime, tp.endTime);
}
static bool isNameValid(String name) { static bool isNameValid(String name) =>
return name != null && name != "" && name.length > 2 && name.length < 21; name != null && name != "" && name.length > 2 && name.length < 21;
}
static bool areTimesValid(DateTime startTime, DateTime endTime) { static bool areTimesValid(DateTime startTime, DateTime endTime) =>
return startTime.isBefore(endTime); startTime.isBefore(endTime);
}
} }

View File

@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
class AppSettingsRepository {
static const String _key = "app_settings";
final SharedPreferences prefs;
final JsonCodec codec;
AppSettingsRepository(this.prefs, {this.codec = json});
Future<AppSettingsEntity> loadAppSettings() {
final String jsonString = this.prefs.getString(_key);
if (jsonString == null)
return Future<AppSettingsEntity>.value(AppSettingsEntity.defaults());
return Future<AppSettingsEntity>.value(
AppSettingsEntity.fromJson(codec.decode(jsonString)));
}
Future<bool> saveAppSettings(AppSettingsEntity appSettings) =>
this.prefs.setString(_key, codec.encode(appSettings));
}
class AppSettingsEntity {
static const String _doneKey = "doneColorValue",
_leftKey = "leftColorValue",
_durationDaysKey = "durationDays";
final int doneColorValue, leftColorValue, durationDays;
AppSettingsEntity(
this.doneColorValue, this.leftColorValue, this.durationDays);
factory AppSettingsEntity.defaults() => AppSettings.defaults().toEntity();
@override
int get hashCode => doneColorValue.hashCode ^ leftColorValue.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppSettingsEntity &&
runtimeType == other.runtimeType &&
doneColorValue == other.doneColorValue &&
leftColorValue == other.leftColorValue;
Map<String, Object> toJson() => {
_doneKey: doneColorValue,
_leftKey: leftColorValue,
_durationDaysKey: durationDays,
};
static AppSettingsEntity fromJson(Map<String, Object> json) =>
AppSettingsEntity(
json[_doneKey],
json[_leftKey],
json[_durationDaysKey],
);
}

View File

@ -2,8 +2,6 @@ import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:time_progress_tracker/persistence/time_progress_entity.dart'; import 'package:time_progress_tracker/persistence/time_progress_entity.dart';
import 'dart:developer' as developer;
class TimeProgressRepository { class TimeProgressRepository {
static const String _key = "time_progress_repo"; static const String _key = "time_progress_repo";
final SharedPreferences prefs; final SharedPreferences prefs;

View File

@ -1,10 +1,35 @@
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/reducers/has_loaded_reducer.dart'; import 'package:time_progress_tracker/reducers/has_loaded_reducer.dart';
import 'package:time_progress_tracker/reducers/time_progress_list_reducer.dart'; import 'package:time_progress_tracker/reducers/time_progress_list_reducer.dart';
AppState appStateReducer(AppState state, dynamic action) { AppState appStateReducer(AppState state, dynamic action) {
return AppState( return AppState(
hasLoaded: hasLoadedReducer(state.hasLoaded, action), hasSettingsLoaded:
hasSettingsLoadedReducer(state.hasSettingsLoaded, action),
hasProgressesLoaded:
hasProgressesLoadedReducer(state.hasProgressesLoaded, action),
timeProgressList: timeProgressListReducer(state.timeProgressList, action), timeProgressList: timeProgressListReducer(state.timeProgressList, action),
appSettings: appSettingsReducers(state.appSettings, action),
); );
} }
final appSettingsReducers = combineReducers<AppSettings>([
TypedReducer<AppSettings, AppSettingsLoadedActions>(_loadAppSettings),
TypedReducer<AppSettings, UpdateAppSettingsActions>(_updateAppSettings),
TypedReducer<AppSettings, AppSettingsNotLoadedAction>(_setDefaultSettings)
]);
AppSettings _loadAppSettings(
AppSettings appSettings, AppSettingsLoadedActions nS) =>
nS.appSettings;
AppSettings _setDefaultSettings(
AppSettings appSettings, AppSettingsNotLoadedAction action) =>
AppSettings.defaults();
AppSettings _updateAppSettings(
AppSettings appSettings, UpdateAppSettingsActions nS) =>
nS.appSettings;

View File

@ -1,15 +1,28 @@
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart'; import 'package:time_progress_tracker/actions/actions.dart';
final hasLoadedReducer = combineReducers<bool>([ final hasProgressesLoadedReducer = combineReducers<bool>([
TypedReducer<bool, TimeProgressListLoadedAction>(_setLoaded), TypedReducer<bool, TimeProgressListLoadedAction>(_setProgressesLoaded),
TypedReducer<bool, TimeProgressListNotLoadedAction>(_setUnloaded) TypedReducer<bool, TimeProgressListNotLoadedAction>(_setProgressesUnloaded)
]); ]);
bool _setLoaded(bool hasLoaded, TimeProgressListLoadedAction action) { bool _setProgressesLoaded(bool hasLoaded, TimeProgressListLoadedAction action) {
return true; return true;
} }
bool _setUnloaded(bool hasLoaded, TimeProgressListNotLoadedAction action) { bool _setProgressesUnloaded(bool hasLoaded, TimeProgressListNotLoadedAction action) {
return false; return false;
} }
final hasSettingsLoadedReducer = combineReducers<bool>([
TypedReducer<bool, AppSettingsLoadedActions>(_setSettingsLoaded),
TypedReducer<bool, AppSettingsNotLoadedAction>(_setSettingsUnloaded)
]);
bool _setSettingsLoaded(bool hasLoaded, AppSettingsLoadedActions action) {
return true;
}
bool _setSettingsUnloaded(bool hasLoaded, AppSettingsNotLoadedAction action) {
return false;
}

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:time_progress_tracker/screens/progress_creation_screen.dart'; import 'package:time_progress_tracker/widgets/buttons/create_progress_button.dart';
import 'package:time_progress_tracker/widgets/home/home_bottom_navbar.dart'; import 'package:time_progress_tracker/widgets/home/home_bottom_navbar.dart';
import 'package:time_progress_tracker/widgets/home/tabs/home_active_progresses_tab.dart'; import 'package:time_progress_tracker/widgets/home/tabs/home_active_progresses_tab.dart';
import 'package:time_progress_tracker/widgets/home/tabs/home_inactive_progresses_tab.dart'; import 'package:time_progress_tracker/widgets/home/tabs/home_inactive_progresses_tab.dart';
@ -37,15 +37,7 @@ class _HomeScreenState extends State<HomeScreen> {
), ),
body: _children[_currentIndex], body: _children[_currentIndex],
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: _currentIndex != 2 floatingActionButton: _currentIndex != 2 ? CreateProgressButton() : null,
? FloatingActionButton(
heroTag: "createProgressBTN",
child: Icon(Icons.add),
onPressed: () {
Navigator.pushNamed(context, ProgressCreationScreen.routeName);
},
)
: null,
bottomNavigationBar: HomeBottomNavBar( bottomNavigationBar: HomeBottomNavBar(
currentIndex: _currentIndex, currentIndex: _currentIndex,
onTap: onBottomTabTapped, onTap: onBottomTabTapped,

View File

@ -1,9 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart'; import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
import 'package:time_progress_tracker/widgets/progress_editor_widget.dart'; import 'package:time_progress_tracker/widgets/progress_editor_widget.dart';
class ProgressCreationScreen extends StatefulWidget { class ProgressCreationScreen extends StatefulWidget {
@ -17,10 +20,16 @@ class ProgressCreationScreen extends StatefulWidget {
} }
class _ProgressCreationScreenState extends State<ProgressCreationScreen> { class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
TimeProgress timeProgressToCreate = TimeProgress timeProgressToCreate;
TimeProgress("", DateTime.now(), DateTime(DateTime.now().year + 1));
bool _isProgressValid = false; bool _isProgressValid = false;
void initTimeProgress(TimeProgress timeProgress) {
if (timeProgressToCreate == null)
setState(() {
timeProgressToCreate = timeProgress;
});
}
void onTimeProgressChanged( void onTimeProgressChanged(
TimeProgress newTimeProgress, bool isNewProgressValid) { TimeProgress newTimeProgress, bool isNewProgressValid) {
setState(() { setState(() {
@ -37,25 +46,34 @@ class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
), ),
body: Container( body: Container(
padding: EdgeInsets.all(12), padding: EdgeInsets.all(12),
child: ProgressEditorWidget( child: StoreConnector<AppState, _ViewModel>(
timeProgress: timeProgressToCreate, onInit: loadSettingsIfUnloaded,
onTimeProgressChanged: onTimeProgressChanged, converter: (store) => _ViewModel.create(store),
), builder: (context, _ViewModel viewModel) {
initTimeProgress(viewModel.defaultDurationProgress);
return ProgressEditorWidget(
timeProgress: timeProgressToCreate,
onTimeProgressChanged: onTimeProgressChanged,
);
}),
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: Row( floatingActionButton: Row(
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FloatingActionButton( child: StoreConnector<AppState, _ViewModel>(
heroTag: "createTimeProgressBTN", onInit: loadSettingsIfUnloaded,
child: Icon(Icons.save), converter: (store) => _ViewModel.create(store),
onPressed: _isProgressValid builder: (context, _ViewModel vm) => FloatingActionButton(
? () { heroTag: "createTimeProgressBTN",
StoreProvider.of<AppState>(context).dispatch( child: Icon(Icons.save),
AddTimeProgressAction(timeProgressToCreate)); onPressed: _isProgressValid
Navigator.pop(context); ? () {
} vm.onAddTimeProgress(timeProgressToCreate);
: null, Navigator.pop(context);
}
: null,
),
), ),
), ),
Expanded( Expanded(
@ -72,3 +90,27 @@ class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
); );
} }
} }
class _ViewModel {
final TimeProgress defaultDurationProgress;
final void Function(TimeProgress) onAddTimeProgress;
_ViewModel({
@required this.defaultDurationProgress,
@required this.onAddTimeProgress,
});
factory _ViewModel.create(Store<AppState> store) {
AppSettings settings = appSettingsSelector(store.state);
_onAddTimeProgress(TimeProgress tp) {
if (TimeProgress.isValid(tp)) store.dispatch(AddTimeProgressAction(tp));
}
return _ViewModel(
defaultDurationProgress:
TimeProgress.defaultFromDuration(settings.duration),
onAddTimeProgress: _onAddTimeProgress,
);
}
}

View File

@ -1,14 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/screens/home_screen.dart'; import 'package:time_progress_tracker/screens/home_screen.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart';
import 'package:time_progress_tracker/widgets/detail_screen_floating_action_buttons.dart'; import 'package:time_progress_tracker/widgets/detail_screen_floating_action_buttons.dart';
import 'package:time_progress_tracker/widgets/progress_editor_widget.dart'; import 'package:time_progress_tracker/widgets/progress_editor_widget.dart';
import 'package:time_progress_tracker/widgets/progress_view_widget.dart'; import 'package:time_progress_tracker/widgets/progress_view_widget.dart';
import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart';
import 'package:time_progress_tracker/widgets/store_connectors/time_progress_store_connector.dart';
class ProgressDetailScreenArguments { class ProgressDetailScreenArguments {
final String id; final String id;
@ -30,6 +27,13 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
bool _editMode = false, _isEditedProgressValid = false; bool _editMode = false, _isEditedProgressValid = false;
TimeProgress _editedProgress, _originalProgress; TimeProgress _editedProgress, _originalProgress;
void _initEditedProgress(TimeProgress tp) {
if (_editedProgress == null) {
_editedProgress = tp;
_originalProgress = tp;
}
}
void _onEditedProgressChanged( void _onEditedProgressChanged(
TimeProgress newProgress, bool isNewProgressValid) { TimeProgress newProgress, bool isNewProgressValid) {
setState(() { setState(() {
@ -51,78 +55,75 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
}); });
} }
List<Widget> _renderColumnChildren(
SettingsViewModel settingsVm, TimeProgressViewModel tpVm) {
List<Widget> columnChildren = [
Expanded(
child: ProgressViewWidget(
timeProgress: _editMode ? _editedProgress : tpVm.tp,
doneColor: settingsVm.appSettings.doneColor,
leftColor: settingsVm.appSettings.leftColor,
))
];
if (_editMode)
columnChildren.add(Expanded(
child: ProgressEditorWidget(
timeProgress: _editedProgress,
onTimeProgressChanged: _onEditedProgressChanged,
)));
return columnChildren;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ProgressDetailScreenArguments args = final ProgressDetailScreenArguments args =
ModalRoute.of(context).settings.arguments; ModalRoute.of(context).settings.arguments;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(ProgressDetailScreen.title), title: Text(ProgressDetailScreen.title),
), ),
body: Container( body: SettingsStoreConnector(
margin: EdgeInsets.all(8), loadedBuilder: (context, settingsVm) {
child: StoreConnector( return TimeProgressStoreConnector(
onInit: loadTimeProgressListIfUnloaded, timeProgressId: args.id,
converter: (store) => timeProgressByIdSelector(store.state, args.id), loadedBuilder: (context, tpVm) {
builder: (BuildContext context, TimeProgress timeProgress) { _initEditedProgress(tpVm.tp);
if (timeProgress == null) //+++++Time Progress Not Found Error+++++ return Container(
return Center( margin: EdgeInsets.all(8),
child: Text("Error Invalid Time Progress"), child: Column(
); children: _renderColumnChildren(settingsVm, tpVm),
if (_editedProgress == null) { ));
_editedProgress = timeProgress; },
_originalProgress = timeProgress; );
} // initialize _editedProgress },
List<Widget> columnChildren = [
Expanded(
child: ProgressViewWidget(
timeProgress: _editMode ? _editedProgress : timeProgress),
)
];
if (_editMode)
columnChildren.add(Expanded(
child: ProgressEditorWidget(
timeProgress: _editedProgress,
onTimeProgressChanged: _onEditedProgressChanged,
),
));
return Column(
children: columnChildren,
);
},
),
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: StoreConnector( floatingActionButton: TimeProgressStoreConnector(
onInit: loadTimeProgressListIfUnloaded, timeProgressId: args.id,
converter: (store) => timeProgressByIdSelector(store.state, args.id), loadedBuilder: (context, tpVm) {
builder: (BuildContext context, TimeProgress timeProgress) { void _saveEditedProgress() {
final Store<AppState> store = StoreProvider.of<AppState>(context); tpVm.updateTimeProgress(_editedProgress);
_switchEditMode(false);
}
void _saveEditedProgress() { void _deleteTimeProgress() {
store tpVm.deleteTimeProgress();
.dispatch(UpdateTimeProgressAction(args.id, _editedProgress)); Navigator.popUntil(
_switchEditMode(false); context, ModalRoute.withName(HomeScreen.routeName));
} }
void _deleteTimeProgress() { return DetailScreenFloatingActionButtons(
store.dispatch(DeleteTimeProgressAction(args.id)); editMode: _editMode,
Navigator.popUntil( originalProgress: tpVm.tp,
context, ModalRoute.withName(HomeScreen.routeName)); editedProgress: _editedProgress,
} isEditedProgressValid: _isEditedProgressValid,
onEditProgress: () => _switchEditMode(true),
return DetailScreenFloatingActionButtons( onSaveEditedProgress: _saveEditedProgress,
editMode: _editMode, onCancelEditProgress: _cancelEditMode,
originalProgress: timeProgress, onDeleteProgress: _deleteTimeProgress);
editedProgress: _editedProgress, },
isEditedProgressValid: _isEditedProgressValid, ),
onEditProgress: () => _switchEditMode(true),
onSaveEditedProgress: _saveEditedProgress,
onCancelEditProgress: _cancelEditMode,
onDeleteProgress: _deleteTimeProgress);
}),
); );
} }
} }

View File

@ -1,3 +1,6 @@
import 'dart:ui';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart'; import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
@ -47,3 +50,9 @@ TimeProgress timeProgressByIdSelector(AppState state, String id) {
return state.timeProgressList return state.timeProgressList
.firstWhere((timeProgress) => timeProgress.id == id, orElse: () => null); .firstWhere((timeProgress) => timeProgress.id == id, orElse: () => null);
} }
AppSettings appSettingsSelector(AppState state) {
return state.appSettings;
}
Color doneColorSelector(AppState state) => state.appSettings.doneColor;

View File

@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:time_progress_tracker/helper_functions.dart';
class ColorPickerButton extends StatelessWidget {
final String title, dialogTitle;
final Color selectedColor;
final void Function(Color) onColorPicked;
ColorPickerButton({
@required this.title,
@required this.dialogTitle,
@required this.selectedColor,
@required this.onColorPicked,
});
@override
Widget build(BuildContext context) {
ThemeData appTheme = Theme.of(context);
return TextButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(dialogTitle),
content: SingleChildScrollView(
child: BlockPicker(
pickerColor: selectedColor,
onColorChanged: onColorPicked,
),
),
);
},
);
},
child: Text(title),
style: TextButton.styleFrom(
primary: useBrightBackground(selectedColor)
? appTheme.primaryTextTheme.button.color
: appTheme.textTheme.button.color,
backgroundColor: selectedColor,
),
);
}
}

View File

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
import 'package:time_progress_tracker/screens/progress_creation_screen.dart';
class CreateProgressButton extends StatelessWidget {
final String _heroTag = "createProgressBTN";
@override
Widget build(BuildContext context) {
void _onButtonPressed() =>
Navigator.pushNamed(context, ProgressCreationScreen.routeName);
return FloatingActionButton(
heroTag: _heroTag,
child: Icon(Icons.add),
onPressed: _onButtonPressed,
);
}
}

View File

@ -11,21 +11,26 @@ class DatePickerBtn extends StatelessWidget {
@required this.onDatePicked, @required this.onDatePicked,
}) : super(); }) : super();
void _onButtonPressed(BuildContext context) async {
onDatePicked(await showDatePicker(
context: context,
initialDate: pickedDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
));
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ThemeData appTheme = Theme.of(context); ThemeData appTheme = Theme.of(context);
return FlatButton( return TextButton(
onPressed: () async { onPressed: () => _onButtonPressed(context),
onDatePicked(await showDatePicker(
context: context,
initialDate: pickedDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
));
},
child: Text( child: Text(
"$leadingString ${pickedDate.toLocal().toString().split(" ")[0]}"), "$leadingString ${pickedDate.toLocal().toString().split(" ")[0]}"),
color: appTheme.accentColor, style: TextButton.styleFrom(
primary: appTheme.primaryTextTheme.button.color,
backgroundColor: appTheme.accentColor,
),
); );
} }
} }

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_picker/flutter_picker.dart';
class SelectDurationBtn extends StatelessWidget {
final Duration duration;
final void Function(Duration) updateDuration;
SelectDurationBtn({
@required this.duration,
@required this.updateDuration,
});
void _onPickerConfirm(Picker picker, List<int> values) {
int years = values[0], months = values[1], days = values[2];
days = (years * 365) + (months * 31) + days;
Duration newDuration = Duration(days: days);
updateDuration(newDuration);
}
void _onButtonPressed(BuildContext context, ThemeData appTheme) => Picker(
adapter: NumberPickerAdapter(data: [
const NumberPickerColumn(begin: 0, end: 999, suffix: Text(" Y")),
const NumberPickerColumn(begin: 0, end: 11, suffix: Text(" M")),
const NumberPickerColumn(begin: 0, end: 31, suffix: Text(" D")),
]),
hideHeader: false,
title: const Text("Default Duration"),
selectedTextStyle: TextStyle(color: appTheme.accentColor),
onConfirm: _onPickerConfirm)
.showModal(context);
@override
Widget build(BuildContext context) {
ThemeData appTheme = Theme.of(context);
int years = duration.inDays ~/ 365;
int months = (duration.inDays - (365 * years)) ~/ 30;
int days = duration.inDays - (365 * years) - (30 * months);
return TextButton(
onPressed: () => _onButtonPressed(context, appTheme),
child: Text("$years Years $months Months $days Days"),
style: TextButton.styleFrom(
primary: appTheme.primaryTextTheme.button.color,
backgroundColor: appTheme.accentColor,
));
}
}

View File

@ -1,44 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/screens/progress_detail_screen.dart';
class HomeProgressListTile extends StatelessWidget {
final TimeProgress timeProgress;
HomeProgressListTile({
Key key,
@required this.timeProgress,
}) : super(key: key);
@override
Widget build(BuildContext context) {
Widget listTileSubTitle;
if (timeProgress.hasStarted() && !timeProgress.hasEnded())
listTileSubTitle = LinearPercentIndicator(
center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
percent: timeProgress.percentDone(),
progressColor: Colors.green,
backgroundColor: Colors.red,
lineHeight: 20,
);
if (!timeProgress.hasStarted())
listTileSubTitle = Text(
"Starts in ${timeProgress.startTime.difference(DateTime.now()).inDays} Days");
if (timeProgress.hasEnded())
listTileSubTitle = Text(
"Ended ${DateTime.now().difference(timeProgress.endTime).inDays} Days ago.");
return Card(
child: ListTile(
title: Text(timeProgress.name),
subtitle: listTileSubTitle,
onTap: () {
Navigator.pushNamed(context, ProgressDetailScreen.routeName,
arguments: ProgressDetailScreenArguments(timeProgress.id));
},
),
);
}
}

View File

@ -1,40 +1,32 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:time_progress_tracker/helper_functions.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_view.dart';
import 'package:time_progress_tracker/widgets/home/home_progress_list_tile.dart'; import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart';
import 'package:time_progress_tracker/widgets/store_connectors/time_progress_list_store_connector.dart';
class HomeActiveProgressesTab extends StatelessWidget { class HomeActiveProgressesTab extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StoreConnector( return SettingsStoreConnector(
onInit: loadTimeProgressListIfUnloaded, loadedBuilder: (context, settingsVm) {
converter: (store) => store.state.hasLoaded, return TimeProgressListStoreConnector(
builder: (BuildContext context, dynamic hasLoaded) { loadedBuilder: (context, tpListVm) {
if (!(hasLoaded as bool)) List<TimeProgress> activeTpList =
return Center( selectActiveProgresses(tpListVm.tpList);
child: CircularProgressIndicator(), if (activeTpList.length < 1)
);
return StoreConnector(
onInit: loadTimeProgressListIfUnloaded,
converter: (store) => activeTimeProgressesSelector(store.state),
builder: (BuildContext context, List<TimeProgress> timeProgresses) {
if (timeProgresses.length < 1)
return Container( return Container(
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
child: Center( child: Center(
child: Text( child: Text(
"You don't have any currently active time progresses, that are tracked."), "You don't have any active time progress, that are tracked."),
), ),
); );
return ListView(
padding: EdgeInsets.all(8), return ProgressListView(
children: timeProgresses timeProgressList: activeTpList,
.map((timeProgress) => HomeProgressListTile( doneColor: settingsVm.appSettings.doneColor,
timeProgress: timeProgress, leftColor: settingsVm.appSettings.leftColor,
))
.toList(),
); );
}, },
); );

View File

@ -1,26 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart'; import 'package:time_progress_tracker/helper_functions.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/selectors/time_progress_selectors.dart'; import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_view.dart';
import 'package:time_progress_tracker/widgets/home/home_progress_list_tile.dart'; import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart';
import 'package:time_progress_tracker/widgets/store_connectors/time_progress_list_store_connector.dart';
class HomeInactiveProgressesTab extends StatelessWidget { class HomeInactiveProgressesTab extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StoreConnector( return SettingsStoreConnector(
onInit: loadTimeProgressListIfUnloaded, loadedBuilder: (context, settingsVm) {
converter: (store) => store.state.hasLoaded, return TimeProgressListStoreConnector(
builder: (BuildContext context, dynamic hasLoaded) { loadedBuilder: (context, tpListVm) {
if (!(hasLoaded as bool)) List<TimeProgress> inactiveTpList =
return Center( selectInactiveProgresses(tpListVm.tpList);
child: CircularProgressIndicator(), if (inactiveTpList.length < 1)
);
return StoreConnector(
onInit: loadTimeProgressListIfUnloaded,
converter: (store) => inactiveTimeProgressesSelector(store.state),
builder: (BuildContext context, List<TimeProgress> timeProgresses) {
if (timeProgresses.length < 1)
return Container( return Container(
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
child: Center( child: Center(
@ -28,12 +22,11 @@ class HomeInactiveProgressesTab extends StatelessWidget {
"You don't have any currently inactive time progresses, that are tracked."), "You don't have any currently inactive time progresses, that are tracked."),
), ),
); );
return ListView(
padding: EdgeInsets.all(8), return ProgressListView(
children: timeProgresses timeProgressList: inactiveTpList,
.map((timeProgress) => doneColor: settingsVm.appSettings.doneColor,
HomeProgressListTile(timeProgress: timeProgress)) leftColor: settingsVm.appSettings.leftColor,
.toList(),
); );
}, },
); );

View File

@ -1,28 +1,52 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:package_info/package_info.dart';
import 'package:time_progress_tracker/app.dart'; import 'package:time_progress_tracker/app.dart';
import 'package:time_progress_tracker/widgets/home/tabs/settings/color_settings_widget.dart';
import 'package:time_progress_tracker/widgets/home/tabs/settings/duration_settings_widget.dart';
import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart';
class HomeSettingsTab extends StatelessWidget { class HomeSettingsTab extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return SettingsStoreConnector(
padding: EdgeInsets.all(16), loadedBuilder: (context, settingsVm) {
child: Center( return Container(
child: Column( padding: EdgeInsets.all(16),
children: [ child: Center(
Text("The Settings of this App are not yet implemented."), child: Column(
FlatButton( children: [
onPressed: () { Expanded(
showAboutDialog( child: ColorSettingsWidget(
context: context, doneColor: settingsVm.appSettings.doneColor,
applicationName: TimeProgressTrackerApp.name, leftColor: settingsVm.appSettings.leftColor,
applicationVersion: "Beta", updateDoneColor: settingsVm.updateDoneColor,
applicationLegalese: '\u00a9Andreas Fahrecker 2020-2021'); updateLeftColor: settingsVm.updateLeftColor,
}, ),
child: Text("About")) ),
], Expanded(
), child: DurationSettingsWidget(
), duration: settingsVm.appSettings.duration,
updateDuration: settingsVm.updateDuration,
),
),
Spacer(),
Expanded(
child: TextButton(
onPressed: () {
showAboutDialog(
context: context,
applicationName: TimeProgressTrackerApp.name,
applicationVersion: "Beta",
applicationLegalese:
'\u00a9Andreas Fahrecker 2020-2021');
},
child: Text("About"),
),
),
],
),
),
);
},
); );
} }
} }

View File

@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:time_progress_tracker/widgets/buttons/color_picker_btn.dart';
class ColorSettingsWidget extends StatelessWidget {
final Color doneColor, leftColor;
final void Function(Color) updateDoneColor, updateLeftColor;
ColorSettingsWidget({
@required this.doneColor,
@required this.leftColor,
@required this.updateDoneColor,
@required this.updateLeftColor,
});
@override
Widget build(BuildContext context) {
ThemeData appTheme = Theme.of(context);
return Column(
children: [
Expanded(
child: Text(
"Color Settings",
style: appTheme.textTheme.headline6,
),
),
Row(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(right: 5),
child: ColorPickerButton(
title: "Done Color",
dialogTitle: "Select Done Color",
selectedColor: doneColor,
onColorPicked: updateDoneColor,
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 5),
child: ColorPickerButton(
title: "Left Color",
dialogTitle: "Select Left Color",
selectedColor: leftColor,
onColorPicked: updateLeftColor,
),
),
),
],
)
],
);
}
}

View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:time_progress_tracker/widgets/buttons/select_duration_btn.dart';
class DurationSettingsWidget extends StatelessWidget {
final Duration duration;
final void Function(Duration) updateDuration;
DurationSettingsWidget({
@required this.duration,
@required this.updateDuration,
});
@override
Widget build(BuildContext context) {
ThemeData appTheme = Theme.of(context);
int years = duration.inDays ~/ 365;
int months = (duration.inDays - (365 * years)) ~/ 30;
int days = duration.inDays - (365 * years) - (30 * months);
return Column(
children: [
Expanded(
child: Text(
"Duration Settings",
style: appTheme.textTheme.headline6,
),
),
Row(
children: [
Expanded(
child: SelectDurationBtn(
duration: duration,
updateDuration: updateDuration,
),
)
],
)
],
);
}
}

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/widgets/date_picker_btn.dart'; import 'package:time_progress_tracker/widgets/buttons/date_picker_btn.dart';
class ProgressEditorWidget extends StatefulWidget { class ProgressEditorWidget extends StatefulWidget {
final TimeProgress timeProgress; final TimeProgress timeProgress;

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/screens/progress_detail_screen.dart';
class ProgressListTileStrings {
static String percentString(TimeProgress tp) =>
"${(tp.percentDone() * 100).floorToDouble()} %";
static String startsInDaysString(TimeProgress tp) =>
"Starts in ${tp.daysTillStart()} Days.";
static String endedDaysAgoString(TimeProgress tp) =>
"Ended ${tp.daysSinceEnd()} Days ago.";
}
class ProgressListTile extends StatelessWidget {
final TimeProgress timeProgress;
final Color doneColor, leftColor;
ProgressListTile({
@required this.timeProgress,
@required this.doneColor,
@required this.leftColor,
});
Widget _renderSubtitle(BuildContext context) {
if (!timeProgress.hasStarted())
return Text(ProgressListTileStrings.startsInDaysString(timeProgress));
if (timeProgress.hasEnded())
return Text(ProgressListTileStrings.endedDaysAgoString(timeProgress));
return LinearPercentIndicator(
center: Text(ProgressListTileStrings.percentString(timeProgress)),
percent: timeProgress.percentDone(),
progressColor: doneColor,
backgroundColor: leftColor,
lineHeight: 20,
);
}
@override
Widget build(BuildContext context) {
void _onTileTap() =>
Navigator.pushNamed(context, ProgressDetailScreen.routeName,
arguments: ProgressDetailScreenArguments(timeProgress.id));
return ListTile(
title: Text(timeProgress.name),
subtitle: _renderSubtitle(context),
onTap: _onTileTap,
);
}
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_tile.dart';
class ProgressListView extends StatelessWidget {
final List<TimeProgress> timeProgressList;
final Color doneColor, leftColor;
ProgressListView({
@required this.timeProgressList,
@required this.doneColor,
@required this.leftColor,
});
List<Widget> _renderListViewChildren() {
return timeProgressList
.map((e) => Card(
child: ProgressListTile(
timeProgress: e,
doneColor: doneColor,
leftColor: leftColor,
),
))
.toList(growable: false);
}
@override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.all(8),
children: _renderListViewChildren(),
);
}
}

View File

@ -5,9 +5,13 @@ import 'package:time_progress_tracker/models/time_progress.dart';
class ProgressViewWidget extends StatelessWidget { class ProgressViewWidget extends StatelessWidget {
final TimeProgress timeProgress; final TimeProgress timeProgress;
final Color doneColor;
final Color leftColor;
ProgressViewWidget({ ProgressViewWidget({
@required this.timeProgress, @required this.timeProgress,
@required this.doneColor,
@required this.leftColor,
}); });
@override @override
@ -33,8 +37,8 @@ class ProgressViewWidget extends StatelessWidget {
radius: 100, radius: 100,
lineWidth: 10, lineWidth: 10,
percent: timeProgress.percentDone(), percent: timeProgress.percentDone(),
progressColor: Colors.green, progressColor: doneColor,
backgroundColor: Colors.red, backgroundColor: leftColor,
center: Text("${(timeProgress.percentDone() * 100).floor()} %"), center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
), ),
), ),
@ -43,10 +47,13 @@ class ProgressViewWidget extends StatelessWidget {
padding: EdgeInsets.symmetric(horizontal: 15), padding: EdgeInsets.symmetric(horizontal: 15),
percent: timeProgress.percentDone(), percent: timeProgress.percentDone(),
leading: Text("${timeProgress.daysBehind()} Days"), leading: Text("${timeProgress.daysBehind()} Days"),
center: Text("${(timeProgress.percentDone() * 100).floor()} %"), center: Text(
"${(timeProgress.percentDone() * 100).floor()} %",
style: TextStyle(color: Colors.white),
),
trailing: Text("${timeProgress.daysLeft()} Days"), trailing: Text("${timeProgress.daysLeft()} Days"),
progressColor: Colors.green, progressColor: doneColor,
backgroundColor: Colors.red, backgroundColor: leftColor,
lineHeight: 25, lineHeight: 25,
), ),
), ),

View File

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/app_state.dart';
class SettingsStoreConnector extends StatelessWidget {
final Widget Function(BuildContext, SettingsViewModel) loadedBuilder;
SettingsStoreConnector({
@required this.loadedBuilder,
});
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, SettingsViewModel>(
onInit: loadSettingsIfUnloaded,
converter: (store) => SettingsViewModel._create(store),
builder: (context, SettingsViewModel vm) {
if (!vm.hasSettingsLoaded)
return Center(
child: CircularProgressIndicator(),
);
return loadedBuilder(context, vm);
},
);
}
}
class SettingsViewModel {
final AppSettings appSettings;
final bool hasSettingsLoaded;
final void Function(Color) updateDoneColor, updateLeftColor;
final void Function(Duration) updateDuration;
SettingsViewModel(
this.appSettings,
this.hasSettingsLoaded,
this.updateDoneColor,
this.updateLeftColor,
this.updateDuration,
);
factory SettingsViewModel._create(Store<AppState> store) {
AppSettings _appSettings = store.state.appSettings;
void _updateDoneColor(Color dC) => store.dispatch(
UpdateAppSettingsActions(_appSettings.copyWith(doneColor: dC)));
void _updateLeftColor(Color lC) => store.dispatch(
UpdateAppSettingsActions(_appSettings.copyWith(leftColor: lC)));
void _updateDuration(Duration d) => store
.dispatch(UpdateAppSettingsActions(_appSettings.copyWith(duration: d)));
return SettingsViewModel(_appSettings, store.state.hasSettingsLoaded,
_updateDoneColor, _updateLeftColor, _updateDuration);
}
}

View File

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
class TimeProgressListStoreConnector extends StatelessWidget {
final Widget Function(BuildContext, TimeProgressListViewModel) loadedBuilder;
TimeProgressListStoreConnector({
@required this.loadedBuilder,
});
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, TimeProgressListViewModel>(
onInit: loadTimeProgressListIfUnloaded,
converter: (store) => TimeProgressListViewModel._create(store),
builder: (context, TimeProgressListViewModel vm) {
if (!vm.hasTpListLoaded)
return Center(
child: CircularProgressIndicator(),
);
return loadedBuilder(context, vm);
},
);
}
}
class TimeProgressListViewModel {
final List<TimeProgress> tpList;
final bool hasTpListLoaded;
TimeProgressListViewModel(this.tpList, this.hasTpListLoaded);
factory TimeProgressListViewModel._create(Store<AppState> store) =>
TimeProgressListViewModel(
store.state.timeProgressList, store.state.hasProgressesLoaded);
}

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
import 'package:time_progress_tracker/models/app_state.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import '../../helper_functions.dart';
class TimeProgressStoreConnector extends StatelessWidget {
final String timeProgressId;
final Widget Function(BuildContext, TimeProgressViewModel) loadedBuilder;
TimeProgressStoreConnector({
@required this.timeProgressId,
@required this.loadedBuilder,
});
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, TimeProgressViewModel>(
onInit: loadTimeProgressListIfUnloaded,
converter: (store) =>
TimeProgressViewModel._create(store, timeProgressId),
builder: (context, TimeProgressViewModel vm) {
if (!vm.hasTpListLoaded)
return Center(
child: CircularProgressIndicator(),
);
if (vm.tp == null)
return Center(
child: Text("Error Invalid Time Progress"),
);
return loadedBuilder(context, vm);
},
);
}
}
class TimeProgressViewModel {
final TimeProgress tp;
final bool hasTpListLoaded;
final void Function(TimeProgress) updateTimeProgress;
final void Function() deleteTimeProgress;
TimeProgressViewModel(
this.tp,
this.hasTpListLoaded,
this.updateTimeProgress,
this.deleteTimeProgress,
);
factory TimeProgressViewModel._create(Store<AppState> store, String id) {
void _updateTimeProgress(TimeProgress tp) =>
store.dispatch(UpdateTimeProgressAction(id, tp));
void _deleteTimeProgress() => store.dispatch(DeleteTimeProgressAction(id));
return TimeProgressViewModel(
selectProgressById(store.state.timeProgressList, id),
store.state.hasProgressesLoaded,
_updateTimeProgress,
_deleteTimeProgress,
);
}
}

View File

@ -5,119 +5,168 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: archive name: archive
url: "https://pub.dartlang.org" sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.13" version: "3.4.10"
args: args:
dependency: transitive dependency: transitive
description: description:
name: args name: args
url: "https://pub.dartlang.org" sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted source: hosted
version: "1.6.0" version: "2.4.2"
async: async:
dependency: transitive dependency: transitive
description: description:
name: async name: async
url: "https://pub.dartlang.org" sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.0" version: "2.11.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
url: "https://pub.dartlang.org" sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
characters: characters:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
url: "https://pub.dartlang.org" sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.3.0"
charcode: checked_yaml:
dependency: transitive dependency: transitive
description: description:
name: charcode name: checked_yaml
url: "https://pub.dartlang.org" sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "2.0.3"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
url: "https://pub.dartlang.org" sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
url: "https://pub.dartlang.org" sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted source: hosted
version: "1.15.0" version: "1.18.0"
convert: convert:
dependency: transitive dependency: transitive
description: description:
name: convert name: convert
url: "https://pub.dartlang.org" sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "3.1.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
name: crypto name: crypto
url: "https://pub.dartlang.org" sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.5" version: "3.0.3"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_icons name: cupertino_icons
url: "https://pub.dartlang.org" sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.3" version: "1.0.6"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
url: "https://pub.dartlang.org" sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.3.1"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
name: ffi name: ffi
url: "https://pub.dartlang.org" sha256: d97fffd9d86f3dccc7a9059128b468a99320c69007cc9d41a3a1bda07d4e86dc
url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.3" version: "1.0.0"
file: file:
dependency: transitive dependency: transitive
description: description:
name: file name: file
url: "https://pub.dartlang.org" sha256: "9fd2163d866769f60f4df8ac1dc59f52498d810c356fe78022e383dd3c57c0e1"
url: "https://pub.dev"
source: hosted source: hosted
version: "5.2.1" version: "6.1.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_colorpicker:
dependency: "direct main"
description:
name: flutter_colorpicker
sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
flutter_launcher_icons: flutter_launcher_icons:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_launcher_icons name: flutter_launcher_icons
url: "https://pub.dartlang.org" sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.1" version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev"
source: hosted
version: "3.0.1"
flutter_picker:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "4a88a436b64d043d2e5412d64035a9bbe7f9d45d"
url: "https://github.com/yangyxd/flutter_picker.git"
source: git
version: "2.1.1"
flutter_redux: flutter_redux:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_redux name: flutter_redux
url: "https://pub.dartlang.org" sha256: "3b20be9e08d0038e1452fbfa1fdb1ea0a7c3738c997734530b3c6d0bb5fcdbdc"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.0" version: "0.10.0"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -132,156 +181,218 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image name: image
url: "https://pub.dartlang.org" sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.19" version: "4.1.7"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.1"
js: js:
dependency: transitive dependency: transitive
description: description:
name: js name: js
url: "https://pub.dartlang.org" sha256: d9bdfd70d828eeb352390f81b18d6a354ef2044aa28ef25682079797fa7cd174
url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.3" version: "0.6.3"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
url: "https://pub.dev"
source: hosted
version: "4.8.1"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
url: "https://pub.dev"
source: hosted
version: "2.0.1"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
url: "https://pub.dev"
source: hosted
version: "2.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "3.0.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
url: "https://pub.dartlang.org" sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.10" version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.8.0"
meta: meta:
dependency: "direct main" dependency: "direct main"
description: description:
name: meta name: meta
url: "https://pub.dartlang.org" sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.11.0"
package_info:
dependency: "direct main"
description:
name: package_info
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.3+4"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
url: "https://pub.dartlang.org" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.0" version: "1.9.0"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
name: path_provider_linux name: path_provider_linux
url: "https://pub.dartlang.org" sha256: "938d2b6591587bcb009d2109a6ea464fd8fb2a75dc6423171b0d0afb1d27c708"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.1+2" version: "2.0.0"
path_provider_platform_interface: path_provider_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: path_provider_platform_interface name: path_provider_platform_interface
url: "https://pub.dartlang.org" sha256: "2e14fc474b8acfc4111ac8eb0e37c2fe70234f9f8cd796f1560d03aa1689fa51"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "2.0.0"
path_provider_windows: path_provider_windows:
dependency: transitive dependency: transitive
description: description:
name: path_provider_windows name: path_provider_windows
url: "https://pub.dartlang.org" sha256: ecd4d04c225596bcf0fbb86408a1f40084a02dfa412e60172ad52a7f12a781ef
url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.4+3" version: "2.0.0"
percent_indicator: percent_indicator:
dependency: "direct main" dependency: "direct main"
description: description:
name: percent_indicator name: percent_indicator
url: "https://pub.dartlang.org" sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.9+1" version: "4.2.3"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
name: petitparser name: petitparser
url: "https://pub.dartlang.org" sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "6.0.2"
platform: platform:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
url: "https://pub.dartlang.org" sha256: ebc79f16b5f6b609aad4a5e63447d4795d16f7adee46e93ed03200848c006735
url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "3.0.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
url: "https://pub.dartlang.org" sha256: c2c49e16d42fd6983eb55e44b7f197fdf16b4da7aab7f8e1d21da307cad3fb02
url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.3" version: "2.0.0"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.dev"
source: hosted
version: "3.7.4"
process: process:
dependency: transitive dependency: transitive
description: description:
name: process name: process
url: "https://pub.dartlang.org" sha256: c7b9f7d8a6ee4407ab4f8a7d4a951f8f5659c40df14c0924e2e97c32372e9b14
url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.13" version: "4.1.0"
redux: redux:
dependency: "direct main" dependency: "direct main"
description: description:
name: redux name: redux
url: "https://pub.dartlang.org" sha256: "1e86ed5b1a9a717922d0a0ca41f9bf49c1a587d50050e9426fc65b14e85ec4d7"
url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0+3" version: "5.0.0"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" sha256: "2ead304936605c686c0c1a5ce3d9771dbc2e60d76db68b9cd313eb860ca8f9e3"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.12+4" version: "2.0.0"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_linux name: shared_preferences_linux
url: "https://pub.dartlang.org" sha256: "33fc7c6ced70d226645a9612132fbff9890805df4edd34f30840e7e738866fee"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.2+4" version: "2.0.0"
shared_preferences_macos: shared_preferences_macos:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_macos name: shared_preferences_macos
url: "https://pub.dartlang.org" sha256: "5d2bad07b196b6ad4cf21af6f7197a87264ef569199502b9352f76e5054f06ae"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.1+11" version: "2.0.0"
shared_preferences_platform_interface: shared_preferences_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_platform_interface name: shared_preferences_platform_interface
url: "https://pub.dartlang.org" sha256: "992f0fdc46d0a3c0ac2e5859f2de0e577bbe51f78a77ee8f357cbe626a2ad32d"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.4" version: "2.0.0"
shared_preferences_web: shared_preferences_web:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_web name: shared_preferences_web
url: "https://pub.dartlang.org" sha256: "09b72ec530a1b1f26cdbec6b138f980d97d4d86ebb86dbf6365369fbd4bb05c8"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.2+7" version: "2.0.0"
shared_preferences_windows: shared_preferences_windows:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_windows name: shared_preferences_windows
url: "https://pub.dartlang.org" sha256: "76c54a0148780d779a3fe332ece9ba8ad2c9dd0bc717ee7fce58bd06b5e8118f"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.0.2+3" version: "2.0.0"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -291,86 +402,106 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_span name: source_span
url: "https://pub.dartlang.org" sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.0" version: "1.10.0"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
url: "https://pub.dartlang.org" sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.11.1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
url: "https://pub.dartlang.org" sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.2"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
url: "https://pub.dartlang.org" sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.0" version: "1.2.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
url: "https://pub.dartlang.org" sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.dartlang.org" sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.19" version: "0.6.1"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.dartlang.org" sha256: "53bdf7e979cfbf3e28987552fd72f637e63f3c8724c9e56d9246942dc2fa36ee"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
url: "https://pub.dartlang.org" sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "13.0.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
url: "https://pub.dartlang.org" sha256: c6a3f4e4058b70b46e27b2935de2d1562c50680e7fb44833911d981db73826c2
url: "https://pub.dev"
source: hosted source: hosted
version: "1.7.4+1" version: "2.0.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
name: xdg_directories name: xdg_directories
url: "https://pub.dartlang.org" sha256: "0186b3f2d66be9a12b0295bddcf8b6f8c0b0cc2f85c6287344e2a6366bc28457"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.2" version: "0.2.0"
xml: xml:
dependency: transitive dependency: transitive
description: description:
name: xml name: xml
url: "https://pub.dartlang.org" sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted source: hosted
version: "4.5.1" version: "6.5.0"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
name: yaml name: yaml
url: "https://pub.dartlang.org" sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "3.1.2"
sdks: sdks:
dart: ">=2.12.0-0.0 <3.0.0" dart: ">=3.3.1 <4.0.0"
flutter: ">=1.12.13+hotfix.5" flutter: ">=2.12.0"

View File

@ -2,7 +2,7 @@ name: time_progress_tracker
description: A Flutter Application to create Timers with a percentage indicator. description: A Flutter Application to create Timers with a percentage indicator.
# The following line prevents the package from being accidentally published to # The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages. # pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application. # The following defines the version and build number for your application.
@ -12,20 +12,29 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# build by specifying --build-name and --build-number, respectively. # build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode. # In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.0.12+12 version: 0.0.19+19
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: '>=3.3.1 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_colorpicker:
flutter_redux: flutter_redux:
flutter_picker:
git:
url: https://github.com/yangyxd/flutter_picker.git
meta: meta:
package_info:
percent_indicator: percent_indicator:
redux: redux:
shared_preferences: shared_preferences:
@ -33,17 +42,24 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.3 cupertino_icons: ^1.0.6
dev_dependencies: dev_dependencies:
flutter_launcher_icons: flutter_launcher_icons:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec # following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter. # The following section is specific to Flutter packages.
flutter: flutter:
# The following line ensures that the Material Icons font is # The following line ensures that the Material Icons font is
@ -57,7 +73,7 @@ flutter:
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware. # https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see # For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages # https://flutter.dev/assets-and-images/#from-packages

View File

@ -0,0 +1,18 @@
import 'package:flutter/material.dart';
class MaterialTesterWidget extends StatelessWidget {
final Widget widget;
MaterialTesterWidget({
@required this.widget,
});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: widget,
),
);
}
}

View File

@ -7,24 +7,124 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:percent_indicator/linear_percent_indicator.dart';
import 'package:time_progress_tracker/models/app_settings.dart';
import 'package:time_progress_tracker/models/time_progress.dart';
import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_tile.dart';
import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_view.dart';
import 'package:time_progress_calculator/main.dart'; import 'MaterialTesterWidget.dart';
void main() { void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async { final AppSettings _defaultAppSettings = AppSettings.defaults();
// Build our app and trigger a frame. final int _thisYear = DateTime.now().year;
await tester.pumpWidget(MyApp()); final TimeProgress _activeProgress = TimeProgress(
"TestProgress", DateTime(_thisYear - 2), DateTime(_thisYear + 2));
// Verify that our counter starts at 0. void _findStringOnce(String str) => expect(find.text(str), findsOneWidget);
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame. testWidgets("Progress List Tile with currently active progress works",
await tester.tap(find.byIcon(Icons.add)); (WidgetTester tester) async {
await tester.pump(); await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile(
timeProgress: _activeProgress,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
),
));
// Verify that our counter has incremented. _findStringOnce(_activeProgress.name);
expect(find.text('0'), findsNothing); _findStringOnce(ProgressListTileStrings.percentString(_activeProgress));
expect(find.text('1'), findsOneWidget);
WidgetPredicate linearPercentPredicate = (Widget widget) =>
widget is LinearPercentIndicator &&
widget.percent == _activeProgress.percentDone() &&
widget.progressColor == _defaultAppSettings.doneColor &&
widget.backgroundColor == _defaultAppSettings.leftColor;
expect(find.byWidgetPredicate(linearPercentPredicate), findsOneWidget);
});
testWidgets("Progress List Tile with future progress works",
(WidgetTester tester) async {
TimeProgress futureProgress = TimeProgress(
"Test Progress",
DateTime(_thisYear + 1),
DateTime(_thisYear + 2),
);
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile(
timeProgress: futureProgress,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
),
));
_findStringOnce(futureProgress.name);
_findStringOnce(ProgressListTileStrings.startsInDaysString(futureProgress));
});
testWidgets("Progress List Tile with past progress works",
(WidgetTester tester) async {
TimeProgress pastProgress = TimeProgress(
"Test Progress",
DateTime(_thisYear - 2),
DateTime(_thisYear - 1),
);
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile(
timeProgress: pastProgress,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
),
));
_findStringOnce(pastProgress.name);
_findStringOnce(ProgressListTileStrings.endedDaysAgoString(pastProgress));
});
WidgetPredicate getProgressListTilePredicate(
TimeProgress tp, AppSettings as) =>
(Widget widget) =>
widget is ProgressListTile &&
widget.timeProgress == tp &&
widget.doneColor == as.doneColor &&
widget.leftColor == as.leftColor;
testWidgets("Progress List View displays one tile",
(WidgetTester tester) async {
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListView(
timeProgressList: [_activeProgress],
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
),
));
_findStringOnce(_activeProgress.name);
expect(
find.byWidgetPredicate(
getProgressListTilePredicate(_activeProgress, _defaultAppSettings)),
findsOneWidget);
});
testWidgets("Progress List View displays file tiles",
(WidgetTester tester) async {
List<TimeProgress> tpList = [];
for (int i = 0; i < 5; i++) tpList.add(_activeProgress);
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListView(
timeProgressList: tpList,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
),
));
expect(find.text(_activeProgress.name), findsNWidgets(5));
expect(
find.byWidgetPredicate(
getProgressListTilePredicate(_activeProgress, _defaultAppSettings)),
findsNWidgets(5));
}); });
} }

BIN
web/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

BIN
web/icons/Icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
web/icons/Icon-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

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