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
*.class
*.lock
*.log
*.pyc
*.swp
@ -9,6 +8,7 @@
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
@ -16,102 +16,28 @@
*.iws
.idea/
# Visual Studio Code related
.classpath
.project
.settings/
.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
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-cache/
.pub/
build/
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds
/build/
# Android 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
# Symbolication related
app.*.symbols
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

View File

@ -4,7 +4,42 @@
# This file should be version controlled and should not be manually edited.
version:
revision: fba99f6cf9a14512e461e3122c8ddfaa25394e89
channel: stable
revision: "ba393198430278b6595976de84fe170f553cc728"
channel: "stable"
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.
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.

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.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
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 localPropertiesFile = rootProject.file('local.properties')
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')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@ -21,10 +22,6 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@ -32,21 +29,30 @@ if (keystorePropertiesFile.exists()) {
}
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 {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.fahrecker.time_progress_calculator"
minSdkVersion 16
targetSdkVersion 29
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
@ -71,6 +77,4 @@ flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
dependencies {}

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
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.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -1,16 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
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
android:name="io.flutter.app.FlutterApplication"
android:label="Time Progress Tracker"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
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: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>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -44,4 +31,15 @@
android:name="flutterEmbedding"
android:value="2" />
</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>

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 -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- 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>
</style>
<!-- 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"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
@ -12,7 +12,7 @@
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
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.
-->
<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 {
repositories {
google()
jcenter()
mavenCentral()
}
}
@ -26,6 +13,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

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

View File

@ -1,6 +1,5 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
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'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}
include ":app"

2
ios/.gitignore vendored
View File

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

View File

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<string>12.0</string>
</dict>
</plist>

View File

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

View File

@ -41,7 +41,9 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</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,9 +49,10 @@ class DeleteTimeProgressAction {
}
void loadTimeProgressListIfUnloaded(Store<AppState> store) {
if (!store.state.hasProgressesLoaded)
if (!store.state.hasProgressesLoaded) {
store.dispatch(LoadTimeProgressListAction());
}
}
void loadSettingsIfUnloaded(Store<AppState> store) {
if (!store.state.hasSettingsLoaded) store.dispatch(LoadSettingsAction());

View File

@ -11,10 +11,10 @@ class TimeProgressTrackerApp extends StatelessWidget {
final Store<AppState> store;
TimeProgressTrackerApp({
Key key,
this.store,
}) : super(key: key);
const TimeProgressTrackerApp({
super.key,
required this.store,
});
@override
Widget build(BuildContext context) {
@ -23,19 +23,22 @@ class TimeProgressTrackerApp extends StatelessWidget {
child: MaterialApp(
title: name,
theme: ThemeData(
primarySwatch: Colors.indigo,
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.indigo,
accentColor: Colors.indigoAccent,
backgroundColor: Colors.white
),
brightness: Brightness.light,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: HomeScreen.routeName,
routes: {
HomeScreen.routeName: (BuildContext context) =>
HomeScreen(),
HomeScreen.routeName: (BuildContext context) => const HomeScreen(),
ProgressDetailScreen.routeName: (BuildContext context) =>
ProgressDetailScreen(),
const ProgressDetailScreen(),
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';
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) =>
tpList.where((tp) => tp.hasStarted() && !tp.hasEnded()).toList();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,13 +10,12 @@ class TimeProgressRepository {
TimeProgressRepository(this.prefs, {this.codec = json});
Future<List<TimeProgressEntity>> loadTimeProgressList() {
final String jsonString = this.prefs.getString(_key);
final String? jsonString = prefs.getString(_key);
if (jsonString == null) {
return Future<List<TimeProgressEntity>>.value([]);
}
return Future<List<TimeProgressEntity>>.value(codec
.decode(jsonString)["timers"]
.cast<Map<String, Object>>()
.map<TimeProgressEntity>(TimeProgressEntity.fromJson)
.toList(growable: false));
}
@ -24,6 +23,6 @@ class TimeProgressRepository {
Future<bool> saveTimeProgressList(List<TimeProgressEntity> timeProgressList) {
final String jsonString = codec.encode(
{"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>([
TypedReducer<AppSettings, AppSettingsLoadedActions>(_loadAppSettings),
TypedReducer<AppSettings, UpdateAppSettingsActions>(_updateAppSettings),
TypedReducer<AppSettings, AppSettingsNotLoadedAction>(_setDefaultSettings)
TypedReducer<AppSettings, AppSettingsLoadedActions>(_loadAppSettings).call,
TypedReducer<AppSettings, UpdateAppSettingsActions>(_updateAppSettings).call,
TypedReducer<AppSettings, AppSettingsNotLoadedAction>(_setDefaultSettings).call
]);
AppSettings _loadAppSettings(

View File

@ -2,8 +2,8 @@ import 'package:redux/redux.dart';
import 'package:time_progress_tracker/actions/actions.dart';
final hasProgressesLoadedReducer = combineReducers<bool>([
TypedReducer<bool, TimeProgressListLoadedAction>(_setProgressesLoaded),
TypedReducer<bool, TimeProgressListNotLoadedAction>(_setProgressesUnloaded)
TypedReducer<bool, TimeProgressListLoadedAction>(_setProgressesLoaded).call,
TypedReducer<bool, TimeProgressListNotLoadedAction>(_setProgressesUnloaded).call
]);
bool _setProgressesLoaded(bool hasLoaded, TimeProgressListLoadedAction action) {
@ -15,8 +15,8 @@ bool _setProgressesUnloaded(bool hasLoaded, TimeProgressListNotLoadedAction acti
}
final hasSettingsLoadedReducer = combineReducers<bool>([
TypedReducer<bool, AppSettingsLoadedActions>(_setSettingsLoaded),
TypedReducer<bool, AppSettingsNotLoadedAction>(_setSettingsUnloaded)
TypedReducer<bool, AppSettingsLoadedActions>(_setSettingsLoaded).call,
TypedReducer<bool, AppSettingsNotLoadedAction>(_setSettingsUnloaded).call
]);
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>>([
TypedReducer<List<TimeProgress>, TimeProgressListLoadedAction>(
_setLoadedTimeProgressList),
_setLoadedTimeProgressList).call,
TypedReducer<List<TimeProgress>, TimeProgressListNotLoadedAction>(
_setEmptyTimeProgressList),
TypedReducer<List<TimeProgress>, AddTimeProgressAction>(_addTimeProgress),
_setEmptyTimeProgressList).call,
TypedReducer<List<TimeProgress>, AddTimeProgressAction>(_addTimeProgress).call,
TypedReducer<List<TimeProgress>, UpdateTimeProgressAction>(
_updateTimeProgress),
TypedReducer<List<TimeProgress>, DeleteTimeProgressAction>(_deleteTimeProgress),
_updateTimeProgress).call,
TypedReducer<List<TimeProgress>, DeleteTimeProgressAction>(_deleteTimeProgress).call,
]);
List<TimeProgress> _setLoadedTimeProgressList(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,11 +7,12 @@ class ColorPickerButton extends StatelessWidget {
final Color selectedColor;
final void Function(Color) onColorPicked;
ColorPickerButton({
@required this.title,
@required this.dialogTitle,
@required this.selectedColor,
@required this.onColorPicked,
const ColorPickerButton({
super.key,
required this.title,
required this.dialogTitle,
required this.selectedColor,
required this.onColorPicked,
});
@override
@ -34,13 +35,13 @@ class ColorPickerButton extends StatelessWidget {
},
);
},
child: Text(title),
style: TextButton.styleFrom(
primary: useBrightBackground(selectedColor)
? appTheme.primaryTextTheme.button.color
: appTheme.textTheme.button.color,
foregroundColor: useBrightBackground(selectedColor)
? appTheme.primaryTextTheme.labelLarge?.color
: appTheme.textTheme.labelLarge?.color,
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 {
final String _heroTag = "createProgressBTN";
const CreateProgressButton({super.key});
@override
Widget build(BuildContext context) {
void _onButtonPressed() =>
void onButtonPressed() =>
Navigator.pushNamed(context, ProgressCreationScreen.routeName);
return FloatingActionButton(
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 {
final String leadingString;
final DateTime pickedDate;
final void Function(DateTime) onDatePicked;
final void Function(DateTime?) onDatePicked;
DatePickerBtn({
@required this.leadingString,
@required this.pickedDate,
@required this.onDatePicked,
}) : super();
const DatePickerBtn({
super.key,
required this.leadingString,
required this.pickedDate,
required this.onDatePicked,
});
void _onButtonPressed(BuildContext context) async {
onDatePicked(await showDatePicker(
@ -25,12 +26,12 @@ class DatePickerBtn extends StatelessWidget {
ThemeData appTheme = Theme.of(context);
return TextButton(
onPressed: () => _onButtonPressed(context),
style: TextButton.styleFrom(
foregroundColor: appTheme.primaryTextTheme.labelLarge?.color,
backgroundColor: appTheme.colorScheme.secondary,
),
child: Text(
"$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 void Function(Duration) updateDuration;
SelectDurationBtn({
@required this.duration,
@required this.updateDuration,
const SelectDurationBtn({
super.key,
required this.duration,
required this.updateDuration,
});
void _onPickerConfirm(Picker picker, List<int> values) {
@ -25,7 +26,7 @@ class SelectDurationBtn extends StatelessWidget {
]),
hideHeader: false,
title: const Text("Default Duration"),
selectedTextStyle: TextStyle(color: appTheme.accentColor),
selectedTextStyle: TextStyle(color: appTheme.colorScheme.secondary),
onConfirm: _onPickerConfirm)
.showModal(context);
@ -38,10 +39,10 @@ class SelectDurationBtn extends StatelessWidget {
int days = duration.inDays - (365 * years) - (30 * months);
return TextButton(
onPressed: () => _onButtonPressed(context, appTheme),
child: Text("$years Years $months Months $days Days"),
style: TextButton.styleFrom(
primary: appTheme.primaryTextTheme.button.color,
backgroundColor: appTheme.accentColor,
));
foregroundColor: appTheme.primaryTextTheme.labelLarge?.color,
backgroundColor: appTheme.colorScheme.secondary,
),
child: Text("$years Years $months Months $days Days"));
}
}

View File

@ -10,25 +10,26 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
onCancelEditProgress,
onDeleteProgress;
DetailScreenFloatingActionButtons({
@required this.editMode,
@required this.originalProgress,
@required this.editedProgress,
@required this.isEditedProgressValid,
@required this.onEditProgress,
@required this.onSaveEditedProgress,
@required this.onCancelEditProgress,
@required this.onDeleteProgress,
const DetailScreenFloatingActionButtons({
super.key,
required this.editMode,
required this.originalProgress,
required this.editedProgress,
required this.isEditedProgressValid,
required this.onEditProgress,
required this.onSaveEditedProgress,
required this.onCancelEditProgress,
required this.onDeleteProgress,
});
@override
Widget build(BuildContext context) {
final ThemeData appTheme = Theme.of(context);
void _onCancelEditTimeProgressBTN() {
if (originalProgress == editedProgress)
void onCancelEditTimeProgressBTN() {
if (originalProgress == editedProgress) {
onCancelEditProgress();
else {
} else {
showDialog(
context: context,
builder: (_) => AppYesNoDialog(
@ -44,7 +45,7 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
}
}
void _onDeleteTimeProgressBTN() {
void onDeleteTimeProgressBTN() {
showDialog(
context: context,
builder: (_) => AppYesNoDialog(
@ -61,13 +62,13 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
child: FloatingActionButton(
heroTag:
editMode ? "saveEditedTimeProgressBTN" : "editTimeProgressBTN",
child: editMode ? Icon(Icons.save) : Icon(Icons.edit),
backgroundColor: editMode ? Colors.green : appTheme.accentColor,
backgroundColor: editMode ? Colors.green : appTheme.colorScheme.secondary,
onPressed: editMode
? isEditedProgressValid
? onSaveEditedProgress
: null
: onEditProgress,
child: editMode ? const Icon(Icons.save) : const Icon(Icons.edit),
),
),
Expanded(
@ -75,11 +76,11 @@ class DetailScreenFloatingActionButtons extends StatelessWidget {
heroTag: editMode
? "cancelEditTimeProgressBTN"
: "deleteTimeProgressBTN",
child: editMode ? Icon(Icons.cancel) : Icon(Icons.delete),
backgroundColor: Colors.red,
onPressed: editMode
? _onCancelEditTimeProgressBTN
: _onDeleteTimeProgressBTN,
? onCancelEditTimeProgressBTN
: 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 {
final int currentIndex;
final Function onTap;
final void Function(int)? onTap;
HomeBottomNavBar({
Key key,
@required this.currentIndex,
@required this.onTap,
}) : super(key: key);
const HomeBottomNavBar({
super.key,
required this.currentIndex,
required this.onTap,
});
@override
Widget build(BuildContext context) {
@ -19,21 +19,21 @@ class HomeBottomNavBar extends StatelessWidget {
currentIndex: currentIndex,
items: [
BottomNavigationBarItem(
icon: new Icon(
icon: Icon(
Icons.alarm,
color: appTheme.primaryColor,
),
label: "Active Progresses",
),
BottomNavigationBarItem(
icon: new Icon(
icon: Icon(
Icons.alarm_off,
color: appTheme.primaryColor,
),
label: "Inactive Progresses",
),
BottomNavigationBarItem(
icon: new Icon(
icon: Icon(
Icons.settings,
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';
class HomeActiveProgressesTab extends StatelessWidget {
const HomeActiveProgressesTab({super.key});
@override
Widget build(BuildContext context) {
return SettingsStoreConnector(
@ -14,14 +16,15 @@ class HomeActiveProgressesTab extends StatelessWidget {
loadedBuilder: (context, tpListVm) {
List<TimeProgress> activeTpList =
selectActiveProgresses(tpListVm.tpList);
if (activeTpList.length < 1)
if (activeTpList.isEmpty) {
return Container(
padding: EdgeInsets.all(16),
child: Center(
padding: const EdgeInsets.all(16),
child: const Center(
child: Text(
"You don't have any active time progress, that are tracked."),
),
);
}
return ProgressListView(
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';
class HomeInactiveProgressesTab extends StatelessWidget {
const HomeInactiveProgressesTab({super.key});
@override
Widget build(BuildContext context) {
return SettingsStoreConnector(
@ -14,14 +16,15 @@ class HomeInactiveProgressesTab extends StatelessWidget {
loadedBuilder: (context, tpListVm) {
List<TimeProgress> inactiveTpList =
selectInactiveProgresses(tpListVm.tpList);
if (inactiveTpList.length < 1)
if (inactiveTpList.isEmpty) {
return Container(
padding: EdgeInsets.all(16),
child: Center(
padding: const EdgeInsets.all(16),
child: const Center(
child: Text(
"You don't have any currently inactive time progresses, that are tracked."),
),
);
}
return ProgressListView(
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';
class HomeSettingsTab extends StatelessWidget {
const HomeSettingsTab({super.key});
@override
Widget build(BuildContext context) {
return SettingsStoreConnector(
loadedBuilder: (context, settingsVm) {
return Container(
padding: EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
child: Center(
child: Column(
children: [
@ -28,7 +30,7 @@ class HomeSettingsTab extends StatelessWidget {
updateDuration: settingsVm.updateDuration,
),
),
Spacer(),
const Spacer(),
Expanded(
child: TextButton(
onPressed: () {
@ -39,7 +41,7 @@ class HomeSettingsTab extends StatelessWidget {
applicationLegalese:
'\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 void Function(Color) updateDoneColor, updateLeftColor;
ColorSettingsWidget({
@required this.doneColor,
@required this.leftColor,
@required this.updateDoneColor,
@required this.updateLeftColor,
const ColorSettingsWidget({
super.key,
required this.doneColor,
required this.leftColor,
required this.updateDoneColor,
required this.updateLeftColor,
});
@override
@ -21,14 +22,14 @@ class ColorSettingsWidget extends StatelessWidget {
Expanded(
child: Text(
"Color Settings",
style: appTheme.textTheme.headline6,
style: appTheme.textTheme.titleLarge,
),
),
Row(
children: [
Expanded(
child: Padding(
padding: EdgeInsets.only(right: 5),
padding: const EdgeInsets.only(right: 5),
child: ColorPickerButton(
title: "Done Color",
dialogTitle: "Select Done Color",
@ -39,7 +40,7 @@ class ColorSettingsWidget extends StatelessWidget {
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 5),
padding: const EdgeInsets.only(left: 5),
child: ColorPickerButton(
title: "Left Color",
dialogTitle: "Select Left Color",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,100 +5,122 @@ packages:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
url: "https://pub.dev"
source: hosted
version: "2.0.13"
version: "3.4.10"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "1.6.0"
version: "2.4.2"
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
charcode:
version: "1.3.0"
checked_yaml:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
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:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.15.0"
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
url: "https://pub.dartlang.org"
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "2.1.5"
version: "3.0.3"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
url: "https://pub.dartlang.org"
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
url: "https://pub.dev"
source: hosted
version: "0.1.3"
version: "1.0.6"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.dartlang.org"
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
version: "2.1.2"
file:
dependency: transitive
description:
name: file
url: "https://pub.dartlang.org"
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
@ -108,32 +130,43 @@ packages:
dependency: "direct main"
description:
name: flutter_colorpicker
url: "https://pub.dartlang.org"
sha256: "458a6ed8ea480eb16ff892aedb4b7092b2804affd7e046591fb03127e8d8ef8b"
url: "https://pub.dev"
source: hosted
version: "0.3.5"
version: "1.0.3"
flutter_launcher_icons:
dependency: "direct dev"
description:
name: flutter_launcher_icons
url: "https://pub.dartlang.org"
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
url: "https://pub.dev"
source: hosted
version: "0.8.1"
version: "0.13.1"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev"
source: hosted
version: "3.0.1"
flutter_picker:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: e95d121f54faba889fbf8a850c86dd5cf4aa5c5a
url: "git://github.com/yangyxd/flutter_picker.git"
resolved-ref: "4a88a436b64d043d2e5412d64035a9bbe7f9d45d"
url: "https://github.com/yangyxd/flutter_picker.git"
source: git
version: "1.1.5"
version: "2.1.1"
flutter_redux:
dependency: "direct main"
description:
name: flutter_redux
url: "https://pub.dartlang.org"
sha256: "3b20be9e08d0038e1452fbfa1fdb1ea0a7c3738c997734530b3c6d0bb5fcdbdc"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
version: "0.10.0"
flutter_test:
dependency: "direct dev"
description: flutter
@ -148,142 +181,218 @@ packages:
dependency: transitive
description:
name: image
url: "https://pub.dartlang.org"
sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e"
url: "https://pub.dev"
source: hosted
version: "2.1.19"
version: "4.1.7"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
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:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
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:
dependency: "direct main"
description:
name: meta
url: "https://pub.dartlang.org"
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.11.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
version: "1.9.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.2.1"
percent_indicator:
dependency: "direct main"
description:
name: percent_indicator
url: "https://pub.dartlang.org"
sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c
url: "https://pub.dev"
source: hosted
version: "2.1.9+1"
version: "4.2.3"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "6.0.2"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.dartlang.org"
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
process:
version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
name: pointycastle
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
version: "3.7.4"
redux:
dependency: "direct main"
description:
name: redux
url: "https://pub.dartlang.org"
sha256: "1e86ed5b1a9a717922d0a0ca41f9bf49c1a587d50050e9426fc65b14e85ec4d7"
url: "https://pub.dev"
source: hosted
version: "4.0.0+3"
version: "5.0.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.dev"
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:
dependency: transitive
description:
name: shared_preferences_linux
url: "https://pub.dartlang.org"
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
shared_preferences_macos:
dependency: transitive
description:
name: shared_preferences_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
version: "2.3.2"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
url: "https://pub.dartlang.org"
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.3.2"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
url: "https://pub.dartlang.org"
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.3.0"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "2.3.2"
sky_engine:
dependency: transitive
description: flutter
@ -293,86 +402,114 @@ packages:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
version: "1.10.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.1.2"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.0"
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
url: "https://pub.dev"
source: hosted
version: "0.2.19"
version: "0.6.1"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.3.2"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
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:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "5.3.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev"
source: hosted
version: "0.2.0"
version: "1.0.4"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "4.5.1"
version: "6.5.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "3.1.2"
sdks:
dart: ">=2.12.0-259.9.beta <3.0.0"
flutter: ">=1.20.0"
dart: ">=3.3.1 <4.0.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.
# 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
# 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.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.0.19+19
version: 0.0.21+21
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:
flutter:
sdk: flutter
flutter_colorpicker:
flutter_redux:
flutter_picker:
git: git://github.com/yangyxd/flutter_picker.git
git:
url: https://github.com/yangyxd/flutter_picker.git
meta:
percent_indicator:
redux:
@ -35,17 +42,24 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.3
cupertino_icons: ^1.0.6
dev_dependencies:
flutter_launcher_icons:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
@ -59,7 +73,7 @@ flutter:
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages

View File

@ -3,8 +3,9 @@ import 'package:flutter/material.dart';
class MaterialTesterWidget extends StatelessWidget {
final Widget widget;
MaterialTesterWidget({
@required this.widget,
const MaterialTesterWidget({
super.key,
required this.widget,
});
@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_view.dart';
import 'MaterialTesterWidget.dart';
import 'material_tester_widget.dart';
void main() {
final AppSettings _defaultAppSettings = AppSettings.defaults();
final int _thisYear = DateTime.now().year;
final TimeProgress _activeProgress = TimeProgress(
"TestProgress", DateTime(_thisYear - 2), DateTime(_thisYear + 2));
final AppSettings defaultAppSettings = AppSettings.defaults();
final int thisYear = DateTime.now().year;
final TimeProgress activeProgress = TimeProgress(
"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",
(WidgetTester tester) async {
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile(
timeProgress: _activeProgress,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
timeProgress: activeProgress,
doneColor: defaultAppSettings.doneColor,
leftColor: defaultAppSettings.leftColor,
),
));
_findStringOnce(_activeProgress.name);
_findStringOnce(ProgressListTileStrings.percentString(_activeProgress));
findStringOnce(activeProgress.name);
findStringOnce(ProgressListTileStrings.percentString(activeProgress));
WidgetPredicate linearPercentPredicate = (Widget widget) =>
linearPercentPredicate(Widget widget) =>
widget is LinearPercentIndicator &&
widget.percent == _activeProgress.percentDone() &&
widget.progressColor == _defaultAppSettings.doneColor &&
widget.backgroundColor == _defaultAppSettings.leftColor;
widget.percent == activeProgress.percentDone() &&
widget.progressColor == defaultAppSettings.doneColor &&
widget.backgroundColor == defaultAppSettings.leftColor;
expect(find.byWidgetPredicate(linearPercentPredicate), findsOneWidget);
});
@ -48,40 +48,40 @@ void main() {
(WidgetTester tester) async {
TimeProgress futureProgress = TimeProgress(
"Test Progress",
DateTime(_thisYear + 1),
DateTime(_thisYear + 2),
DateTime(thisYear + 1),
DateTime(thisYear + 2),
);
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile(
timeProgress: futureProgress,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
doneColor: defaultAppSettings.doneColor,
leftColor: defaultAppSettings.leftColor,
),
));
_findStringOnce(futureProgress.name);
_findStringOnce(ProgressListTileStrings.startsInDaysString(futureProgress));
findStringOnce(futureProgress.name);
findStringOnce(ProgressListTileStrings.startsInDaysString(futureProgress));
});
testWidgets("Progress List Tile with past progress works",
(WidgetTester tester) async {
TimeProgress pastProgress = TimeProgress(
"Test Progress",
DateTime(_thisYear - 2),
DateTime(_thisYear - 1),
DateTime(thisYear - 2),
DateTime(thisYear - 1),
);
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListTile(
timeProgress: pastProgress,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
doneColor: defaultAppSettings.doneColor,
leftColor: defaultAppSettings.leftColor,
),
));
_findStringOnce(pastProgress.name);
_findStringOnce(ProgressListTileStrings.endedDaysAgoString(pastProgress));
findStringOnce(pastProgress.name);
findStringOnce(ProgressListTileStrings.endedDaysAgoString(pastProgress));
});
WidgetPredicate getProgressListTilePredicate(
@ -96,35 +96,37 @@ void main() {
(WidgetTester tester) async {
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListView(
timeProgressList: [_activeProgress],
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
timeProgressList: [activeProgress],
doneColor: defaultAppSettings.doneColor,
leftColor: defaultAppSettings.leftColor,
),
));
_findStringOnce(_activeProgress.name);
findStringOnce(activeProgress.name);
expect(
find.byWidgetPredicate(
getProgressListTilePredicate(_activeProgress, _defaultAppSettings)),
getProgressListTilePredicate(activeProgress, defaultAppSettings)),
findsOneWidget);
});
testWidgets("Progress List View displays file tiles",
(WidgetTester tester) async {
List<TimeProgress> tpList = [];
for (int i = 0; i < 5; i++) tpList.add(_activeProgress);
for (int i = 0; i < 5; i++) {
tpList.add(activeProgress);
}
await tester.pumpWidget(MaterialTesterWidget(
widget: ProgressListView(
timeProgressList: tpList,
doneColor: _defaultAppSettings.doneColor,
leftColor: _defaultAppSettings.leftColor,
doneColor: defaultAppSettings.doneColor,
leftColor: defaultAppSettings.leftColor,
),
));
expect(find.text(_activeProgress.name), findsNWidgets(5));
expect(find.text(activeProgress.name), findsNWidgets(5));
expect(
find.byWidgetPredicate(
getProgressListTilePredicate(_activeProgress, _defaultAppSettings)),
getProgressListTilePredicate(activeProgress, defaultAppSettings)),
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
it to work correctly.
Fore more details:
For more details:
* 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 content="IE=Edge" http-equiv="X-UA-Compatible">
@ -28,18 +31,29 @@
<title>time_progress_tracker</title>
<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>
<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>
if ('serviceWorker' in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

View File

@ -18,6 +18,18 @@
"src": "icons/Icon-512.png",
"sizes": "512x512",
"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"
}
]
}