Compare commits

...

8 Commits

Author SHA1 Message Date
3940184975 Fix Displaying Bug 2024-05-14 20:58:22 +02:00
eee1d12851 Fix Version Name for new Release 2024-03-15 23:05:33 +01:00
e11a73d37e Fixed Android Label 2024-03-15 23:02:13 +01:00
ead31e12c0 Fixed Remaining Migration Errors 2024-03-15 22:47:43 +01:00
421d19f91f Fixed Remaining Problems 2024-03-15 21:09:16 +01:00
3085a295e5 Fixed Simple Problem, that occured after migrating to new version.
Still WIP need to fix more Problems
2024-03-15 07:05:10 +01:00
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
65 changed files with 908 additions and 615 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,10 +22,6 @@ 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 keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties') def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) { if (keystorePropertiesFile.exists()) {
@ -32,21 +29,30 @@ if (keystorePropertiesFile.exists()) {
} }
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 +77,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

@ -3,7 +3,7 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> <!-- 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"> <style name="LaunchTheme" parent="@android:style/Theme.Black.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.

View File

@ -1,9 +1,9 @@
<?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.
@ -12,7 +12,7 @@
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"

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

@ -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,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

@ -41,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,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

@ -49,8 +49,9 @@ class DeleteTimeProgressAction {
} }
void loadTimeProgressListIfUnloaded(Store<AppState> store) { void loadTimeProgressListIfUnloaded(Store<AppState> store) {
if (!store.state.hasProgressesLoaded) if (!store.state.hasProgressesLoaded) {
store.dispatch(LoadTimeProgressListAction()); store.dispatch(LoadTimeProgressListAction());
}
} }
void loadSettingsIfUnloaded(Store<AppState> store) { void loadSettingsIfUnloaded(Store<AppState> store) {

View File

@ -11,10 +11,10 @@ class TimeProgressTrackerApp extends StatelessWidget {
final Store<AppState> store; final Store<AppState> store;
TimeProgressTrackerApp({ const TimeProgressTrackerApp({
Key key, super.key,
this.store, required this.store,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -24,18 +24,21 @@ class TimeProgressTrackerApp extends StatelessWidget {
title: name, title: name,
theme: ThemeData( theme: ThemeData(
primarySwatch: Colors.indigo, primarySwatch: Colors.indigo,
accentColor: Colors.indigoAccent, colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.indigo,
accentColor: Colors.indigoAccent,
backgroundColor: Colors.white
),
brightness: Brightness.light, brightness: Brightness.light,
visualDensity: VisualDensity.adaptivePlatformDensity, visualDensity: VisualDensity.adaptivePlatformDensity,
), ),
initialRoute: HomeScreen.routeName, initialRoute: HomeScreen.routeName,
routes: { routes: {
HomeScreen.routeName: (BuildContext context) => HomeScreen.routeName: (BuildContext context) => const HomeScreen(),
HomeScreen(),
ProgressDetailScreen.routeName: (BuildContext context) => ProgressDetailScreen.routeName: (BuildContext context) =>
ProgressDetailScreen(), const ProgressDetailScreen(),
ProgressCreationScreen.routeName: (BuildContext context) => ProgressCreationScreen.routeName: (BuildContext context) =>
ProgressCreationScreen(), const ProgressCreationScreen(),
}, },
), ),
); );

View File

@ -3,7 +3,7 @@ import 'dart:ui';
import 'package:time_progress_tracker/models/time_progress.dart'; import 'package:time_progress_tracker/models/time_progress.dart';
TimeProgress selectProgressById(List<TimeProgress> tpList, String id) => TimeProgress selectProgressById(List<TimeProgress> tpList, String id) =>
tpList.firstWhere((tp) => tp.id == id, orElse: null); tpList.firstWhere((tp) => tp.id == id, orElse: () => TimeProgress.initialDefault());
List<TimeProgress> selectActiveProgresses(List<TimeProgress> tpList) => List<TimeProgress> selectActiveProgresses(List<TimeProgress> tpList) =>
tpList.where((tp) => tp.hasStarted() && !tp.hasEnded()).toList(); tpList.where((tp) => tp.hasStarted() && !tp.hasEnded()).toList();

View File

@ -17,12 +17,12 @@ List<Middleware<AppState>> createStoreMiddleware(
final loadSettings = _createLoadAppSettings(settingsRepo); final loadSettings = _createLoadAppSettings(settingsRepo);
return [ return [
TypedMiddleware<AppState, LoadTimeProgressListAction>(loadTimeProgressList), TypedMiddleware<AppState, LoadTimeProgressListAction>(loadTimeProgressList).call,
TypedMiddleware<AppState, AddTimeProgressAction>(saveTimeProgressList), TypedMiddleware<AppState, AddTimeProgressAction>(saveTimeProgressList).call,
TypedMiddleware<AppState, UpdateTimeProgressAction>(saveTimeProgressList), TypedMiddleware<AppState, UpdateTimeProgressAction>(saveTimeProgressList).call,
TypedMiddleware<AppState, DeleteTimeProgressAction>(saveTimeProgressList), TypedMiddleware<AppState, DeleteTimeProgressAction>(saveTimeProgressList).call,
TypedMiddleware<AppState, LoadSettingsAction>(loadSettings), TypedMiddleware<AppState, LoadSettingsAction>(loadSettings).call,
TypedMiddleware<AppState, UpdateAppSettingsActions>(saveSettings) TypedMiddleware<AppState, UpdateAppSettingsActions>(saveSettings).call
]; ];
} }
@ -45,9 +45,6 @@ Middleware<AppState> _createLoadTimeProgressList(
repository.loadTimeProgressList().then((timeProgresses) { repository.loadTimeProgressList().then((timeProgresses) {
List<TimeProgress> timeProgressList = List<TimeProgress> timeProgressList =
timeProgresses.map<TimeProgress>(TimeProgress.fromEntity).toList(); timeProgresses.map<TimeProgress>(TimeProgress.fromEntity).toList();
if (timeProgressList == null) {
timeProgressList = [];
}
store.dispatch(TimeProgressListLoadedAction( store.dispatch(TimeProgressListLoadedAction(
timeProgressList, timeProgressList,
)); ));

View File

@ -1,5 +1,5 @@
class TimeProgressInvalidNameException implements Exception { class TimeProgressInvalidNameException implements Exception {
final invalidName; final String invalidName;
TimeProgressInvalidNameException(this.invalidName); TimeProgressInvalidNameException(this.invalidName);
@ -7,8 +7,8 @@ class TimeProgressInvalidNameException implements Exception {
} }
class TimeProgressStartTimeIsNotBeforeEndTimeException implements Exception { class TimeProgressStartTimeIsNotBeforeEndTimeException implements Exception {
final startTime; final DateTime startTime;
final endTime; final DateTime endTime;
TimeProgressStartTimeIsNotBeforeEndTimeException( TimeProgressStartTimeIsNotBeforeEndTimeException(
this.startTime, this.endTime); this.startTime, this.endTime);

View File

@ -7,22 +7,22 @@ class AppSettings {
final Color leftColor; final Color leftColor;
final Duration duration; final Duration duration;
AppSettings({ const AppSettings({
this.doneColor, required this.doneColor,
this.leftColor, required this.leftColor,
this.duration, required this.duration,
}); });
factory AppSettings.defaults() => AppSettings( factory AppSettings.defaults() => const AppSettings(
doneColor: Colors.green, doneColor: Colors.green,
leftColor: Colors.red, leftColor: Colors.red,
duration: Duration(days: 365), duration: Duration(days: 365),
); );
AppSettings copyWith({ AppSettings copyWith({
Color doneColor, Color? doneColor,
Color leftColor, Color? leftColor,
Duration duration, Duration? duration,
}) => }) =>
AppSettings( AppSettings(
doneColor: doneColor ?? this.doneColor, doneColor: doneColor ?? this.doneColor,

View File

@ -8,22 +8,24 @@ class AppState {
final List<TimeProgress> timeProgressList; final List<TimeProgress> timeProgressList;
final AppSettings appSettings; final AppSettings appSettings;
AppState( const AppState(
{this.hasProgressesLoaded = false, {this.hasProgressesLoaded = false,
this.hasSettingsLoaded = false, this.hasSettingsLoaded = false,
this.timeProgressList = const [], this.timeProgressList = const [],
this.appSettings}); required this.appSettings});
factory AppState.initial() => factory AppState.initial() =>
AppState(hasProgressesLoaded: false, appSettings: AppSettings.defaults()); AppState(hasProgressesLoaded: false, appSettings: AppSettings.defaults());
AppState copyWith({ AppState copyWith({
bool hasLoaded, bool? hasLoaded,
List<TimeProgress> timeProgressList, List<TimeProgress>? timeProgressList,
AppSettings? appSettings,
}) { }) {
return AppState( return AppState(
hasProgressesLoaded: hasLoaded ?? this.hasProgressesLoaded, hasProgressesLoaded: hasLoaded ?? hasProgressesLoaded,
timeProgressList: timeProgressList ?? this.timeProgressList, timeProgressList: timeProgressList ?? this.timeProgressList,
appSettings: appSettings ?? this.appSettings,
); );
} }

View File

@ -10,7 +10,7 @@ class TimeProgress {
final DateTime startTime; final DateTime startTime;
final DateTime endTime; final DateTime endTime;
TimeProgress(this.name, this.startTime, this.endTime, {String id}) TimeProgress(this.name, this.startTime, this.endTime, {String? id})
: id = id ?? Uuid().generateV4(); : id = id ?? Uuid().generateV4();
factory TimeProgress.initialDefault() { factory TimeProgress.initialDefault() {
@ -23,7 +23,7 @@ class TimeProgress {
TimeProgress("", DateTime.now(), DateTime.now().add(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}) =>
TimeProgress( TimeProgress(
name ?? this.name, name ?? this.name,
startTime ?? this.startTime, startTime ?? this.startTime,
@ -38,7 +38,7 @@ class TimeProgress {
int allDays() => endTime.difference(startTime).inDays; int allDays() => endTime.difference(startTime).inDays;
double percentDone() { double percentDone() {
double percent = this.daysBehind() / (this.allDays() / 100) / 100; double percent = daysBehind() / (allDays() / 100) / 100;
if (percent < 0) percent = 0; if (percent < 0) percent = 0;
if (percent > 1) percent = 1; if (percent > 1) percent = 1;
return percent; return percent;
@ -48,7 +48,7 @@ class TimeProgress {
DateTime.now().millisecondsSinceEpoch > startTime.millisecondsSinceEpoch; DateTime.now().millisecondsSinceEpoch > startTime.millisecondsSinceEpoch;
int daysTillStart() { int daysTillStart() {
if (hasStarted()) throw new TimeProgressHasStartedException(); if (hasStarted()) throw TimeProgressHasStartedException();
return startTime.difference(DateTime.now()).inDays; return startTime.difference(DateTime.now()).inDays;
} }
@ -56,7 +56,7 @@ class TimeProgress {
DateTime.now().millisecondsSinceEpoch > endTime.millisecondsSinceEpoch; DateTime.now().millisecondsSinceEpoch > endTime.millisecondsSinceEpoch;
int daysSinceEnd() { int daysSinceEnd() {
if (!hasEnded()) throw new TimeProgressHasNotEndedException(); if (!hasEnded()) throw TimeProgressHasNotEndedException();
return DateTime.now().difference(endTime).inDays; return DateTime.now().difference(endTime).inDays;
} }
@ -79,24 +79,26 @@ class TimeProgress {
"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)) {
throw new TimeProgressInvalidNameException(name); throw TimeProgressInvalidNameException(name);
if (!TimeProgress.areTimesValid(startTime, endTime)) }
throw new TimeProgressStartTimeIsNotBeforeEndTimeException( if (!TimeProgress.areTimesValid(startTime, endTime)) {
throw TimeProgressStartTimeIsNotBeforeEndTimeException(
startTime, endTime); startTime, endTime);
}
return TimeProgressEntity(id, name, startTime, endTime); return TimeProgressEntity(id, name, startTime, endTime);
} }
static TimeProgress fromEntity(TimeProgressEntity entity) => static TimeProgress fromEntity(TimeProgressEntity entity) =>
TimeProgress(entity.name, entity.startTime, entity.endTime, TimeProgress(entity.name, entity.startTime, entity.endTime,
id: entity.id ?? Uuid().generateV4()); id: entity.id);
static bool isValid(TimeProgress tp) => static bool isValid(TimeProgress tp) =>
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) =>
name != null && name != "" && name.length > 2 && name.length < 21; name != "" && name.length > 2 && name.length < 21;
static bool areTimesValid(DateTime startTime, DateTime endTime) => static bool areTimesValid(DateTime startTime, DateTime endTime) =>
startTime.isBefore(endTime); startTime.isBefore(endTime);

View File

@ -11,15 +11,16 @@ class AppSettingsRepository {
AppSettingsRepository(this.prefs, {this.codec = json}); AppSettingsRepository(this.prefs, {this.codec = json});
Future<AppSettingsEntity> loadAppSettings() { Future<AppSettingsEntity> loadAppSettings() {
final String jsonString = this.prefs.getString(_key); final String? jsonString = prefs.getString(_key);
if (jsonString == null) if (jsonString == null) {
return Future<AppSettingsEntity>.value(AppSettingsEntity.defaults()); return Future<AppSettingsEntity>.value(AppSettingsEntity.defaults());
}
return Future<AppSettingsEntity>.value( return Future<AppSettingsEntity>.value(
AppSettingsEntity.fromJson(codec.decode(jsonString))); AppSettingsEntity.fromJson(codec.decode(jsonString)));
} }
Future<bool> saveAppSettings(AppSettingsEntity appSettings) => Future<bool> saveAppSettings(AppSettingsEntity appSettings) =>
this.prefs.setString(_key, codec.encode(appSettings)); prefs.setString(_key, codec.encode(appSettings));
} }
class AppSettingsEntity { class AppSettingsEntity {
@ -52,8 +53,8 @@ class AppSettingsEntity {
static AppSettingsEntity fromJson(Map<String, Object> json) => static AppSettingsEntity fromJson(Map<String, Object> json) =>
AppSettingsEntity( AppSettingsEntity(
json[_doneKey], json[_doneKey] as int,
json[_leftKey], json[_leftKey] as int,
json[_durationDaysKey], json[_durationDaysKey] as int,
); );
} }

View File

@ -29,7 +29,7 @@ class TimeProgressEntity {
}; };
} }
static TimeProgressEntity fromJson(Map<String, Object> json) { static TimeProgressEntity fromJson(dynamic json) {
final String id = json["id"] as String; final String id = json["id"] as String;
final String name = json["name"] as String; final String name = json["name"] as String;
final DateTime startTime = final DateTime startTime =

View File

@ -10,13 +10,12 @@ class TimeProgressRepository {
TimeProgressRepository(this.prefs, {this.codec = json}); TimeProgressRepository(this.prefs, {this.codec = json});
Future<List<TimeProgressEntity>> loadTimeProgressList() { Future<List<TimeProgressEntity>> loadTimeProgressList() {
final String jsonString = this.prefs.getString(_key); final String? jsonString = prefs.getString(_key);
if (jsonString == null) { if (jsonString == null) {
return Future<List<TimeProgressEntity>>.value([]); return Future<List<TimeProgressEntity>>.value([]);
} }
return Future<List<TimeProgressEntity>>.value(codec return Future<List<TimeProgressEntity>>.value(codec
.decode(jsonString)["timers"] .decode(jsonString)["timers"]
.cast<Map<String, Object>>()
.map<TimeProgressEntity>(TimeProgressEntity.fromJson) .map<TimeProgressEntity>(TimeProgressEntity.fromJson)
.toList(growable: false)); .toList(growable: false));
} }
@ -24,6 +23,6 @@ class TimeProgressRepository {
Future<bool> saveTimeProgressList(List<TimeProgressEntity> timeProgressList) { Future<bool> saveTimeProgressList(List<TimeProgressEntity> timeProgressList) {
final String jsonString = codec.encode( final String jsonString = codec.encode(
{"timers": timeProgressList.map((timer) => timer.toJson()).toList()}); {"timers": timeProgressList.map((timer) => timer.toJson()).toList()});
return this.prefs.setString(_key, jsonString); return prefs.setString(_key, jsonString);
} }
} }

View File

@ -17,9 +17,9 @@ AppState appStateReducer(AppState state, dynamic action) {
} }
final appSettingsReducers = combineReducers<AppSettings>([ final appSettingsReducers = combineReducers<AppSettings>([
TypedReducer<AppSettings, AppSettingsLoadedActions>(_loadAppSettings), TypedReducer<AppSettings, AppSettingsLoadedActions>(_loadAppSettings).call,
TypedReducer<AppSettings, UpdateAppSettingsActions>(_updateAppSettings), TypedReducer<AppSettings, UpdateAppSettingsActions>(_updateAppSettings).call,
TypedReducer<AppSettings, AppSettingsNotLoadedAction>(_setDefaultSettings) TypedReducer<AppSettings, AppSettingsNotLoadedAction>(_setDefaultSettings).call
]); ]);
AppSettings _loadAppSettings( AppSettings _loadAppSettings(

View File

@ -2,8 +2,8 @@ import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart'; import 'package:time_progress_tracker/actions/actions.dart';
final hasProgressesLoadedReducer = combineReducers<bool>([ final hasProgressesLoadedReducer = combineReducers<bool>([
TypedReducer<bool, TimeProgressListLoadedAction>(_setProgressesLoaded), TypedReducer<bool, TimeProgressListLoadedAction>(_setProgressesLoaded).call,
TypedReducer<bool, TimeProgressListNotLoadedAction>(_setProgressesUnloaded) TypedReducer<bool, TimeProgressListNotLoadedAction>(_setProgressesUnloaded).call
]); ]);
bool _setProgressesLoaded(bool hasLoaded, TimeProgressListLoadedAction action) { bool _setProgressesLoaded(bool hasLoaded, TimeProgressListLoadedAction action) {
@ -15,8 +15,8 @@ bool _setProgressesUnloaded(bool hasLoaded, TimeProgressListNotLoadedAction acti
} }
final hasSettingsLoadedReducer = combineReducers<bool>([ final hasSettingsLoadedReducer = combineReducers<bool>([
TypedReducer<bool, AppSettingsLoadedActions>(_setSettingsLoaded), TypedReducer<bool, AppSettingsLoadedActions>(_setSettingsLoaded).call,
TypedReducer<bool, AppSettingsNotLoadedAction>(_setSettingsUnloaded) TypedReducer<bool, AppSettingsNotLoadedAction>(_setSettingsUnloaded).call
]); ]);
bool _setSettingsLoaded(bool hasLoaded, AppSettingsLoadedActions action) { bool _setSettingsLoaded(bool hasLoaded, AppSettingsLoadedActions action) {

View File

@ -4,13 +4,13 @@ import 'package:time_progress_tracker/models/time_progress.dart';
final timeProgressListReducer = combineReducers<List<TimeProgress>>([ final timeProgressListReducer = combineReducers<List<TimeProgress>>([
TypedReducer<List<TimeProgress>, TimeProgressListLoadedAction>( TypedReducer<List<TimeProgress>, TimeProgressListLoadedAction>(
_setLoadedTimeProgressList), _setLoadedTimeProgressList).call,
TypedReducer<List<TimeProgress>, TimeProgressListNotLoadedAction>( TypedReducer<List<TimeProgress>, TimeProgressListNotLoadedAction>(
_setEmptyTimeProgressList), _setEmptyTimeProgressList).call,
TypedReducer<List<TimeProgress>, AddTimeProgressAction>(_addTimeProgress), TypedReducer<List<TimeProgress>, AddTimeProgressAction>(_addTimeProgress).call,
TypedReducer<List<TimeProgress>, UpdateTimeProgressAction>( TypedReducer<List<TimeProgress>, UpdateTimeProgressAction>(
_updateTimeProgress), _updateTimeProgress).call,
TypedReducer<List<TimeProgress>, DeleteTimeProgressAction>(_deleteTimeProgress), TypedReducer<List<TimeProgress>, DeleteTimeProgressAction>(_deleteTimeProgress).call,
]); ]);
List<TimeProgress> _setLoadedTimeProgressList( List<TimeProgress> _setLoadedTimeProgressList(

View File

@ -9,6 +9,8 @@ class HomeScreen extends StatefulWidget {
static const routeName = "/home"; static const routeName = "/home";
static const title = "Time Progress Tracker"; static const title = "Time Progress Tracker";
const HomeScreen({super.key});
@override @override
State<StatefulWidget> createState() { State<StatefulWidget> createState() {
return _HomeScreenState(); return _HomeScreenState();
@ -18,9 +20,9 @@ class HomeScreen extends StatefulWidget {
class _HomeScreenState extends State<HomeScreen> { class _HomeScreenState extends State<HomeScreen> {
int _currentIndex = 0; int _currentIndex = 0;
final List<Widget> _children = [ final List<Widget> _children = [
HomeActiveProgressesTab(), const HomeActiveProgressesTab(),
HomeInactiveProgressesTab(), const HomeInactiveProgressesTab(),
HomeSettingsTab(), const HomeSettingsTab(),
]; ];
void onBottomTabTapped(int index) { void onBottomTabTapped(int index) {
@ -31,13 +33,16 @@ class _HomeScreenState extends State<HomeScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData appTheme = Theme.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(HomeScreen.title), title: const Text(HomeScreen.title),
backgroundColor: appTheme.colorScheme.primary,
), ),
body: _children[_currentIndex], body: _children[_currentIndex],
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: _currentIndex != 2 ? CreateProgressButton() : null, floatingActionButton: _currentIndex != 2 ? const CreateProgressButton() : null,
bottomNavigationBar: HomeBottomNavBar( bottomNavigationBar: HomeBottomNavBar(
currentIndex: _currentIndex, currentIndex: _currentIndex,
onTap: onBottomTabTapped, onTap: onBottomTabTapped,

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.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:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart'; import 'package:time_progress_tracker/actions/actions.dart';
@ -13,6 +12,8 @@ class ProgressCreationScreen extends StatefulWidget {
static const routeName = "/create-progress"; static const routeName = "/create-progress";
static const title = "Create Time Progress"; static const title = "Create Time Progress";
const ProgressCreationScreen({super.key});
@override @override
State<StatefulWidget> createState() { State<StatefulWidget> createState() {
return _ProgressCreationScreenState(); return _ProgressCreationScreenState();
@ -20,14 +21,15 @@ class ProgressCreationScreen extends StatefulWidget {
} }
class _ProgressCreationScreenState extends State<ProgressCreationScreen> { class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
TimeProgress timeProgressToCreate; TimeProgress? timeProgressToCreate;
bool _isProgressValid = false; bool _isProgressValid = false;
void initTimeProgress(TimeProgress timeProgress) { void initTimeProgress(TimeProgress timeProgress) {
if (timeProgressToCreate == null) if (timeProgressToCreate == null) {
setState(() { setState(() {
timeProgressToCreate = timeProgress; timeProgressToCreate = timeProgress;
}); });
}
} }
void onTimeProgressChanged( void onTimeProgressChanged(
@ -40,19 +42,25 @@ class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData appTheme = Theme.of(context);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(ProgressCreationScreen.title), title: const Text(ProgressCreationScreen.title),
backgroundColor: appTheme.colorScheme.primary,
), ),
body: Container( body: Container(
padding: EdgeInsets.all(12), padding: const EdgeInsets.all(12),
child: StoreConnector<AppState, _ViewModel>( child: StoreConnector<AppState, _ViewModel>(
onInit: loadSettingsIfUnloaded, onInit: loadSettingsIfUnloaded,
converter: (store) => _ViewModel.create(store), converter: (store) => _ViewModel.create(store),
builder: (context, _ViewModel viewModel) { builder: (context, _ViewModel viewModel) {
initTimeProgress(viewModel.defaultDurationProgress); WidgetsBinding.instance.addPostFrameCallback((_) {
initTimeProgress(viewModel.defaultDurationProgress);
});
return ProgressEditorWidget( return ProgressEditorWidget(
timeProgress: timeProgressToCreate, timeProgress:
timeProgressToCreate ?? viewModel.defaultDurationProgress,
onTimeProgressChanged: onTimeProgressChanged, onTimeProgressChanged: onTimeProgressChanged,
); );
}), }),
@ -66,23 +74,24 @@ class _ProgressCreationScreenState extends State<ProgressCreationScreen> {
converter: (store) => _ViewModel.create(store), converter: (store) => _ViewModel.create(store),
builder: (context, _ViewModel vm) => FloatingActionButton( builder: (context, _ViewModel vm) => FloatingActionButton(
heroTag: "createTimeProgressBTN", heroTag: "createTimeProgressBTN",
child: Icon(Icons.save),
onPressed: _isProgressValid onPressed: _isProgressValid
? () { ? () {
vm.onAddTimeProgress(timeProgressToCreate); vm.onAddTimeProgress(
timeProgressToCreate ?? vm.defaultDurationProgress);
Navigator.pop(context); Navigator.pop(context);
} }
: null, : null,
child: const Icon(Icons.save),
), ),
), ),
), ),
Expanded( Expanded(
child: FloatingActionButton( child: FloatingActionButton(
heroTag: "cancelTimeProgressCreationBTN", heroTag: "cancelTimeProgressCreationBTN",
child: Icon(Icons.cancel),
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
}, },
child: const Icon(Icons.cancel),
), ),
) )
], ],
@ -96,21 +105,21 @@ class _ViewModel {
final void Function(TimeProgress) onAddTimeProgress; final void Function(TimeProgress) onAddTimeProgress;
_ViewModel({ _ViewModel({
@required this.defaultDurationProgress, required this.defaultDurationProgress,
@required this.onAddTimeProgress, required this.onAddTimeProgress,
}); });
factory _ViewModel.create(Store<AppState> store) { factory _ViewModel.create(Store<AppState> store) {
AppSettings settings = appSettingsSelector(store.state); AppSettings settings = appSettingsSelector(store.state);
_onAddTimeProgress(TimeProgress tp) { onAddTimeProgress(TimeProgress tp) {
if (TimeProgress.isValid(tp)) store.dispatch(AddTimeProgressAction(tp)); if (TimeProgress.isValid(tp)) store.dispatch(AddTimeProgressAction(tp));
} }
return _ViewModel( return _ViewModel(
defaultDurationProgress: defaultDurationProgress:
TimeProgress.defaultFromDuration(settings.duration), TimeProgress.defaultFromDuration(settings.duration),
onAddTimeProgress: _onAddTimeProgress, onAddTimeProgress: onAddTimeProgress,
); );
} }
} }

View File

@ -17,6 +17,8 @@ class ProgressDetailScreen extends StatefulWidget {
static const routeName = "/progress"; static const routeName = "/progress";
static const title = "Progress View"; static const title = "Progress View";
const ProgressDetailScreen({super.key});
@override @override
State<StatefulWidget> createState() { State<StatefulWidget> createState() {
return _ProgressDetailScreenState(); return _ProgressDetailScreenState();
@ -25,7 +27,7 @@ class ProgressDetailScreen extends StatefulWidget {
class _ProgressDetailScreenState extends State<ProgressDetailScreen> { 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) { void _initEditedProgress(TimeProgress tp) {
if (_editedProgress == null) { if (_editedProgress == null) {
@ -57,31 +59,35 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
List<Widget> _renderColumnChildren( List<Widget> _renderColumnChildren(
SettingsViewModel settingsVm, TimeProgressViewModel tpVm) { SettingsViewModel settingsVm, TimeProgressViewModel tpVm) {
List<Widget> columnChildren = [ List<Widget> columnChildren = [];
Expanded( if (!_editMode) {
columnChildren.add(Expanded(
child: ProgressViewWidget( child: ProgressViewWidget(
timeProgress: _editMode ? _editedProgress : tpVm.tp, timeProgress: _editMode ? _editedProgress ?? tpVm.tp : tpVm.tp,
doneColor: settingsVm.appSettings.doneColor, doneColor: settingsVm.appSettings.doneColor,
leftColor: settingsVm.appSettings.leftColor, leftColor: settingsVm.appSettings.leftColor,
)) )));
]; } else {
if (_editMode)
columnChildren.add(Expanded( columnChildren.add(Expanded(
child: ProgressEditorWidget( child: ProgressEditorWidget(
timeProgress: _editedProgress, timeProgress: _editedProgress ?? tpVm.tp,
onTimeProgressChanged: _onEditedProgressChanged, onTimeProgressChanged: _onEditedProgressChanged,
))); )));
}
return columnChildren; return columnChildren;
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ProgressDetailScreenArguments args = final ThemeData appTheme = Theme.of(context);
ModalRoute.of(context).settings.arguments; final ProgressDetailScreenArguments args = ModalRoute.of(context)
?.settings
.arguments as ProgressDetailScreenArguments;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(ProgressDetailScreen.title), title: const Text(ProgressDetailScreen.title),
backgroundColor: appTheme.colorScheme.primary,
), ),
body: SettingsStoreConnector( body: SettingsStoreConnector(
loadedBuilder: (context, settingsVm) { loadedBuilder: (context, settingsVm) {
@ -90,10 +96,11 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
loadedBuilder: (context, tpVm) { loadedBuilder: (context, tpVm) {
_initEditedProgress(tpVm.tp); _initEditedProgress(tpVm.tp);
return Container( return Container(
margin: EdgeInsets.all(8), margin: const EdgeInsets.all(8),
child: Column( child: Column(
children: _renderColumnChildren(settingsVm, tpVm), children: _renderColumnChildren(settingsVm, tpVm),
)); ),
);
}, },
); );
}, },
@ -102,12 +109,12 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
floatingActionButton: TimeProgressStoreConnector( floatingActionButton: TimeProgressStoreConnector(
timeProgressId: args.id, timeProgressId: args.id,
loadedBuilder: (context, tpVm) { loadedBuilder: (context, tpVm) {
void _saveEditedProgress() { void saveEditedProgress() {
tpVm.updateTimeProgress(_editedProgress); tpVm.updateTimeProgress(_editedProgress ?? tpVm.tp);
_switchEditMode(false); _switchEditMode(false);
} }
void _deleteTimeProgress() { void deleteTimeProgress() {
tpVm.deleteTimeProgress(); tpVm.deleteTimeProgress();
Navigator.popUntil( Navigator.popUntil(
context, ModalRoute.withName(HomeScreen.routeName)); context, ModalRoute.withName(HomeScreen.routeName));
@ -116,12 +123,12 @@ class _ProgressDetailScreenState extends State<ProgressDetailScreen> {
return DetailScreenFloatingActionButtons( return DetailScreenFloatingActionButtons(
editMode: _editMode, editMode: _editMode,
originalProgress: tpVm.tp, originalProgress: tpVm.tp,
editedProgress: _editedProgress, editedProgress: _editedProgress ?? tpVm.tp,
isEditedProgressValid: _isEditedProgressValid, isEditedProgressValid: _isEditedProgressValid,
onEditProgress: () => _switchEditMode(true), onEditProgress: () => _switchEditMode(true),
onSaveEditedProgress: _saveEditedProgress, onSaveEditedProgress: saveEditedProgress,
onCancelEditProgress: _cancelEditMode, onCancelEditProgress: _cancelEditMode,
onDeleteProgress: _deleteTimeProgress); onDeleteProgress: deleteTimeProgress);
}, },
), ),
); );

View File

@ -45,10 +45,11 @@ List<TimeProgress> pastTimeProgressesSelector(AppState state) =>
DateTime.now().millisecondsSinceEpoch) DateTime.now().millisecondsSinceEpoch)
.toList(); .toList();
TimeProgress timeProgressByIdSelector(AppState state, String id) { TimeProgress? timeProgressByIdSelector(AppState state, String id) {
if (state.timeProgressList.length < 1) return null; if (state.timeProgressList.isEmpty) return null;
return state.timeProgressList return state.timeProgressList.firstWhere(
.firstWhere((timeProgress) => timeProgress.id == id, orElse: () => null); (timeProgress) => timeProgress.id == id,
orElse: () => TimeProgress.initialDefault());
} }
AppSettings appSettingsSelector(AppState state) { AppSettings appSettingsSelector(AppState state) {

View File

@ -5,12 +5,12 @@ class AppYesNoDialog extends StatelessWidget {
final String contentText; final String contentText;
final void Function() onYesPressed; final void Function() onYesPressed;
AppYesNoDialog({ const AppYesNoDialog({
Key key, super.key,
@required this.titleText, required this.titleText,
@required this.contentText, required this.contentText,
@required this.onYesPressed, required this.onYesPressed,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -18,15 +18,15 @@ class AppYesNoDialog extends StatelessWidget {
title: Text(titleText), title: Text(titleText),
content: Text(contentText), content: Text(contentText),
actions: <Widget>[ actions: <Widget>[
FlatButton( TextButton(
child: Text("Yes"),
onPressed: onYesPressed, onPressed: onYesPressed,
child: const Text("Yes"),
), ),
FlatButton( TextButton(
child: Text("No"),
onPressed: () { onPressed: () {
Navigator.pop(context); Navigator.pop(context);
}, },
child: const Text("No"),
) )
], ],
); );

View File

@ -7,11 +7,12 @@ class ColorPickerButton extends StatelessWidget {
final Color selectedColor; final Color selectedColor;
final void Function(Color) onColorPicked; final void Function(Color) onColorPicked;
ColorPickerButton({ const ColorPickerButton({
@required this.title, super.key,
@required this.dialogTitle, required this.title,
@required this.selectedColor, required this.dialogTitle,
@required this.onColorPicked, required this.selectedColor,
required this.onColorPicked,
}); });
@override @override
@ -34,13 +35,13 @@ class ColorPickerButton extends StatelessWidget {
}, },
); );
}, },
child: Text(title),
style: TextButton.styleFrom( style: TextButton.styleFrom(
primary: useBrightBackground(selectedColor) foregroundColor: useBrightBackground(selectedColor)
? appTheme.primaryTextTheme.button.color ? appTheme.primaryTextTheme.labelLarge?.color
: appTheme.textTheme.button.color, : appTheme.textTheme.labelLarge?.color,
backgroundColor: selectedColor, backgroundColor: selectedColor,
), ),
child: Text(title),
); );
} }
} }

View File

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

View File

@ -3,13 +3,14 @@ import 'package:flutter/material.dart';
class DatePickerBtn extends StatelessWidget { class DatePickerBtn extends StatelessWidget {
final String leadingString; final String leadingString;
final DateTime pickedDate; final DateTime pickedDate;
final void Function(DateTime) onDatePicked; final void Function(DateTime?) onDatePicked;
DatePickerBtn({ const DatePickerBtn({
@required this.leadingString, super.key,
@required this.pickedDate, required this.leadingString,
@required this.onDatePicked, required this.pickedDate,
}) : super(); required this.onDatePicked,
});
void _onButtonPressed(BuildContext context) async { void _onButtonPressed(BuildContext context) async {
onDatePicked(await showDatePicker( onDatePicked(await showDatePicker(
@ -25,12 +26,12 @@ class DatePickerBtn extends StatelessWidget {
ThemeData appTheme = Theme.of(context); ThemeData appTheme = Theme.of(context);
return TextButton( return TextButton(
onPressed: () => _onButtonPressed(context), onPressed: () => _onButtonPressed(context),
style: TextButton.styleFrom(
foregroundColor: appTheme.primaryTextTheme.labelLarge?.color,
backgroundColor: appTheme.colorScheme.secondary,
),
child: Text( child: Text(
"$leadingString ${pickedDate.toLocal().toString().split(" ")[0]}"), "$leadingString ${pickedDate.toLocal().toString().split(" ")[0]}"),
style: TextButton.styleFrom(
primary: appTheme.primaryTextTheme.button.color,
backgroundColor: appTheme.accentColor,
),
); );
} }
} }

View File

@ -5,9 +5,10 @@ class SelectDurationBtn extends StatelessWidget {
final Duration duration; final Duration duration;
final void Function(Duration) updateDuration; final void Function(Duration) updateDuration;
SelectDurationBtn({ const SelectDurationBtn({
@required this.duration, super.key,
@required this.updateDuration, required this.duration,
required this.updateDuration,
}); });
void _onPickerConfirm(Picker picker, List<int> values) { void _onPickerConfirm(Picker picker, List<int> values) {
@ -25,7 +26,7 @@ class SelectDurationBtn extends StatelessWidget {
]), ]),
hideHeader: false, hideHeader: false,
title: const Text("Default Duration"), title: const Text("Default Duration"),
selectedTextStyle: TextStyle(color: appTheme.accentColor), selectedTextStyle: TextStyle(color: appTheme.colorScheme.secondary),
onConfirm: _onPickerConfirm) onConfirm: _onPickerConfirm)
.showModal(context); .showModal(context);
@ -38,10 +39,10 @@ class SelectDurationBtn extends StatelessWidget {
int days = duration.inDays - (365 * years) - (30 * months); int days = duration.inDays - (365 * years) - (30 * months);
return TextButton( return TextButton(
onPressed: () => _onButtonPressed(context, appTheme), onPressed: () => _onButtonPressed(context, appTheme),
child: Text("$years Years $months Months $days Days"),
style: TextButton.styleFrom( style: TextButton.styleFrom(
primary: appTheme.primaryTextTheme.button.color, foregroundColor: appTheme.primaryTextTheme.labelLarge?.color,
backgroundColor: appTheme.accentColor, backgroundColor: appTheme.colorScheme.secondary,
)); ),
child: Text("$years Years $months Months $days Days"));
} }
} }

View File

@ -10,25 +10,26 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
onCancelEditProgress, onCancelEditProgress,
onDeleteProgress; onDeleteProgress;
DetailScreenFloatingActionButtons({ const DetailScreenFloatingActionButtons({
@required this.editMode, super.key,
@required this.originalProgress, required this.editMode,
@required this.editedProgress, required this.originalProgress,
@required this.isEditedProgressValid, required this.editedProgress,
@required this.onEditProgress, required this.isEditedProgressValid,
@required this.onSaveEditedProgress, required this.onEditProgress,
@required this.onCancelEditProgress, required this.onSaveEditedProgress,
@required this.onDeleteProgress, required this.onCancelEditProgress,
required this.onDeleteProgress,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData appTheme = Theme.of(context); final ThemeData appTheme = Theme.of(context);
void _onCancelEditTimeProgressBTN() { void onCancelEditTimeProgressBTN() {
if (originalProgress == editedProgress) if (originalProgress == editedProgress) {
onCancelEditProgress(); onCancelEditProgress();
else { } else {
showDialog( showDialog(
context: context, context: context,
builder: (_) => AppYesNoDialog( builder: (_) => AppYesNoDialog(
@ -44,7 +45,7 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
} }
} }
void _onDeleteTimeProgressBTN() { void onDeleteTimeProgressBTN() {
showDialog( showDialog(
context: context, context: context,
builder: (_) => AppYesNoDialog( builder: (_) => AppYesNoDialog(
@ -61,13 +62,13 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
child: FloatingActionButton( child: FloatingActionButton(
heroTag: heroTag:
editMode ? "saveEditedTimeProgressBTN" : "editTimeProgressBTN", editMode ? "saveEditedTimeProgressBTN" : "editTimeProgressBTN",
child: editMode ? Icon(Icons.save) : Icon(Icons.edit), backgroundColor: editMode ? Colors.green : appTheme.colorScheme.secondary,
backgroundColor: editMode ? Colors.green : appTheme.accentColor,
onPressed: editMode onPressed: editMode
? isEditedProgressValid ? isEditedProgressValid
? onSaveEditedProgress ? onSaveEditedProgress
: null : null
: onEditProgress, : onEditProgress,
child: editMode ? const Icon(Icons.save) : const Icon(Icons.edit),
), ),
), ),
Expanded( Expanded(
@ -75,11 +76,11 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
heroTag: editMode heroTag: editMode
? "cancelEditTimeProgressBTN" ? "cancelEditTimeProgressBTN"
: "deleteTimeProgressBTN", : "deleteTimeProgressBTN",
child: editMode ? Icon(Icons.cancel) : Icon(Icons.delete),
backgroundColor: Colors.red, backgroundColor: Colors.red,
onPressed: editMode onPressed: editMode
? _onCancelEditTimeProgressBTN ? onCancelEditTimeProgressBTN
: _onDeleteTimeProgressBTN, : onDeleteTimeProgressBTN,
child: editMode ? const Icon(Icons.cancel) : const Icon(Icons.delete),
), ),
), ),
], ],

View File

@ -2,13 +2,13 @@ import 'package:flutter/material.dart';
class HomeBottomNavBar extends StatelessWidget { class HomeBottomNavBar extends StatelessWidget {
final int currentIndex; final int currentIndex;
final Function onTap; final void Function(int)? onTap;
HomeBottomNavBar({ const HomeBottomNavBar({
Key key, super.key,
@required this.currentIndex, required this.currentIndex,
@required this.onTap, required this.onTap,
}) : super(key: key); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -19,21 +19,21 @@ class HomeBottomNavBar extends StatelessWidget {
currentIndex: currentIndex, currentIndex: currentIndex,
items: [ items: [
BottomNavigationBarItem( BottomNavigationBarItem(
icon: new Icon( icon: Icon(
Icons.alarm, Icons.alarm,
color: appTheme.primaryColor, color: appTheme.primaryColor,
), ),
label: "Active Progresses", label: "Active Progresses",
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: new Icon( icon: Icon(
Icons.alarm_off, Icons.alarm_off,
color: appTheme.primaryColor, color: appTheme.primaryColor,
), ),
label: "Inactive Progresses", label: "Inactive Progresses",
), ),
BottomNavigationBarItem( BottomNavigationBarItem(
icon: new Icon( icon: Icon(
Icons.settings, Icons.settings,
color: appTheme.primaryColor, color: appTheme.primaryColor,
), ),

View File

@ -6,6 +6,8 @@ import 'package:time_progress_tracker/widgets/store_connectors/settings_store_co
import 'package:time_progress_tracker/widgets/store_connectors/time_progress_list_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 {
const HomeActiveProgressesTab({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SettingsStoreConnector( return SettingsStoreConnector(
@ -14,14 +16,15 @@ class HomeActiveProgressesTab extends StatelessWidget {
loadedBuilder: (context, tpListVm) { loadedBuilder: (context, tpListVm) {
List<TimeProgress> activeTpList = List<TimeProgress> activeTpList =
selectActiveProgresses(tpListVm.tpList); selectActiveProgresses(tpListVm.tpList);
if (activeTpList.length < 1) if (activeTpList.isEmpty) {
return Container( return Container(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Center( child: const Center(
child: Text( child: Text(
"You don't have any active time progress, that are tracked."), "You don't have any active time progress, that are tracked."),
), ),
); );
}
return ProgressListView( return ProgressListView(
timeProgressList: activeTpList, timeProgressList: activeTpList,

View File

@ -6,6 +6,8 @@ import 'package:time_progress_tracker/widgets/store_connectors/settings_store_co
import 'package:time_progress_tracker/widgets/store_connectors/time_progress_list_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 {
const HomeInactiveProgressesTab({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SettingsStoreConnector( return SettingsStoreConnector(
@ -14,14 +16,15 @@ class HomeInactiveProgressesTab extends StatelessWidget {
loadedBuilder: (context, tpListVm) { loadedBuilder: (context, tpListVm) {
List<TimeProgress> inactiveTpList = List<TimeProgress> inactiveTpList =
selectInactiveProgresses(tpListVm.tpList); selectInactiveProgresses(tpListVm.tpList);
if (inactiveTpList.length < 1) if (inactiveTpList.isEmpty) {
return Container( return Container(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Center( child: const Center(
child: Text( child: Text(
"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 ProgressListView( return ProgressListView(
timeProgressList: inactiveTpList, timeProgressList: inactiveTpList,

View File

@ -5,12 +5,14 @@ import 'package:time_progress_tracker/widgets/home/tabs/settings/duration_settin
import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart'; import 'package:time_progress_tracker/widgets/store_connectors/settings_store_connector.dart';
class HomeSettingsTab extends StatelessWidget { class HomeSettingsTab extends StatelessWidget {
const HomeSettingsTab({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SettingsStoreConnector( return SettingsStoreConnector(
loadedBuilder: (context, settingsVm) { loadedBuilder: (context, settingsVm) {
return Container( return Container(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Center( child: Center(
child: Column( child: Column(
children: [ children: [
@ -28,7 +30,7 @@ class HomeSettingsTab extends StatelessWidget {
updateDuration: settingsVm.updateDuration, updateDuration: settingsVm.updateDuration,
), ),
), ),
Spacer(), const Spacer(),
Expanded( Expanded(
child: TextButton( child: TextButton(
onPressed: () { onPressed: () {
@ -39,7 +41,7 @@ class HomeSettingsTab extends StatelessWidget {
applicationLegalese: applicationLegalese:
'\u00a9Andreas Fahrecker 2020-2021'); '\u00a9Andreas Fahrecker 2020-2021');
}, },
child: Text("About"), child: const Text("About"),
), ),
), ),
], ],

View File

@ -5,11 +5,12 @@ class ColorSettingsWidget extends StatelessWidget {
final Color doneColor, leftColor; final Color doneColor, leftColor;
final void Function(Color) updateDoneColor, updateLeftColor; final void Function(Color) updateDoneColor, updateLeftColor;
ColorSettingsWidget({ const ColorSettingsWidget({
@required this.doneColor, super.key,
@required this.leftColor, required this.doneColor,
@required this.updateDoneColor, required this.leftColor,
@required this.updateLeftColor, required this.updateDoneColor,
required this.updateLeftColor,
}); });
@override @override
@ -21,14 +22,14 @@ class ColorSettingsWidget extends StatelessWidget {
Expanded( Expanded(
child: Text( child: Text(
"Color Settings", "Color Settings",
style: appTheme.textTheme.headline6, style: appTheme.textTheme.titleLarge,
), ),
), ),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Padding( child: Padding(
padding: EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: ColorPickerButton( child: ColorPickerButton(
title: "Done Color", title: "Done Color",
dialogTitle: "Select Done Color", dialogTitle: "Select Done Color",
@ -39,7 +40,7 @@ class ColorSettingsWidget extends StatelessWidget {
), ),
Expanded( Expanded(
child: Padding( child: Padding(
padding: EdgeInsets.only(left: 5), padding: const EdgeInsets.only(left: 5),
child: ColorPickerButton( child: ColorPickerButton(
title: "Left Color", title: "Left Color",
dialogTitle: "Select Left Color", dialogTitle: "Select Left Color",

View File

@ -5,24 +5,25 @@ class DurationSettingsWidget extends StatelessWidget {
final Duration duration; final Duration duration;
final void Function(Duration) updateDuration; final void Function(Duration) updateDuration;
DurationSettingsWidget({ const DurationSettingsWidget({
@required this.duration, super.key,
@required this.updateDuration, required this.duration,
required this.updateDuration,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
ThemeData appTheme = Theme.of(context); ThemeData appTheme = Theme.of(context);
int years = duration.inDays ~/ 365; //int years = duration.inDays ~/ 365;
int months = (duration.inDays - (365 * years)) ~/ 30; //int months = (duration.inDays - (365 * years)) ~/ 30;
int days = duration.inDays - (365 * years) - (30 * months); //int days = duration.inDays - (365 * years) - (30 * months);
return Column( return Column(
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
"Duration Settings", "Duration Settings",
style: appTheme.textTheme.headline6, style: appTheme.textTheme.titleLarge,
), ),
), ),
Row( Row(

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.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/buttons/date_picker_btn.dart'; import 'package:time_progress_tracker/widgets/buttons/date_picker_btn.dart';
@ -6,9 +7,10 @@ class ProgressEditorWidget extends StatefulWidget {
final TimeProgress timeProgress; final TimeProgress timeProgress;
final Function(TimeProgress, bool) onTimeProgressChanged; final Function(TimeProgress, bool) onTimeProgressChanged;
ProgressEditorWidget({ const ProgressEditorWidget({
@required this.timeProgress, super.key,
@required this.onTimeProgressChanged, required this.timeProgress,
required this.onTimeProgressChanged,
}); });
@override @override
@ -31,7 +33,10 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
}); });
} }
void _onStartDateChanged(DateTime newStartDate) { void _onStartDateChanged(DateTime? newStartDate) {
if (newStartDate == null) {
return;
}
TimeProgress newProgress = TimeProgress newProgress =
widget.timeProgress.copyWith(startTime: newStartDate); widget.timeProgress.copyWith(startTime: newStartDate);
widget.onTimeProgressChanged( widget.onTimeProgressChanged(
@ -42,7 +47,10 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
}); });
} }
void _onEndDateChanged(DateTime newEndDate) { void _onEndDateChanged(DateTime? newEndDate) {
if (newEndDate == null) {
return;
}
TimeProgress newProgress = TimeProgress newProgress =
widget.timeProgress.copyWith(endTime: newEndDate); widget.timeProgress.copyWith(endTime: newEndDate);
widget.onTimeProgressChanged( widget.onTimeProgressChanged(
@ -62,12 +70,15 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double heightFactor = (!_validDate) ? 0.3 : 0.5;
List<Widget> columnChildren = [ List<Widget> columnChildren = [
Expanded( SizedBox(
height: MediaQuery.of(context).size.height * heightFactor,
child: TextField( child: TextField(
controller: _nameTextController, controller: _nameTextController,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
labelText: "Progress Name", labelText: "Progress Name",
errorText: _validName errorText: _validName
? null ? null
@ -75,12 +86,13 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
), ),
), ),
), ),
Expanded( SizedBox(
height: MediaQuery.of(context).size.height * heightFactor,
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: Padding( child: Padding(
padding: EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: DatePickerBtn( child: DatePickerBtn(
leadingString: "Start Date:", leadingString: "Start Date:",
pickedDate: widget.timeProgress.startTime, pickedDate: widget.timeProgress.startTime,
@ -90,7 +102,7 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
), ),
Expanded( Expanded(
child: Padding( child: Padding(
padding: EdgeInsets.only(left: 5), padding: const EdgeInsets.only(left: 5),
child: DatePickerBtn( child: DatePickerBtn(
leadingString: "End Date:", leadingString: "End Date:",
pickedDate: widget.timeProgress.endTime, pickedDate: widget.timeProgress.endTime,
@ -103,10 +115,11 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
) )
]; ];
if (!_validDate) if (!_validDate) {
columnChildren.add( columnChildren.add(
Expanded( SizedBox(
child: Center( height: MediaQuery.of(context).size.height * heightFactor,
child: const Center(
child: Text( child: Text(
"Invalid Dates. The Start Date has to be before the End Date", "Invalid Dates. The Start Date has to be before the End Date",
style: TextStyle(color: Colors.red), style: TextStyle(color: Colors.red),
@ -114,8 +127,10 @@ class _ProgressEditorWidgetState extends State<ProgressEditorWidget> {
), ),
), ),
); );
}
return Container( return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column( child: Column(
children: columnChildren, children: columnChildren,
), ),

View File

@ -18,17 +18,19 @@ class ProgressListTile extends StatelessWidget {
final TimeProgress timeProgress; final TimeProgress timeProgress;
final Color doneColor, leftColor; final Color doneColor, leftColor;
ProgressListTile({ const ProgressListTile({super.key,
@required this.timeProgress, required this.timeProgress,
@required this.doneColor, required this.doneColor,
@required this.leftColor, required this.leftColor,
}); });
Widget _renderSubtitle(BuildContext context) { Widget _renderSubtitle(BuildContext context) {
if (!timeProgress.hasStarted()) if (!timeProgress.hasStarted()) {
return Text(ProgressListTileStrings.startsInDaysString(timeProgress)); return Text(ProgressListTileStrings.startsInDaysString(timeProgress));
if (timeProgress.hasEnded()) }
if (timeProgress.hasEnded()) {
return Text(ProgressListTileStrings.endedDaysAgoString(timeProgress)); return Text(ProgressListTileStrings.endedDaysAgoString(timeProgress));
}
return LinearPercentIndicator( return LinearPercentIndicator(
center: Text(ProgressListTileStrings.percentString(timeProgress)), center: Text(ProgressListTileStrings.percentString(timeProgress)),
percent: timeProgress.percentDone(), percent: timeProgress.percentDone(),
@ -40,14 +42,14 @@ class ProgressListTile extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
void _onTileTap() => void onTileTap() =>
Navigator.pushNamed(context, ProgressDetailScreen.routeName, Navigator.pushNamed(context, ProgressDetailScreen.routeName,
arguments: ProgressDetailScreenArguments(timeProgress.id)); arguments: ProgressDetailScreenArguments(timeProgress.id));
return ListTile( return ListTile(
title: Text(timeProgress.name), title: Text(timeProgress.name),
subtitle: _renderSubtitle(context), subtitle: _renderSubtitle(context),
onTap: _onTileTap, onTap: onTileTap,
); );
} }
} }

View File

@ -6,10 +6,11 @@ class ProgressListView extends StatelessWidget {
final List<TimeProgress> timeProgressList; final List<TimeProgress> timeProgressList;
final Color doneColor, leftColor; final Color doneColor, leftColor;
ProgressListView({ const ProgressListView({
@required this.timeProgressList, super.key,
@required this.doneColor, required this.timeProgressList,
@required this.leftColor, required this.doneColor,
required this.leftColor,
}); });
List<Widget> _renderListViewChildren() { List<Widget> _renderListViewChildren() {
@ -27,7 +28,7 @@ class ProgressListView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return ListView(
padding: EdgeInsets.all(8), padding: const EdgeInsets.all(8),
children: _renderListViewChildren(), children: _renderListViewChildren(),
); );
} }

View File

@ -8,31 +8,37 @@ class ProgressViewWidget extends StatelessWidget {
final Color doneColor; final Color doneColor;
final Color leftColor; final Color leftColor;
ProgressViewWidget({ const ProgressViewWidget({
@required this.timeProgress, super.key,
@required this.doneColor, required this.timeProgress,
@required this.leftColor, required this.doneColor,
required this.leftColor,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column( child: Column(
children: [ children: [
Expanded( SizedBox(
height: MediaQuery.of(context).size.height *
0.3, // adjust the value as needed
child: FittedBox( child: FittedBox(
fit: BoxFit.fitWidth, fit: BoxFit.fitWidth,
child: Text( child: Text(
timeProgress.name, timeProgress.name,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.black87, color: Colors.black87,
), ),
), ),
), ),
), ),
Expanded( SizedBox(
height: MediaQuery.of(context).size.height *
0.3, // adjust the value as needed
child: CircularPercentIndicator( child: CircularPercentIndicator(
radius: 100, radius: 100,
lineWidth: 10, lineWidth: 10,
@ -42,14 +48,16 @@ class ProgressViewWidget extends StatelessWidget {
center: Text("${(timeProgress.percentDone() * 100).floor()} %"), center: Text("${(timeProgress.percentDone() * 100).floor()} %"),
), ),
), ),
Expanded( SizedBox(
height: MediaQuery.of(context).size.height *
0.3, // adjust the value as needed
child: LinearPercentIndicator( child: LinearPercentIndicator(
padding: EdgeInsets.symmetric(horizontal: 15), padding: const EdgeInsets.symmetric(horizontal: 15),
percent: timeProgress.percentDone(), percent: timeProgress.percentDone(),
leading: Text("${timeProgress.daysBehind()} Days"), leading: Text("${timeProgress.daysBehind()} Days"),
center: Text( center: Text(
"${(timeProgress.percentDone() * 100).floor()} %", "${(timeProgress.percentDone() * 100).floor()} %",
style: TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
), ),
trailing: Text("${timeProgress.daysLeft()} Days"), trailing: Text("${timeProgress.daysLeft()} Days"),
progressColor: doneColor, progressColor: doneColor,

View File

@ -8,8 +8,9 @@ import 'package:time_progress_tracker/models/app_state.dart';
class SettingsStoreConnector extends StatelessWidget { class SettingsStoreConnector extends StatelessWidget {
final Widget Function(BuildContext, SettingsViewModel) loadedBuilder; final Widget Function(BuildContext, SettingsViewModel) loadedBuilder;
SettingsStoreConnector({ const SettingsStoreConnector({
@required this.loadedBuilder, super.key,
required this.loadedBuilder,
}); });
@override @override
@ -18,10 +19,11 @@ class SettingsStoreConnector extends StatelessWidget {
onInit: loadSettingsIfUnloaded, onInit: loadSettingsIfUnloaded,
converter: (store) => SettingsViewModel._create(store), converter: (store) => SettingsViewModel._create(store),
builder: (context, SettingsViewModel vm) { builder: (context, SettingsViewModel vm) {
if (!vm.hasSettingsLoaded) if (!vm.hasSettingsLoaded) {
return Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
}
return loadedBuilder(context, vm); return loadedBuilder(context, vm);
}, },
); );
@ -44,17 +46,17 @@ class SettingsViewModel {
); );
factory SettingsViewModel._create(Store<AppState> store) { factory SettingsViewModel._create(Store<AppState> store) {
AppSettings _appSettings = store.state.appSettings; AppSettings appSettings = store.state.appSettings;
void _updateDoneColor(Color dC) => store.dispatch( void updateDoneColor(Color dC) => store.dispatch(
UpdateAppSettingsActions(_appSettings.copyWith(doneColor: dC))); UpdateAppSettingsActions(appSettings.copyWith(doneColor: dC)));
void _updateLeftColor(Color lC) => store.dispatch( void updateLeftColor(Color lC) => store.dispatch(
UpdateAppSettingsActions(_appSettings.copyWith(leftColor: lC))); UpdateAppSettingsActions(appSettings.copyWith(leftColor: lC)));
void _updateDuration(Duration d) => store void updateDuration(Duration d) => store
.dispatch(UpdateAppSettingsActions(_appSettings.copyWith(duration: d))); .dispatch(UpdateAppSettingsActions(appSettings.copyWith(duration: d)));
return SettingsViewModel(_appSettings, store.state.hasSettingsLoaded, return SettingsViewModel(appSettings, store.state.hasSettingsLoaded,
_updateDoneColor, _updateLeftColor, _updateDuration); updateDoneColor, updateLeftColor, updateDuration);
} }
} }

View File

@ -8,8 +8,9 @@ import 'package:time_progress_tracker/models/time_progress.dart';
class TimeProgressListStoreConnector extends StatelessWidget { class TimeProgressListStoreConnector extends StatelessWidget {
final Widget Function(BuildContext, TimeProgressListViewModel) loadedBuilder; final Widget Function(BuildContext, TimeProgressListViewModel) loadedBuilder;
TimeProgressListStoreConnector({ const TimeProgressListStoreConnector({
@required this.loadedBuilder, super.key,
required this.loadedBuilder,
}); });
@override @override
@ -18,10 +19,11 @@ class TimeProgressListStoreConnector extends StatelessWidget {
onInit: loadTimeProgressListIfUnloaded, onInit: loadTimeProgressListIfUnloaded,
converter: (store) => TimeProgressListViewModel._create(store), converter: (store) => TimeProgressListViewModel._create(store),
builder: (context, TimeProgressListViewModel vm) { builder: (context, TimeProgressListViewModel vm) {
if (!vm.hasTpListLoaded) if (!vm.hasTpListLoaded) {
return Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
}
return loadedBuilder(context, vm); return loadedBuilder(context, vm);
}, },
); );

View File

@ -11,9 +11,10 @@ class TimeProgressStoreConnector extends StatelessWidget {
final String timeProgressId; final String timeProgressId;
final Widget Function(BuildContext, TimeProgressViewModel) loadedBuilder; final Widget Function(BuildContext, TimeProgressViewModel) loadedBuilder;
TimeProgressStoreConnector({ const TimeProgressStoreConnector({
@required this.timeProgressId, super.key,
@required this.loadedBuilder, required this.timeProgressId,
required this.loadedBuilder,
}); });
@override @override
@ -23,14 +24,11 @@ class TimeProgressStoreConnector extends StatelessWidget {
converter: (store) => converter: (store) =>
TimeProgressViewModel._create(store, timeProgressId), TimeProgressViewModel._create(store, timeProgressId),
builder: (context, TimeProgressViewModel vm) { builder: (context, TimeProgressViewModel vm) {
if (!vm.hasTpListLoaded) if (!vm.hasTpListLoaded) {
return Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
if (vm.tp == null) }
return Center(
child: Text("Error Invalid Time Progress"),
);
return loadedBuilder(context, vm); return loadedBuilder(context, vm);
}, },
); );
@ -52,15 +50,15 @@ class TimeProgressViewModel {
); );
factory TimeProgressViewModel._create(Store<AppState> store, String id) { factory TimeProgressViewModel._create(Store<AppState> store, String id) {
void _updateTimeProgress(TimeProgress tp) => void updateTimeProgress(TimeProgress tp) =>
store.dispatch(UpdateTimeProgressAction(id, tp)); store.dispatch(UpdateTimeProgressAction(id, tp));
void _deleteTimeProgress() => store.dispatch(DeleteTimeProgressAction(id)); void deleteTimeProgress() => store.dispatch(DeleteTimeProgressAction(id));
return TimeProgressViewModel( return TimeProgressViewModel(
selectProgressById(store.state.timeProgressList, id), selectProgressById(store.state.timeProgressList, id),
store.state.hasProgressesLoaded, store.state.hasProgressesLoaded,
_updateTimeProgress, updateTimeProgress,
_deleteTimeProgress, deleteTimeProgress,
); );
} }
} }

View File

@ -5,100 +5,122 @@ 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: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "2.1.2"
file: file:
dependency: transitive dependency: transitive
description: description:
name: file name: file
url: "https://pub.dartlang.org" sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.0" version: "7.0.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -108,32 +130,43 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_colorpicker name: flutter_colorpicker
url: "https://pub.dartlang.org" sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b"
url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.5" 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: flutter_picker:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: HEAD ref: HEAD
resolved-ref: e95d121f54faba889fbf8a850c86dd5cf4aa5c5a resolved-ref: "4a88a436b64d043d2e5412d64035a9bbe7f9d45d"
url: "git://github.com/yangyxd/flutter_picker.git" url: "https://github.com/yangyxd/flutter_picker.git"
source: git source: git
version: "1.1.5" 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
@ -148,142 +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"
js: js:
dependency: transitive dependency: transitive
description: description:
name: js name: js
url: "https://pub.dartlang.org" sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.3" version: "0.7.1"
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"
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: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.2.1"
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: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.1.2"
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: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.2.1"
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: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.0" version: "3.1.4"
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: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.1.8"
process: pointycastle:
dependency: transitive dependency: transitive
description: description:
name: process name: pointycastle
url: "https://pub.dartlang.org" sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.dev"
source: hosted source: hosted
version: "4.1.0" version: "3.7.4"
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: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.2.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
url: "https://pub.dev"
source: hosted
version: "2.3.5"
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: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.3.2"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
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: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.3.2"
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: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.3.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: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.3.2"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -293,86 +402,114 @@ 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: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.0" version: "1.3.2"
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"
web:
dependency: transitive
description:
name: web
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
url: "https://pub.dartlang.org" sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "5.3.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
name: xdg_directories name: xdg_directories
url: "https://pub.dartlang.org" sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.0" version: "1.0.4"
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-259.9.beta <3.0.0" dart: ">=3.3.1 <4.0.0"
flutter: ">=1.20.0" flutter: ">=3.19.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,21 +12,28 @@ 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.19+19 version: 0.0.21+21
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_colorpicker:
flutter_redux: flutter_redux:
flutter_picker: flutter_picker:
git: git://github.com/yangyxd/flutter_picker.git git:
url: https://github.com/yangyxd/flutter_picker.git
meta: meta:
percent_indicator: percent_indicator:
redux: redux:
@ -35,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
@ -59,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

@ -3,8 +3,9 @@ import 'package:flutter/material.dart';
class MaterialTesterWidget extends StatelessWidget { class MaterialTesterWidget extends StatelessWidget {
final Widget widget; final Widget widget;
MaterialTesterWidget({ const MaterialTesterWidget({
@required this.widget, super.key,
required this.widget,
}); });
@override @override

View File

@ -13,34 +13,34 @@ 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_tile.dart';
import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_view.dart'; import 'package:time_progress_tracker/widgets/progress_list_view/progress_list_view.dart';
import 'MaterialTesterWidget.dart'; import 'material_tester_widget.dart';
void main() { void main() {
final AppSettings _defaultAppSettings = AppSettings.defaults(); final AppSettings defaultAppSettings = AppSettings.defaults();
final int _thisYear = DateTime.now().year; final int thisYear = DateTime.now().year;
final TimeProgress _activeProgress = TimeProgress( final TimeProgress activeProgress = TimeProgress(
"TestProgress", DateTime(_thisYear - 2), DateTime(_thisYear + 2)); "TestProgress", DateTime(thisYear - 2), DateTime(thisYear + 2));
void _findStringOnce(String str) => expect(find.text(str), findsOneWidget); void findStringOnce(String str) => expect(find.text(str), findsOneWidget);
testWidgets("Progress List Tile with currently active progress works", testWidgets("Progress List Tile with currently active progress works",
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(MaterialTesterWidget( await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile( widget: ProgressListTile(
timeProgress: _activeProgress, timeProgress: activeProgress,
doneColor: _defaultAppSettings.doneColor, doneColor: defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor, leftColor: defaultAppSettings.leftColor,
), ),
)); ));
_findStringOnce(_activeProgress.name); findStringOnce(activeProgress.name);
_findStringOnce(ProgressListTileStrings.percentString(_activeProgress)); findStringOnce(ProgressListTileStrings.percentString(activeProgress));
WidgetPredicate linearPercentPredicate = (Widget widget) => linearPercentPredicate(Widget widget) =>
widget is LinearPercentIndicator && widget is LinearPercentIndicator &&
widget.percent == _activeProgress.percentDone() && widget.percent == activeProgress.percentDone() &&
widget.progressColor == _defaultAppSettings.doneColor && widget.progressColor == defaultAppSettings.doneColor &&
widget.backgroundColor == _defaultAppSettings.leftColor; widget.backgroundColor == defaultAppSettings.leftColor;
expect(find.byWidgetPredicate(linearPercentPredicate), findsOneWidget); expect(find.byWidgetPredicate(linearPercentPredicate), findsOneWidget);
}); });
@ -48,40 +48,40 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
TimeProgress futureProgress = TimeProgress( TimeProgress futureProgress = TimeProgress(
"Test Progress", "Test Progress",
DateTime(_thisYear + 1), DateTime(thisYear + 1),
DateTime(_thisYear + 2), DateTime(thisYear + 2),
); );
await tester.pumpWidget(MaterialTesterWidget( await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile( widget: ProgressListTile(
timeProgress: futureProgress, timeProgress: futureProgress,
doneColor: _defaultAppSettings.doneColor, doneColor: defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor, leftColor: defaultAppSettings.leftColor,
), ),
)); ));
_findStringOnce(futureProgress.name); findStringOnce(futureProgress.name);
_findStringOnce(ProgressListTileStrings.startsInDaysString(futureProgress)); findStringOnce(ProgressListTileStrings.startsInDaysString(futureProgress));
}); });
testWidgets("Progress List Tile with past progress works", testWidgets("Progress List Tile with past progress works",
(WidgetTester tester) async { (WidgetTester tester) async {
TimeProgress pastProgress = TimeProgress( TimeProgress pastProgress = TimeProgress(
"Test Progress", "Test Progress",
DateTime(_thisYear - 2), DateTime(thisYear - 2),
DateTime(_thisYear - 1), DateTime(thisYear - 1),
); );
await tester.pumpWidget(MaterialTesterWidget( await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile( widget: ProgressListTile(
timeProgress: pastProgress, timeProgress: pastProgress,
doneColor: _defaultAppSettings.doneColor, doneColor: defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor, leftColor: defaultAppSettings.leftColor,
), ),
)); ));
_findStringOnce(pastProgress.name); findStringOnce(pastProgress.name);
_findStringOnce(ProgressListTileStrings.endedDaysAgoString(pastProgress)); findStringOnce(ProgressListTileStrings.endedDaysAgoString(pastProgress));
}); });
WidgetPredicate getProgressListTilePredicate( WidgetPredicate getProgressListTilePredicate(
@ -96,35 +96,37 @@ void main() {
(WidgetTester tester) async { (WidgetTester tester) async {
await tester.pumpWidget(MaterialTesterWidget( await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListView( widget: ProgressListView(
timeProgressList: [_activeProgress], timeProgressList: [activeProgress],
doneColor: _defaultAppSettings.doneColor, doneColor: defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor, leftColor: defaultAppSettings.leftColor,
), ),
)); ));
_findStringOnce(_activeProgress.name); findStringOnce(activeProgress.name);
expect( expect(
find.byWidgetPredicate( find.byWidgetPredicate(
getProgressListTilePredicate(_activeProgress, _defaultAppSettings)), getProgressListTilePredicate(activeProgress, defaultAppSettings)),
findsOneWidget); findsOneWidget);
}); });
testWidgets("Progress List View displays file tiles", testWidgets("Progress List View displays file tiles",
(WidgetTester tester) async { (WidgetTester tester) async {
List<TimeProgress> tpList = []; List<TimeProgress> tpList = [];
for (int i = 0; i < 5; i++) tpList.add(_activeProgress); for (int i = 0; i < 5; i++) {
tpList.add(activeProgress);
}
await tester.pumpWidget(MaterialTesterWidget( await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListView( widget: ProgressListView(
timeProgressList: tpList, timeProgressList: tpList,
doneColor: _defaultAppSettings.doneColor, doneColor: defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor, leftColor: defaultAppSettings.leftColor,
), ),
)); ));
expect(find.text(_activeProgress.name), findsNWidgets(5)); expect(find.text(activeProgress.name), findsNWidgets(5));
expect( expect(
find.byWidgetPredicate( find.byWidgetPredicate(
getProgressListTilePredicate(_activeProgress, _defaultAppSettings)), getProgressListTilePredicate(activeProgress, defaultAppSettings)),
findsNWidgets(5)); findsNWidgets(5));
}); });
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -8,10 +8,13 @@
The path provided below has to start and end with a slash "/" in order for The path provided below has to start and end with a slash "/" in order for
it to work correctly. it to work correctly.
Fore more details: For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
--> -->
<base href="/"> <base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
@ -28,18 +31,29 @@
<title>time_progress_tracker</title> <title>time_progress_tracker</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<script>
// The value below is injected by flutter build, do not touch.
const serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head> </head>
<body> <body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script> <script>
if ('serviceWorker' in navigator) { window.addEventListener('load', function(ev) {
window.addEventListener('flutter-first-frame', function () { // Download main.dart.js
navigator.serviceWorker.register('flutter_service_worker.js'); _flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
}); });
} });
</script> </script>
<script src="main.dart.js" type="application/javascript"></script>
</body> </body>
</html> </html>

View File

@ -18,6 +18,18 @@
"src": "icons/Icon-512.png", "src": "icons/Icon-512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
} }
] ]
} }