Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
f0ccf59db9 | |||
cc7caffffa | |||
17d8fac893 | |||
1f48e868b0 | |||
9d2871405d | |||
9637551d7a | |||
2456f7cbda | |||
0f39a30921 | |||
1e4ca0c61b | |||
1269a93624 | |||
a5615fe3cb | |||
342b870e93 | |||
0647f260db |
92 changed files with 6906 additions and 1175 deletions
14
.metadata
14
.metadata
|
@ -4,8 +4,8 @@
|
||||||
# This file should be version controlled.
|
# This file should be version controlled.
|
||||||
|
|
||||||
version:
|
version:
|
||||||
revision: bcea432bce54a83306b3c00a7ad0ed98f777348d
|
revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||||
channel: beta
|
channel: stable
|
||||||
|
|
||||||
project_type: app
|
project_type: app
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@ project_type: app
|
||||||
migration:
|
migration:
|
||||||
platforms:
|
platforms:
|
||||||
- platform: root
|
- platform: root
|
||||||
create_revision: bcea432bce54a83306b3c00a7ad0ed98f777348d
|
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||||
base_revision: bcea432bce54a83306b3c00a7ad0ed98f777348d
|
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||||
- platform: linux
|
- platform: windows
|
||||||
create_revision: bcea432bce54a83306b3c00a7ad0ed98f777348d
|
create_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||||
base_revision: bcea432bce54a83306b3c00a7ad0ed98f777348d
|
base_revision: 52b3dc25f6471c27b2144594abb11c741cb88f57
|
||||||
|
|
||||||
# User provided section
|
# User provided section
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
v2.7.11
|
||||||
|
Add support for IC trains.
|
||||||
|
Allow choosing displayed timezone.
|
||||||
|
Show notes about wagon detachment, receival, or train number changes.
|
||||||
|
Use system accent color if available.
|
||||||
|
Use API v3.
|
||||||
|
|
||||||
|
v2.7.10
|
||||||
|
Add about page to Fluent UI.
|
||||||
|
Add settings page, allowing changing between UIs.
|
||||||
|
Add touch scrolling on Linux.
|
||||||
|
|
||||||
v2.7.9
|
v2.7.9
|
||||||
Add Fluent UI for Windows and Linux.
|
Add Fluent UI for Windows and Linux.
|
||||||
Add split view in landscape when viewing a train.
|
Add split view in landscape when viewing a train.
|
||||||
|
|
|
@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 31
|
compileSdkVersion 33
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
@ -51,7 +51,7 @@ android {
|
||||||
// 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 "ro.dcdev.infotren"
|
applicationId "ro.dcdev.infotren"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 31
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,6 @@ subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ 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-6.7.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.2-all.zip
|
||||||
|
|
216
codemagic.yaml
Normal file
216
codemagic.yaml
Normal file
File diff suppressed because one or more lines are too long
|
@ -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>9.0</string>
|
<string>11.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Uncomment this line to define a global platform for your project
|
# Uncomment this line to define a global platform for your project
|
||||||
# platform :ios, '9.0'
|
# platform :ios, '11.0'
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
34
ios/Podfile.lock
Normal file
34
ios/Podfile.lock
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
PODS:
|
||||||
|
- Flutter (1.0.0)
|
||||||
|
- package_info_plus (0.4.5):
|
||||||
|
- Flutter
|
||||||
|
- shared_preferences_ios (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- url_launcher_ios (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- Flutter (from `Flutter`)
|
||||||
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
|
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
|
||||||
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
Flutter:
|
||||||
|
:path: Flutter
|
||||||
|
package_info_plus:
|
||||||
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
|
shared_preferences_ios:
|
||||||
|
:path: ".symlinks/plugins/shared_preferences_ios/ios"
|
||||||
|
url_launcher_ios:
|
||||||
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
|
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||||
|
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
|
||||||
|
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
||||||
|
|
||||||
|
COCOAPODS: 1.11.3
|
|
@ -13,6 +13,7 @@
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
|
AF5528149967EA996B5AA109 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E6EB5FA5AA2228D5622CD62 /* Pods_Runner.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
@ -31,7 +32,11 @@
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
2088AE25E07C211FFB9CE536 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
2F80AD107B0E1CC9E1C01A5A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
5DA42B3CD8940DB121C028E8 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
6E6EB5FA5AA2228D5622CD62 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
@ -49,6 +54,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
AF5528149967EA996B5AA109 /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -72,6 +78,8 @@
|
||||||
9740EEB11CF90186004384FC /* Flutter */,
|
9740EEB11CF90186004384FC /* Flutter */,
|
||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
|
B55F9B76DFEAB456725329A0 /* Pods */,
|
||||||
|
E56598AA51C5533E6B51BD5A /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -98,6 +106,25 @@
|
||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
B55F9B76DFEAB456725329A0 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
2F80AD107B0E1CC9E1C01A5A /* Pods-Runner.debug.xcconfig */,
|
||||||
|
2088AE25E07C211FFB9CE536 /* Pods-Runner.release.xcconfig */,
|
||||||
|
5DA42B3CD8940DB121C028E8 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
E56598AA51C5533E6B51BD5A /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
6E6EB5FA5AA2228D5622CD62 /* Pods_Runner.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
@ -105,12 +132,14 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
2B2F3198BD0D2214C77EC99E /* [CP] Check Pods Manifest.lock */,
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
D71FC49A789443CEBF7C5C70 /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -127,7 +156,7 @@
|
||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1020;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
|
@ -169,6 +198,28 @@
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
2B2F3198BD0D2214C77EC99E /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
@ -197,6 +248,23 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||||
};
|
};
|
||||||
|
D71FC49A789443CEBF7C5C70 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
@ -272,7 +340,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
@ -351,7 +419,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -400,7 +468,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1020"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
|
@ -4,4 +4,7 @@
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|
|
@ -43,5 +43,7 @@
|
||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -12,5 +12,7 @@ Future<List<ChangelogEntry>> getRemoteReleases() async {
|
||||||
version: ChangelogVersion.parse(e['tag_name']),
|
version: ChangelogVersion.parse(e['tag_name']),
|
||||||
description: e['body'],
|
description: e['body'],
|
||||||
apkLink: (e['assets'] as List<dynamic>).where((e) => (e['name'] as String).contains('.apk')).map((e) => Uri.parse(e['browser_download_url'] as String)).firstOrNull,
|
apkLink: (e['assets'] as List<dynamic>).where((e) => (e['name'] as String).contains('.apk')).map((e) => Uri.parse(e['browser_download_url'] as String)).firstOrNull,
|
||||||
|
linuxLink: (e['assets'] as List<dynamic>).where((e) => (e['name'] as String).contains('infotren-linux')).map((e) => Uri.parse(e['browser_download_url'] as String)).firstOrNull,
|
||||||
|
windowsLink: (e['assets'] as List<dynamic>).where((e) => (e['name'] as String).contains('-win.zip')).map((e) => Uri.parse(e['browser_download_url'] as String)).firstOrNull,
|
||||||
)).toList();
|
)).toList();
|
||||||
}
|
}
|
|
@ -4,7 +4,11 @@ import 'package:http/http.dart' as http;
|
||||||
import 'package:info_tren/api/common.dart';
|
import 'package:info_tren/api/common.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
|
|
||||||
Future<StationData> getStationData(String stationName) async {
|
Future<StationData> getStationData(String stationName, [DateTime? date]) async {
|
||||||
final response = await http.get(Uri.https(authority, 'v3/stations/$stationName'));
|
final uri = Uri.https(authority, 'v3/stations/$stationName');
|
||||||
|
if (date != null) {
|
||||||
|
uri.queryParameters['date'] = date.toIso8601String();
|
||||||
|
}
|
||||||
|
final response = await http.get(uri);
|
||||||
return StationData.fromJson(jsonDecode(response.body));
|
return StationData.fromJson(jsonDecode(response.body));
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:info_tren/models.dart';
|
||||||
|
|
||||||
Future<TrainData> getTrain(String trainNumber, {DateTime? date}) async {
|
Future<TrainData> getTrain(String trainNumber, {DateTime? date}) async {
|
||||||
date ??= DateTime.now();
|
date ??= DateTime.now();
|
||||||
final response = await http.get(Uri.https(authority, 'v2/train/$trainNumber', {
|
final response = await http.get(Uri.https(authority, 'v3/trains/$trainNumber', {
|
||||||
'date': date.toUtc().toIso8601String(),
|
'date': date.toUtc().toIso8601String(),
|
||||||
}),);
|
}),);
|
||||||
return trainDataFromJson(response.body);
|
return trainDataFromJson(response.body);
|
||||||
|
|
|
@ -5,19 +5,29 @@ class CupertinoListTile extends StatelessWidget {
|
||||||
final Widget? title;
|
final Widget? title;
|
||||||
final Widget? subtitle;
|
final Widget? subtitle;
|
||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
|
final void Function()? onTap;
|
||||||
|
|
||||||
const CupertinoListTile({ Key? key, this.leading, this.title, this.subtitle, this.trailing, }) : super(key: key);
|
const CupertinoListTile({ super.key, this.leading, this.title, this.subtitle, this.trailing, this.onTap, });
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
if (leading != null)
|
if (leading != null)
|
||||||
leading!,
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: leading!,
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (title != null)
|
if (title != null)
|
||||||
title!,
|
title!,
|
||||||
|
@ -35,9 +45,14 @@ class CupertinoListTile extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
if (trailing != null)
|
if (trailing != null)
|
||||||
trailing!,
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: trailing!,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_cupertino.dart';
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_cupertino.dart';
|
||||||
|
@ -6,7 +5,6 @@ import 'package:info_tren/components/select_train_suggestions/select_train_sugge
|
||||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_material.dart';
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_material.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
import 'package:info_tren/utils/default_ui_design.dart';
|
|
||||||
|
|
||||||
class SelectTrainSuggestions extends ConsumerWidget {
|
class SelectTrainSuggestions extends ConsumerWidget {
|
||||||
final List<TrainsResult> choices;
|
final List<TrainsResult> choices;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:info_tren/components/cupertino_divider.dart';
|
import 'package:info_tren/components/cupertino_divider.dart';
|
||||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
|
|
||||||
class SelectTrainSuggestionsCupertino extends SelectTrainSuggestionsShared {
|
class SelectTrainSuggestionsCupertino extends SelectTrainSuggestionsShared {
|
||||||
|
@ -69,8 +70,8 @@ class OperatorAutocompleteTileCupertino extends OperatorAutocompleteTile {
|
||||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
|
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
Text(
|
Text.rich(
|
||||||
"${train.rank} ${train.number}",
|
trainIdSpan(rank: train.rank, number: train.number),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
|
|
||||||
class SelectTrainSuggestionsFluent extends SelectTrainSuggestionsShared {
|
class SelectTrainSuggestionsFluent extends SelectTrainSuggestionsShared {
|
||||||
|
@ -68,8 +69,8 @@ class OperatorAutocompleteTileFluent extends OperatorAutocompleteTile {
|
||||||
style: FluentTheme.of(context).typography.body?.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
|
style: FluentTheme.of(context).typography.body?.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
Text(
|
Text.rich(
|
||||||
"${train.rank} ${train.number}",
|
trainIdSpan(rank: train.rank, number: train.number),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
|
|
||||||
class SelectTrainSuggestionsMaterial extends SelectTrainSuggestionsShared {
|
class SelectTrainSuggestionsMaterial extends SelectTrainSuggestionsShared {
|
||||||
|
@ -44,7 +45,7 @@ class OperatorAutocompleteTileMaterial extends OperatorAutocompleteTile {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
title: Text("${train.rank} ${train.number}"),
|
title: Text.rich(trainIdSpan(rank: train.rank, number: train.number)),
|
||||||
subtitle: Text(operatorName),
|
subtitle: Text(operatorName),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
onTrainSelected(train.number);
|
onTrainSelected(train.number);
|
||||||
|
|
|
@ -44,7 +44,7 @@ class SlimAppBar extends StatelessWidget {
|
||||||
title,
|
title,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style:
|
style:
|
||||||
Theme.of(context).appBarTheme.textTheme?.bodySmall?.copyWith(color: Theme.of(context).appBarTheme.textTheme?.bodyMedium?.color) ??
|
Theme.of(context).textTheme.titleSmall?.copyWith(color: Theme.of(context).textTheme.titleLarge?.color) ??
|
||||||
Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).textTheme.bodyMedium?.color),
|
Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).textTheme.bodyMedium?.color),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
39
lib/components/train_id_text_span.dart
Normal file
39
lib/components/train_id_text_span.dart
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
|
TextSpan trainIdSpan({
|
||||||
|
required String rank,
|
||||||
|
required String number,
|
||||||
|
Locale? locale,
|
||||||
|
MouseCursor? mouseCursor,
|
||||||
|
void Function(PointerEnterEvent)? onEnter,
|
||||||
|
void Function(PointerExitEvent)? onExit,
|
||||||
|
GestureRecognizer? recognizer,
|
||||||
|
String? semanticsLabel,
|
||||||
|
bool? spellOut,
|
||||||
|
TextStyle? style,
|
||||||
|
}) => TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: rank,
|
||||||
|
style: TextStyle(
|
||||||
|
inherit: true,
|
||||||
|
color: rank.startsWith('IC')
|
||||||
|
? const Color.fromARGB(255, 0, 255, 0)
|
||||||
|
: rank.startsWith('IR')
|
||||||
|
? const Color.fromARGB(255, 255, 0, 0)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const TextSpan(text: ' '),
|
||||||
|
TextSpan(text: number),
|
||||||
|
],
|
||||||
|
locale: locale,
|
||||||
|
mouseCursor: mouseCursor,
|
||||||
|
onEnter: onEnter,
|
||||||
|
onExit: onExit,
|
||||||
|
recognizer: recognizer,
|
||||||
|
semanticsLabel: semanticsLabel,
|
||||||
|
spellOut: spellOut,
|
||||||
|
style: style,
|
||||||
|
);
|
|
@ -1,5 +1,9 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as f;
|
import 'package:fluent_ui/fluent_ui.dart' as f;
|
||||||
import 'package:flutter/cupertino.dart' as c;
|
import 'package:flutter/cupertino.dart' as c;
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart' as m;
|
import 'package:flutter/material.dart' as m;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
@ -7,14 +11,18 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/about/about_page.dart';
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
import 'package:info_tren/pages/main/main_page.dart';
|
import 'package:info_tren/pages/main/main_page.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:timezone/data/latest.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
initializeTimeZones();
|
||||||
final sharedPreferences = await SharedPreferences.getInstance();
|
final sharedPreferences = await SharedPreferences.getInstance();
|
||||||
runApp(
|
runApp(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
|
@ -33,6 +41,9 @@ Map<String, WidgetBuilder> get routes => {
|
||||||
AboutPage.routeName: (context) {
|
AboutPage.routeName: (context) {
|
||||||
return const AboutPage();
|
return const AboutPage();
|
||||||
},
|
},
|
||||||
|
SettingsPage.routeName: (context) {
|
||||||
|
return const SettingsPage();
|
||||||
|
},
|
||||||
SelectTrainPage.routeName: (context) {
|
SelectTrainPage.routeName: (context) {
|
||||||
return const SelectTrainPage();
|
return const SelectTrainPage();
|
||||||
},
|
},
|
||||||
|
@ -59,6 +70,36 @@ Map<String, WidgetBuilder> get routes => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DragFluentScrollBevahior extends f.FluentScrollBehavior {
|
||||||
|
const DragFluentScrollBevahior();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class DragCupertinoScrollBevahior extends c.CupertinoScrollBehavior {
|
||||||
|
const DragCupertinoScrollBevahior();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class DragMaterialScrollBevahior extends m.MaterialScrollBehavior {
|
||||||
|
const DragMaterialScrollBevahior();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<PointerDeviceKind> get dragDevices => {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class StartPoint extends ConsumerWidget {
|
class StartPoint extends ConsumerWidget {
|
||||||
final String appTitle = 'Info Tren';
|
final String appTitle = 'Info Tren';
|
||||||
|
|
||||||
|
@ -68,6 +109,8 @@ class StartPoint extends ConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final uiDesign = ref.watch(uiDesignProvider);
|
final uiDesign = ref.watch(uiDesignProvider);
|
||||||
if (uiDesign == UiDesign.CUPERTINO) {
|
if (uiDesign == UiDesign.CUPERTINO) {
|
||||||
|
return DynamicColorBuilder(
|
||||||
|
builder: (lightScheme, darkScheme) {
|
||||||
return AnnotatedRegion(
|
return AnnotatedRegion(
|
||||||
value: const SystemUiOverlayStyle(
|
value: const SystemUiOverlayStyle(
|
||||||
statusBarBrightness: c.Brightness.dark,
|
statusBarBrightness: c.Brightness.dark,
|
||||||
|
@ -75,7 +118,7 @@ class StartPoint extends ConsumerWidget {
|
||||||
child: c.CupertinoApp(
|
child: c.CupertinoApp(
|
||||||
title: appTitle,
|
title: appTitle,
|
||||||
theme: c.CupertinoThemeData(
|
theme: c.CupertinoThemeData(
|
||||||
primaryColor: m.Colors.blue.shade600,
|
primaryColor: darkScheme?.primary ?? m.Colors.blue.shade600,
|
||||||
brightness: c.Brightness.dark,
|
brightness: c.Brightness.dark,
|
||||||
// textTheme: CupertinoTextThemeData(
|
// textTheme: CupertinoTextThemeData(
|
||||||
// textStyle: TextStyle(
|
// textStyle: TextStyle(
|
||||||
|
@ -83,35 +126,66 @@ class StartPoint extends ConsumerWidget {
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
),
|
),
|
||||||
|
scrollBehavior: Platform.isLinux ? const DragCupertinoScrollBevahior() : null,
|
||||||
routes: routes,
|
routes: routes,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
else if (uiDesign == UiDesign.FLUENT) {
|
else if (uiDesign == UiDesign.FLUENT) {
|
||||||
|
return DynamicColorBuilder(
|
||||||
|
builder: (lightScheme, darkScheme) {
|
||||||
return f.FluentApp(
|
return f.FluentApp(
|
||||||
title: appTitle,
|
title: appTitle,
|
||||||
theme: f.ThemeData(
|
theme: f.FluentThemeData(
|
||||||
|
brightness: f.Brightness.light,
|
||||||
|
accentColor: lightScheme != null ? f.AccentColor.swatch({
|
||||||
|
'normal': lightScheme.primary,
|
||||||
|
}) : f.Colors.blue,
|
||||||
|
),
|
||||||
|
darkTheme: f.FluentThemeData(
|
||||||
brightness: f.Brightness.dark,
|
brightness: f.Brightness.dark,
|
||||||
accentColor: f.Colors.blue,
|
accentColor: darkScheme != null ? f.AccentColor.swatch({
|
||||||
|
'normal': darkScheme.primary,
|
||||||
|
}) : f.Colors.blue,
|
||||||
),
|
),
|
||||||
routes: routes,
|
routes: routes,
|
||||||
|
scrollBehavior: Platform.isLinux ? const DragFluentScrollBevahior() : const f.FluentScrollBehavior(),
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
return DynamicColorBuilder(
|
||||||
|
builder: (lightScheme, darkScheme) {
|
||||||
|
lightScheme ??= m.ColorScheme.fromSwatch(
|
||||||
|
brightness: m.Brightness.light,
|
||||||
|
primarySwatch: m.Colors.blue,
|
||||||
|
);
|
||||||
|
darkScheme ??= m.ColorScheme.fromSwatch(
|
||||||
|
brightness: m.Brightness.dark,
|
||||||
|
primarySwatch: m.Colors.blue,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return m.MaterialApp(
|
return m.MaterialApp(
|
||||||
title: appTitle,
|
title: appTitle,
|
||||||
theme: m.ThemeData(
|
theme: m.ThemeData(
|
||||||
primarySwatch: m.Colors.blue,
|
colorScheme: lightScheme,
|
||||||
colorScheme: m.ColorScheme.fromSwatch(
|
|
||||||
brightness: m.Brightness.dark,
|
|
||||||
primarySwatch: m.Colors.blue,
|
|
||||||
accentColor: m.Colors.blue.shade700,
|
|
||||||
),
|
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
// fontFamily: 'Atkinson Hyperlegible',
|
// fontFamily: 'Atkinson Hyperlegible',
|
||||||
),
|
),
|
||||||
|
darkTheme: m.ThemeData(
|
||||||
|
colorScheme: darkScheme,
|
||||||
|
useMaterial3: true,
|
||||||
|
// fontFamily: 'Atkinson Hyperlegible',
|
||||||
|
),
|
||||||
|
scrollBehavior: Platform.isLinux ? const DragMaterialScrollBevahior() : null,
|
||||||
routes: routes,
|
routes: routes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,11 @@ export 'package:info_tren/models/station_data.dart';
|
||||||
export 'package:info_tren/models/station_status.dart';
|
export 'package:info_tren/models/station_status.dart';
|
||||||
export 'package:info_tren/models/station_train.dart';
|
export 'package:info_tren/models/station_train.dart';
|
||||||
export 'package:info_tren/models/stations_result.dart';
|
export 'package:info_tren/models/stations_result.dart';
|
||||||
export 'package:info_tren/models/train_data.dart' hide State;
|
export 'package:info_tren/models/train_data.dart';
|
||||||
export 'package:info_tren/models/trains_result.dart';
|
export 'package:info_tren/models/trains_result.dart';
|
||||||
export 'package:info_tren/models/ui_design.dart';
|
export 'package:info_tren/models/ui_design.dart';
|
||||||
|
export 'package:info_tren/models/ui_timezone.dart';
|
||||||
|
|
||||||
import 'package:info_tren/models/train_data.dart' show State;
|
import 'package:info_tren/models/train_data.dart' show TrainDataStatusState;
|
||||||
|
|
||||||
typedef TrainDataState = State;
|
typedef TrainDataState = TrainDataStatusState;
|
|
@ -4,8 +4,16 @@ class ChangelogEntry {
|
||||||
final ChangelogVersion version;
|
final ChangelogVersion version;
|
||||||
final String description;
|
final String description;
|
||||||
final Uri? apkLink;
|
final Uri? apkLink;
|
||||||
|
final Uri? linuxLink;
|
||||||
|
final Uri? windowsLink;
|
||||||
|
|
||||||
ChangelogEntry({required this.version, required this.description, this.apkLink});
|
ChangelogEntry({
|
||||||
|
required this.version,
|
||||||
|
required this.description,
|
||||||
|
this.apkLink,
|
||||||
|
this.linuxLink,
|
||||||
|
this.windowsLink,
|
||||||
|
});
|
||||||
|
|
||||||
factory ChangelogEntry.fromTextBlock(String text) {
|
factory ChangelogEntry.fromTextBlock(String text) {
|
||||||
final lines = text.split(RegExp(r'(\r?\n)+'));
|
final lines = text.split(RegExp(r'(\r?\n)+'));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'station_arrdep.dart';
|
part of 'station_arrdep.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'station_data.dart';
|
part of 'station_data.dart';
|
||||||
|
|
||||||
|
@ -158,6 +158,7 @@ class _$_StationData implements _StationData {
|
||||||
List<StationArrDep>? get arrivals {
|
List<StationArrDep>? get arrivals {
|
||||||
final value = _arrivals;
|
final value = _arrivals;
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
|
if (_arrivals is EqualUnmodifiableListView) return _arrivals;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableListView(value);
|
return EqualUnmodifiableListView(value);
|
||||||
}
|
}
|
||||||
|
@ -167,6 +168,7 @@ class _$_StationData implements _StationData {
|
||||||
List<StationArrDep>? get departures {
|
List<StationArrDep>? get departures {
|
||||||
final value = _departures;
|
final value = _departures;
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
|
if (_departures is EqualUnmodifiableListView) return _departures;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableListView(value);
|
return EqualUnmodifiableListView(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'station_status.dart';
|
part of 'station_status.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'station_train.dart';
|
part of 'station_train.dart';
|
||||||
|
|
||||||
|
@ -189,6 +189,7 @@ class _$_StationTrain implements _StationTrain {
|
||||||
List<String>? get route {
|
List<String>? get route {
|
||||||
final value = _route;
|
final value = _route;
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
|
if (_route is EqualUnmodifiableListView) return _route;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableListView(value);
|
return EqualUnmodifiableListView(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'stations_result.dart';
|
part of 'stations_result.dart';
|
||||||
|
|
||||||
|
@ -121,6 +121,7 @@ class _$_StationsResult implements _StationsResult {
|
||||||
List<String>? get stoppedAtBy {
|
List<String>? get stoppedAtBy {
|
||||||
final value = _stoppedAtBy;
|
final value = _stoppedAtBy;
|
||||||
if (value == null) return null;
|
if (value == null) return null;
|
||||||
|
if (_stoppedAtBy is EqualUnmodifiableListView) return _stoppedAtBy;
|
||||||
// ignore: implicit_dynamic_type
|
// ignore: implicit_dynamic_type
|
||||||
return EqualUnmodifiableListView(value);
|
return EqualUnmodifiableListView(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,181 +4,118 @@
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'train_data.freezed.dart';
|
||||||
|
part 'train_data.g.dart';
|
||||||
|
|
||||||
TrainData trainDataFromJson(String str) => TrainData.fromJson(json.decode(str));
|
TrainData trainDataFromJson(String str) => TrainData.fromJson(json.decode(str));
|
||||||
|
|
||||||
String trainDataToJson(TrainData data) => json.encode(data.toJson());
|
String trainDataToJson(TrainData data) => json.encode(data.toJson());
|
||||||
|
|
||||||
/// Results of scrapping InfoFer website for train info
|
/// Results of scrapping InfoFer website for train info
|
||||||
class TrainData {
|
@freezed
|
||||||
TrainData({
|
class TrainData with _$TrainData {
|
||||||
required this.date,
|
const TrainData._();
|
||||||
required this.number,
|
|
||||||
required this.operator,
|
|
||||||
required this.rank,
|
|
||||||
required this.route,
|
|
||||||
required this.stations,
|
|
||||||
this.status,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String date;
|
const factory TrainData({
|
||||||
final String number;
|
required String rank,
|
||||||
final String operator;
|
required String number,
|
||||||
final String rank;
|
required String date,
|
||||||
final Route route;
|
required String operator,
|
||||||
final List<Station> stations;
|
required List<TrainDataGroup> groups,
|
||||||
final TrainDataStatus? status;
|
}) = _TrainData;
|
||||||
|
|
||||||
factory TrainData.fromJson(Map<String, dynamic> json) => TrainData(
|
factory TrainData.fromJson(Map<String, dynamic> json) => _$TrainDataFromJson(json);
|
||||||
date: json["date"],
|
|
||||||
number: json["number"],
|
|
||||||
operator: json["operator"],
|
|
||||||
rank: json["rank"],
|
|
||||||
route: Route.fromJson(json["route"]),
|
|
||||||
stations: List<Station>.from(
|
|
||||||
json["stations"].map((x) => Station.fromJson(x))),
|
|
||||||
status: json["status"] == null
|
|
||||||
? null
|
|
||||||
: TrainDataStatus.fromJson(json["status"]),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
TrainDataGroup get idealGroup {
|
||||||
"date": date,
|
var result = groups.first;
|
||||||
"number": number,
|
|
||||||
"operator": operator,
|
for (final group in groups) {
|
||||||
"rank": rank,
|
if (result.stations.map((s) => s.linkName).toSet().difference(group.stations.map((s) => s.linkName).toSet()).isEmpty) {
|
||||||
"route": route.toJson(),
|
result = group;
|
||||||
"stations": List<dynamic>.from(stations.map((x) => x.toJson())),
|
}
|
||||||
"status": status?.toJson(),
|
}
|
||||||
};
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TrainDataStation> get stations => idealGroup.stations;
|
||||||
|
TrainDataRoute get route => idealGroup.route;
|
||||||
|
TrainDataStatus? get status => idealGroup.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class TrainDataGroup with _$TrainDataGroup {
|
||||||
|
const factory TrainDataGroup({
|
||||||
|
required TrainDataRoute route,
|
||||||
|
required List<TrainDataStation> stations,
|
||||||
|
TrainDataStatus? status,
|
||||||
|
}) = _TrainDataGroup;
|
||||||
|
|
||||||
|
factory TrainDataGroup.fromJson(Map<String, dynamic> json) => _$TrainDataGroupFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Route of the train
|
/// Route of the train
|
||||||
class Route {
|
@freezed
|
||||||
Route({
|
class TrainDataRoute with _$TrainDataRoute {
|
||||||
required this.from,
|
const factory TrainDataRoute({
|
||||||
required this.to,
|
required String from,
|
||||||
});
|
required String to,
|
||||||
|
}) = _TrainDataRoute;
|
||||||
|
|
||||||
final String from;
|
factory TrainDataRoute.fromJson(Map<String, dynamic> json) => _$TrainDataRouteFromJson(json);
|
||||||
final String to;
|
|
||||||
|
|
||||||
factory Route.fromJson(Map<String, dynamic> json) => Route(
|
|
||||||
from: json["from"],
|
|
||||||
to: json["to"],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
"from": from,
|
|
||||||
"to": to,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Station {
|
@freezed
|
||||||
Station({
|
class TrainDataStation with _$TrainDataStation {
|
||||||
this.arrival,
|
const factory TrainDataStation({
|
||||||
this.departure,
|
required String name,
|
||||||
required this.km,
|
required String linkName,
|
||||||
required this.name,
|
required int km,
|
||||||
this.platform,
|
int? stoppingTime,
|
||||||
this.stoppingTime,
|
String? platform,
|
||||||
});
|
StationArrDepTime? arrival,
|
||||||
|
StationArrDepTime? departure,
|
||||||
|
@TrainDataNoteConverter()
|
||||||
|
required List<TrainDataNote> notes,
|
||||||
|
}) = _TrainDataStation;
|
||||||
|
|
||||||
final StationArrDepTime? arrival;
|
factory TrainDataStation.fromJson(Map<String, dynamic> json) => _$TrainDataStationFromJson(json);
|
||||||
final StationArrDepTime? departure;
|
|
||||||
final int km;
|
|
||||||
final String name;
|
|
||||||
final String? platform;
|
|
||||||
final int? stoppingTime;
|
|
||||||
|
|
||||||
factory Station.fromJson(Map<String, dynamic> json) => Station(
|
|
||||||
arrival: json["arrival"] == null
|
|
||||||
? null
|
|
||||||
: StationArrDepTime.fromJson(json["arrival"]),
|
|
||||||
departure: json["departure"] == null
|
|
||||||
? null
|
|
||||||
: StationArrDepTime.fromJson(json["departure"]),
|
|
||||||
km: json["km"],
|
|
||||||
name: json["name"],
|
|
||||||
platform: json["platform"],
|
|
||||||
stoppingTime:
|
|
||||||
json["stoppingTime"],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
"arrival": arrival?.toJson(),
|
|
||||||
"departure": departure?.toJson(),
|
|
||||||
"km": km,
|
|
||||||
"name": name,
|
|
||||||
"platform": platform,
|
|
||||||
"stoppingTime": stoppingTime,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StationArrDepTime {
|
@freezed
|
||||||
StationArrDepTime({
|
class StationArrDepTime with _$StationArrDepTime {
|
||||||
required this.scheduleTime,
|
const factory StationArrDepTime({
|
||||||
this.status,
|
required DateTime scheduleTime,
|
||||||
});
|
StationArrDepTimeStatus? status,
|
||||||
|
}) = _StationArrDepTime;
|
||||||
|
|
||||||
final DateTime scheduleTime;
|
factory StationArrDepTime.fromJson(Map<String, dynamic> json) => _$StationArrDepTimeFromJson(json);
|
||||||
final StationArrDepTimeStatus? status;
|
|
||||||
|
|
||||||
factory StationArrDepTime.fromJson(Map<String, dynamic> json) =>
|
|
||||||
StationArrDepTime(
|
|
||||||
scheduleTime: DateTime.parse(json["scheduleTime"] as String),
|
|
||||||
status: json["status"] == null ? null : StationArrDepTimeStatus.fromJson(json["status"]),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
"scheduleTime": scheduleTime.toIso8601String(),
|
|
||||||
"status": status?.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StationArrDepTimeStatus {
|
@freezed
|
||||||
StationArrDepTimeStatus({
|
class StationArrDepTimeStatus with _$StationArrDepTimeStatus {
|
||||||
required this.delay,
|
const factory StationArrDepTimeStatus({
|
||||||
required this.real,
|
required int delay,
|
||||||
});
|
required bool real,
|
||||||
|
required bool cancelled,
|
||||||
|
}) = _StationArrDepTimeStatus;
|
||||||
|
|
||||||
final int delay;
|
factory StationArrDepTimeStatus.fromJson(Map<String, dynamic> json) => _$StationArrDepTimeStatusFromJson(json);
|
||||||
final bool real;
|
|
||||||
|
|
||||||
factory StationArrDepTimeStatus.fromJson(Map<String, dynamic> json) =>
|
|
||||||
StationArrDepTimeStatus(
|
|
||||||
delay: json["delay"],
|
|
||||||
real: json["real"],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
"delay": delay,
|
|
||||||
"real": real,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TrainDataStatus {
|
@freezed
|
||||||
TrainDataStatus({
|
class TrainDataStatus with _$TrainDataStatus {
|
||||||
required this.delay,
|
const TrainDataStatus._();
|
||||||
required this.state,
|
|
||||||
required this.station,
|
|
||||||
});
|
|
||||||
|
|
||||||
final int delay;
|
const factory TrainDataStatus({
|
||||||
final State state;
|
required int delay,
|
||||||
final String station;
|
required String station,
|
||||||
|
required TrainDataStatusState state,
|
||||||
|
}) = _TrainDataStatus;
|
||||||
|
|
||||||
factory TrainDataStatus.fromJson(Map<String, dynamic> json) =>
|
factory TrainDataStatus.fromJson(Map<String, dynamic> json) => _$TrainDataStatusFromJson(json);
|
||||||
TrainDataStatus(
|
|
||||||
delay: json["delay"],
|
|
||||||
state: stateValues.map[json["state"]]!,
|
|
||||||
station: json["station"],
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
"delay": delay,
|
|
||||||
"state": stateValues.reverse[state],
|
|
||||||
"station": station,
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
@ -190,38 +127,118 @@ class TrainDataStatus {
|
||||||
result += '${delay.abs()} min';
|
result += '${delay.abs()} min';
|
||||||
}
|
}
|
||||||
result += ' la ';
|
result += ' la ';
|
||||||
switch (state) {
|
result += switch (state) {
|
||||||
case State.PASSING:
|
TrainDataStatusState.passing => 'trecerea fără oprire prin',
|
||||||
result += 'trecerea fără oprire prin';
|
TrainDataStatusState.arrival => 'sosirea în',
|
||||||
break;
|
TrainDataStatusState.departure => 'plecarea din',
|
||||||
case State.ARRIVAL:
|
};
|
||||||
result += 'sosirea în';
|
|
||||||
break;
|
|
||||||
case State.DEPARTURE:
|
|
||||||
result += 'plecarea din';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
result += station;
|
result += station;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State { PASSING, ARRIVAL, DEPARTURE }
|
enum TrainDataStatusState { passing, arrival, departure }
|
||||||
|
|
||||||
final stateValues = EnumValues({
|
abstract class TrainDataNote {
|
||||||
"arrival": State.ARRIVAL,
|
final String kind;
|
||||||
"departure": State.DEPARTURE,
|
|
||||||
"passing": State.PASSING
|
|
||||||
});
|
|
||||||
|
|
||||||
class EnumValues<T> {
|
const TrainDataNote({required this.kind});
|
||||||
Map<String, T> map;
|
|
||||||
Map<T, String>? reverseMap;
|
|
||||||
|
|
||||||
EnumValues(this.map);
|
Map<String, dynamic> toJson() => {
|
||||||
|
"kind": kind,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Map<T, String> get reverse {
|
class TrainDataNoteConverter implements JsonConverter<TrainDataNote, Map<String, dynamic>> {
|
||||||
reverseMap ??= map.map((k, v) => MapEntry(v, k));
|
const TrainDataNoteConverter();
|
||||||
return reverseMap!;
|
|
||||||
|
@override
|
||||||
|
TrainDataNote fromJson(Map<String, dynamic> json) {
|
||||||
|
return switch(json['kind']) {
|
||||||
|
'trainNumberChange' => TrainDataNoteTrainNumberChange.fromJson(json),
|
||||||
|
'departsAs' => TrainDataNoteDepartsAs.fromJson(json),
|
||||||
|
'detachingWagons' => TrainDataNoteDetachingWagons.fromJson(json),
|
||||||
|
'receivingWagons' => TrainDataNoteReceivingWagons.fromJson(json),
|
||||||
|
_ => TrainDataNoteUnknown.fromJson(json),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson(TrainDataNote object) {
|
||||||
|
return object.toJson();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class TrainDataNoteTrainNumberChange with _$TrainDataNoteTrainNumberChange implements TrainDataNote {
|
||||||
|
@Implements<TrainDataNote>()
|
||||||
|
const factory TrainDataNoteTrainNumberChange({
|
||||||
|
// base
|
||||||
|
@Default("trainNumberChange")
|
||||||
|
String kind,
|
||||||
|
// impl
|
||||||
|
required String rank,
|
||||||
|
required String number,
|
||||||
|
}) = _TrainDataNoteTrainNumberChange;
|
||||||
|
|
||||||
|
factory TrainDataNoteTrainNumberChange.fromJson(Map<String, dynamic> json) => _$TrainDataNoteTrainNumberChangeFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class TrainDataNoteDepartsAs with _$TrainDataNoteDepartsAs implements TrainDataNote {
|
||||||
|
@Implements<TrainDataNote>()
|
||||||
|
const factory TrainDataNoteDepartsAs({
|
||||||
|
// base
|
||||||
|
@Default("departsAs")
|
||||||
|
String kind,
|
||||||
|
// impl
|
||||||
|
required String rank,
|
||||||
|
required String number,
|
||||||
|
required DateTime departureDate,
|
||||||
|
}) = _TrainDataNoteDepartsAs;
|
||||||
|
|
||||||
|
factory TrainDataNoteDepartsAs.fromJson(Map<String, dynamic> json) => _$TrainDataNoteDepartsAsFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class TrainDataNoteDetachingWagons with _$TrainDataNoteDetachingWagons implements TrainDataNote {
|
||||||
|
@Implements<TrainDataNote>()
|
||||||
|
const factory TrainDataNoteDetachingWagons({
|
||||||
|
// base
|
||||||
|
@Default("detachingWagons")
|
||||||
|
String kind,
|
||||||
|
// impl
|
||||||
|
required String station,
|
||||||
|
}) = _TrainDataNoteDetachingWagons;
|
||||||
|
|
||||||
|
factory TrainDataNoteDetachingWagons.fromJson(Map<String, dynamic> json) => _$TrainDataNoteDetachingWagonsFromJson(json);
|
||||||
|
}
|
||||||
|
@freezed
|
||||||
|
class TrainDataNoteReceivingWagons with _$TrainDataNoteReceivingWagons implements TrainDataNote {
|
||||||
|
@Implements<TrainDataNote>()
|
||||||
|
const factory TrainDataNoteReceivingWagons({
|
||||||
|
// base
|
||||||
|
@Default("receivingWagons")
|
||||||
|
String kind,
|
||||||
|
// impl
|
||||||
|
required String station,
|
||||||
|
}) = _TrainDataNoteReceivingWagons;
|
||||||
|
|
||||||
|
factory TrainDataNoteReceivingWagons.fromJson(Map<String, dynamic> json) => _$TrainDataNoteReceivingWagonsFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class TrainDataNoteUnknown with _$TrainDataNoteUnknown implements TrainDataNote {
|
||||||
|
@Implements<TrainDataNote>()
|
||||||
|
const factory TrainDataNoteUnknown({
|
||||||
|
// base
|
||||||
|
required String kind,
|
||||||
|
// impl
|
||||||
|
required Map<String, dynamic> extra,
|
||||||
|
}) = _TrainDataNoteUnknown;
|
||||||
|
|
||||||
|
factory TrainDataNoteUnknown.fromJson(Map<String, dynamic> json) => TrainDataNoteUnknown(
|
||||||
|
kind: json['kind'],
|
||||||
|
extra: Map.from(json)..remove('kind'),
|
||||||
|
);
|
||||||
|
}
|
2365
lib/models/train_data.freezed.dart
Normal file
2365
lib/models/train_data.freezed.dart
Normal file
File diff suppressed because it is too large
Load diff
217
lib/models/train_data.g.dart
Normal file
217
lib/models/train_data.g.dart
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'train_data.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$_TrainData _$$_TrainDataFromJson(Map<String, dynamic> json) => _$_TrainData(
|
||||||
|
rank: json['rank'] as String,
|
||||||
|
number: json['number'] as String,
|
||||||
|
date: json['date'] as String,
|
||||||
|
operator: json['operator'] as String,
|
||||||
|
groups: (json['groups'] as List<dynamic>)
|
||||||
|
.map((e) => TrainDataGroup.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataToJson(_$_TrainData instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'rank': instance.rank,
|
||||||
|
'number': instance.number,
|
||||||
|
'date': instance.date,
|
||||||
|
'operator': instance.operator,
|
||||||
|
'groups': instance.groups,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataGroup _$$_TrainDataGroupFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataGroup(
|
||||||
|
route: TrainDataRoute.fromJson(json['route'] as Map<String, dynamic>),
|
||||||
|
stations: (json['stations'] as List<dynamic>)
|
||||||
|
.map((e) => TrainDataStation.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
status: json['status'] == null
|
||||||
|
? null
|
||||||
|
: TrainDataStatus.fromJson(json['status'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataGroupToJson(_$_TrainDataGroup instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'route': instance.route,
|
||||||
|
'stations': instance.stations,
|
||||||
|
'status': instance.status,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataRoute _$$_TrainDataRouteFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataRoute(
|
||||||
|
from: json['from'] as String,
|
||||||
|
to: json['to'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataRouteToJson(_$_TrainDataRoute instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'from': instance.from,
|
||||||
|
'to': instance.to,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataStation _$$_TrainDataStationFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataStation(
|
||||||
|
name: json['name'] as String,
|
||||||
|
linkName: json['linkName'] as String,
|
||||||
|
km: json['km'] as int,
|
||||||
|
stoppingTime: json['stoppingTime'] as int?,
|
||||||
|
platform: json['platform'] as String?,
|
||||||
|
arrival: json['arrival'] == null
|
||||||
|
? null
|
||||||
|
: StationArrDepTime.fromJson(json['arrival'] as Map<String, dynamic>),
|
||||||
|
departure: json['departure'] == null
|
||||||
|
? null
|
||||||
|
: StationArrDepTime.fromJson(
|
||||||
|
json['departure'] as Map<String, dynamic>),
|
||||||
|
notes: (json['notes'] as List<dynamic>)
|
||||||
|
.map((e) => const TrainDataNoteConverter()
|
||||||
|
.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataStationToJson(_$_TrainDataStation instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'name': instance.name,
|
||||||
|
'linkName': instance.linkName,
|
||||||
|
'km': instance.km,
|
||||||
|
'stoppingTime': instance.stoppingTime,
|
||||||
|
'platform': instance.platform,
|
||||||
|
'arrival': instance.arrival,
|
||||||
|
'departure': instance.departure,
|
||||||
|
'notes':
|
||||||
|
instance.notes.map(const TrainDataNoteConverter().toJson).toList(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_StationArrDepTime _$$_StationArrDepTimeFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$_StationArrDepTime(
|
||||||
|
scheduleTime: DateTime.parse(json['scheduleTime'] as String),
|
||||||
|
status: json['status'] == null
|
||||||
|
? null
|
||||||
|
: StationArrDepTimeStatus.fromJson(
|
||||||
|
json['status'] as Map<String, dynamic>),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_StationArrDepTimeToJson(
|
||||||
|
_$_StationArrDepTime instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'scheduleTime': instance.scheduleTime.toIso8601String(),
|
||||||
|
'status': instance.status,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_StationArrDepTimeStatus _$$_StationArrDepTimeStatusFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$_StationArrDepTimeStatus(
|
||||||
|
delay: json['delay'] as int,
|
||||||
|
real: json['real'] as bool,
|
||||||
|
cancelled: json['cancelled'] as bool,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_StationArrDepTimeStatusToJson(
|
||||||
|
_$_StationArrDepTimeStatus instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'delay': instance.delay,
|
||||||
|
'real': instance.real,
|
||||||
|
'cancelled': instance.cancelled,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataStatus _$$_TrainDataStatusFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataStatus(
|
||||||
|
delay: json['delay'] as int,
|
||||||
|
station: json['station'] as String,
|
||||||
|
state: $enumDecode(_$TrainDataStatusStateEnumMap, json['state']),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataStatusToJson(_$_TrainDataStatus instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'delay': instance.delay,
|
||||||
|
'station': instance.station,
|
||||||
|
'state': _$TrainDataStatusStateEnumMap[instance.state]!,
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$TrainDataStatusStateEnumMap = {
|
||||||
|
TrainDataStatusState.passing: 'passing',
|
||||||
|
TrainDataStatusState.arrival: 'arrival',
|
||||||
|
TrainDataStatusState.departure: 'departure',
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataNoteTrainNumberChange _$$_TrainDataNoteTrainNumberChangeFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataNoteTrainNumberChange(
|
||||||
|
kind: json['kind'] as String? ?? "trainNumberChange",
|
||||||
|
rank: json['rank'] as String,
|
||||||
|
number: json['number'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataNoteTrainNumberChangeToJson(
|
||||||
|
_$_TrainDataNoteTrainNumberChange instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'kind': instance.kind,
|
||||||
|
'rank': instance.rank,
|
||||||
|
'number': instance.number,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataNoteDepartsAs _$$_TrainDataNoteDepartsAsFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataNoteDepartsAs(
|
||||||
|
kind: json['kind'] as String? ?? "departsAs",
|
||||||
|
rank: json['rank'] as String,
|
||||||
|
number: json['number'] as String,
|
||||||
|
departureDate: DateTime.parse(json['departureDate'] as String),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataNoteDepartsAsToJson(
|
||||||
|
_$_TrainDataNoteDepartsAs instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'kind': instance.kind,
|
||||||
|
'rank': instance.rank,
|
||||||
|
'number': instance.number,
|
||||||
|
'departureDate': instance.departureDate.toIso8601String(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataNoteDetachingWagons _$$_TrainDataNoteDetachingWagonsFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataNoteDetachingWagons(
|
||||||
|
kind: json['kind'] as String? ?? "detachingWagons",
|
||||||
|
station: json['station'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataNoteDetachingWagonsToJson(
|
||||||
|
_$_TrainDataNoteDetachingWagons instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'kind': instance.kind,
|
||||||
|
'station': instance.station,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataNoteReceivingWagons _$$_TrainDataNoteReceivingWagonsFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataNoteReceivingWagons(
|
||||||
|
kind: json['kind'] as String? ?? "receivingWagons",
|
||||||
|
station: json['station'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataNoteReceivingWagonsToJson(
|
||||||
|
_$_TrainDataNoteReceivingWagons instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'kind': instance.kind,
|
||||||
|
'station': instance.station,
|
||||||
|
};
|
||||||
|
|
||||||
|
_$_TrainDataNoteUnknown _$$_TrainDataNoteUnknownFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$_TrainDataNoteUnknown(
|
||||||
|
kind: json['kind'] as String,
|
||||||
|
extra: json['extra'] as Map<String, dynamic>,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$_TrainDataNoteUnknownToJson(
|
||||||
|
_$_TrainDataNoteUnknown instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'kind': instance.kind,
|
||||||
|
'extra': instance.extra,
|
||||||
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
// coverage:ignore-file
|
// coverage:ignore-file
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
part of 'trains_result.dart';
|
part of 'trains_result.dart';
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,14 @@ enum UiDesign {
|
||||||
FLUENT,
|
FLUENT,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension UIName on UiDesign {
|
||||||
|
String get userInterfaceName => (const {
|
||||||
|
UiDesign.MATERIAL: 'Material',
|
||||||
|
UiDesign.CUPERTINO: 'Cupertino',
|
||||||
|
UiDesign.FLUENT: 'Fluent',
|
||||||
|
})[this]!;
|
||||||
|
}
|
||||||
|
|
||||||
class UnmatchedUiDesignException implements Exception {
|
class UnmatchedUiDesignException implements Exception {
|
||||||
final UiDesign uiDesign;
|
final UiDesign uiDesign;
|
||||||
|
|
||||||
|
|
94
lib/models/ui_timezone.dart
Normal file
94
lib/models/ui_timezone.dart
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import 'package:timezone/timezone.dart' as tz;
|
||||||
|
|
||||||
|
enum UiTimeZoneType {
|
||||||
|
ro,
|
||||||
|
local,
|
||||||
|
utc,
|
||||||
|
iana,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UITimeZoneTypeName on UiTimeZoneType {
|
||||||
|
String get userInterfaceName => (const {
|
||||||
|
UiTimeZoneType.iana: 'Fus orar IANA',
|
||||||
|
UiTimeZoneType.local: 'Local',
|
||||||
|
UiTimeZoneType.ro: 'România',
|
||||||
|
UiTimeZoneType.utc: 'UTC',
|
||||||
|
})[this]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Map<UiTimeZoneType, UiTimeZone Function(String)> fromSerStringConstructors = {
|
||||||
|
UiTimeZoneType.ro: RoUiTimeZone.fromSerString,
|
||||||
|
UiTimeZoneType.local: LocalUiTimeZone.fromSerString,
|
||||||
|
UiTimeZoneType.utc: UtcUiTimeZone.fromSerString,
|
||||||
|
UiTimeZoneType.iana: IanaUiTimeZone.fromSerString,
|
||||||
|
};
|
||||||
|
|
||||||
|
abstract class UiTimeZone {
|
||||||
|
final UiTimeZoneType type;
|
||||||
|
|
||||||
|
const UiTimeZone({required this.type});
|
||||||
|
|
||||||
|
DateTime convertDateTime(DateTime dt);
|
||||||
|
|
||||||
|
factory UiTimeZone.fromSerString(String ser) {
|
||||||
|
final arr = ser.split('\n');
|
||||||
|
return fromSerStringConstructors.map((key, value) => MapEntry(key.name, value))[arr[0]]!(ser);
|
||||||
|
}
|
||||||
|
|
||||||
|
String toSerString() {
|
||||||
|
return '${type.name}\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RoUiTimeZone extends UiTimeZone {
|
||||||
|
static final roTz = tz.getLocation('Europe/Bucharest');
|
||||||
|
|
||||||
|
const RoUiTimeZone() : super(type: UiTimeZoneType.ro);
|
||||||
|
|
||||||
|
factory RoUiTimeZone.fromSerString(String ser) => const RoUiTimeZone();
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime convertDateTime(DateTime dt) {
|
||||||
|
return tz.TZDateTime.from(dt, roTz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocalUiTimeZone extends UiTimeZone {
|
||||||
|
const LocalUiTimeZone() : super(type: UiTimeZoneType.local);
|
||||||
|
|
||||||
|
factory LocalUiTimeZone.fromSerString(String ser) => LocalUiTimeZone();
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime convertDateTime(DateTime dt) => dt.toLocal();
|
||||||
|
}
|
||||||
|
|
||||||
|
class UtcUiTimeZone extends UiTimeZone {
|
||||||
|
const UtcUiTimeZone() : super(type: UiTimeZoneType.utc);
|
||||||
|
|
||||||
|
factory UtcUiTimeZone.fromSerString(String ser) => UtcUiTimeZone();
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime convertDateTime(DateTime dt) => dt.toUtc();
|
||||||
|
}
|
||||||
|
|
||||||
|
class IanaUiTimeZone extends UiTimeZone {
|
||||||
|
late final tz.Location location;
|
||||||
|
|
||||||
|
IanaUiTimeZone({required String ianaName}): super(type: UiTimeZoneType.iana) {
|
||||||
|
location = tz.getLocation(ianaName);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory IanaUiTimeZone.fromSerString(String ser) => IanaUiTimeZone(
|
||||||
|
ianaName: ser.split('\n').skip(1).join('\n'),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
DateTime convertDateTime(DateTime dt) {
|
||||||
|
return tz.TZDateTime.from(dt, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toSerString() {
|
||||||
|
return '${type.name}\n${location.name}';
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/api/releases.dart';
|
import 'package:info_tren/api/releases.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/about/about_page_cupertino.dart';
|
import 'package:info_tren/pages/about/about_page_cupertino.dart';
|
||||||
|
import 'package:info_tren/pages/about/about_page_fluent.dart';
|
||||||
import 'package:info_tren/pages/about/about_page_material.dart';
|
import 'package:info_tren/pages/about/about_page_material.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
@ -21,6 +22,8 @@ class AboutPage extends ConsumerWidget {
|
||||||
return const AboutPageMaterial();
|
return const AboutPageMaterial();
|
||||||
case UiDesign.CUPERTINO:
|
case UiDesign.CUPERTINO:
|
||||||
return const AboutPageCupertino();
|
return const AboutPageCupertino();
|
||||||
|
case UiDesign.FLUENT:
|
||||||
|
return const AboutPageFluent();
|
||||||
default:
|
default:
|
||||||
throw UnmatchedUiDesignException(uiDesign);
|
throw UnmatchedUiDesignException(uiDesign);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +34,7 @@ abstract class AboutPageShared extends StatefulWidget {
|
||||||
const AboutPageShared({super.key});
|
const AboutPageShared({super.key});
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class AboutPageState extends State<AboutPageShared> {
|
abstract class AboutPageState<T extends AboutPageShared> extends State<T> {
|
||||||
static const String download = String.fromEnvironment('DOWNLOAD');
|
static const String download = String.fromEnvironment('DOWNLOAD');
|
||||||
|
|
||||||
final String pageTitle = 'Despre aplicație';
|
final String pageTitle = 'Despre aplicație';
|
||||||
|
|
|
@ -3,14 +3,14 @@ import 'package:info_tren/components/cupertino_divider.dart';
|
||||||
import 'package:info_tren/pages/about/about_page.dart';
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class AboutPageCupertino extends StatefulWidget {
|
class AboutPageCupertino extends AboutPageShared {
|
||||||
const AboutPageCupertino({super.key});
|
const AboutPageCupertino({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AboutPageShared> createState() => AboutPageStateCupertino();
|
State<AboutPageShared> createState() => AboutPageStateCupertino();
|
||||||
}
|
}
|
||||||
|
|
||||||
class AboutPageStateCupertino extends AboutPageState {
|
class AboutPageStateCupertino extends AboutPageState<AboutPageCupertino> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CupertinoPageScaffold(
|
return CupertinoPageScaffold(
|
||||||
|
|
198
lib/pages/about/about_page_fluent.dart
Normal file
198
lib/pages/about/about_page_fluent.dart
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class AboutPageFluent extends AboutPageShared {
|
||||||
|
const AboutPageFluent({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AboutPageShared> createState() => AboutPageStateFluent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AboutPageStateFluent extends AboutPageState<AboutPageFluent> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return NavigationView(
|
||||||
|
appBar: NavigationAppBar(
|
||||||
|
title: Text(pageTitle),
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
'Info Tren',
|
||||||
|
style: FluentTheme.of(context).typography.display,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (packageInfo != null)
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
packageInfo!.packageName,
|
||||||
|
style: FluentTheme.of(context).typography.caption,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// ListTile(
|
||||||
|
// title: Text(versionTitleText),
|
||||||
|
// subtitle: localChangelog.isEmpty ? null : Text(localChangelog.first.title),
|
||||||
|
// ),
|
||||||
|
const Divider(),
|
||||||
|
for (final log in mergedChangelogs) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(8, 8, 8, 0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
log.version.toString(),
|
||||||
|
style: FluentTheme.of(context).typography.title,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (localChangelog.isNotEmpty && log.version == localChangelog.first.version)
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: FluentTheme.of(context).inactiveColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Text(
|
||||||
|
currentVersionText,
|
||||||
|
style: const TextStyle(
|
||||||
|
inherit: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (remoteChangelog.isNotEmpty && log.version == remoteChangelog.first.version && (localChangelog.isEmpty || localChangelog.first.version != log.version))
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.green,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Text(
|
||||||
|
latestVersionText,
|
||||||
|
style: TextStyle(
|
||||||
|
inherit: true,
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (AboutPageState.download == 'apk' && log.apkLink != null)
|
||||||
|
GestureDetector(
|
||||||
|
onSecondaryTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.apkLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.apkLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
launchUrl(
|
||||||
|
log.apkLink!,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
child: const Tooltip(
|
||||||
|
message: 'Download APK',
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
child: Icon(FluentIcons.download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (AboutPageState.download == 'linux' && log.linuxLink != null)
|
||||||
|
GestureDetector(
|
||||||
|
onSecondaryTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.linuxLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.linuxLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
launchUrl(
|
||||||
|
log.linuxLink!,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
child: const Tooltip(
|
||||||
|
message: 'Download Linux ZIP',
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
child: Icon(FluentIcons.download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (AboutPageState.download == 'windows' && log.windowsLink != null)
|
||||||
|
GestureDetector(
|
||||||
|
onSecondaryTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.windowsLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.windowsLink!.toString()));
|
||||||
|
// ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
// content: Text('Link copied to clipboard'),
|
||||||
|
// ));
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
launchUrl(
|
||||||
|
log.windowsLink!,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
child: const Tooltip(
|
||||||
|
message: 'Download Windows App ZIP',
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
child: Icon(FluentIcons.download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
text: log.description,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,14 +3,14 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:info_tren/pages/about/about_page.dart';
|
import 'package:info_tren/pages/about/about_page.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class AboutPageMaterial extends StatefulWidget {
|
class AboutPageMaterial extends AboutPageShared {
|
||||||
const AboutPageMaterial({super.key});
|
const AboutPageMaterial({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AboutPageShared> createState() => AboutPageStateMaterial();
|
State<AboutPageShared> createState() => AboutPageStateMaterial();
|
||||||
}
|
}
|
||||||
|
|
||||||
class AboutPageStateMaterial extends AboutPageState {
|
class AboutPageStateMaterial extends AboutPageState<AboutPageMaterial> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -121,6 +121,64 @@ class AboutPageStateMaterial extends AboutPageState {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (AboutPageState.download == 'linux' && log.linuxLink != null)
|
||||||
|
GestureDetector(
|
||||||
|
onSecondaryTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.linuxLink!.toString()));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
content: Text('Link copied to clipboard'),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.linuxLink!.toString()));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
content: Text('Link copied to clipboard'),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
launchUrl(
|
||||||
|
log.linuxLink!,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
child: const Tooltip(
|
||||||
|
message: 'Download Linux ZIP',
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
child: Icon(Icons.download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (AboutPageState.download == 'windows' && log.windowsLink != null)
|
||||||
|
GestureDetector(
|
||||||
|
onSecondaryTap: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.windowsLink!.toString()));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
content: Text('Link copied to clipboard'),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
Clipboard.setData(ClipboardData(text: log.windowsLink!.toString()));
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||||
|
content: Text('Link copied to clipboard'),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
launchUrl(
|
||||||
|
log.windowsLink!,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
child: const Tooltip(
|
||||||
|
message: 'Download Windows App ZIP',
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
child: Icon(Icons.download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:info_tren/pages/about/about_page.dart';
|
||||||
import 'package:info_tren/pages/main/main_page_cupertino.dart';
|
import 'package:info_tren/pages/main/main_page_cupertino.dart';
|
||||||
import 'package:info_tren/pages/main/main_page_fluent.dart';
|
import 'package:info_tren/pages/main/main_page_fluent.dart';
|
||||||
import 'package:info_tren/pages/main/main_page_material.dart';
|
import 'package:info_tren/pages/main/main_page_material.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
|
@ -36,6 +37,12 @@ abstract class MainPageShared extends StatelessWidget {
|
||||||
const MainPageShared({super.key});
|
const MainPageShared({super.key});
|
||||||
|
|
||||||
List<MainPageAction> get popupMenu => [
|
List<MainPageAction> get popupMenu => [
|
||||||
|
MainPageAction(
|
||||||
|
name: 'Setări',
|
||||||
|
action: (context) {
|
||||||
|
Navigator.of(context).pushNamed(SettingsPage.routeName);
|
||||||
|
},
|
||||||
|
),
|
||||||
MainPageAction(
|
MainPageAction(
|
||||||
name: 'Despre aplicație',
|
name: 'Despre aplicație',
|
||||||
action: (context) {
|
action: (context) {
|
||||||
|
@ -85,5 +92,9 @@ class MainPageAction {
|
||||||
final String? description;
|
final String? description;
|
||||||
final void Function(BuildContext context)? action;
|
final void Function(BuildContext context)? action;
|
||||||
|
|
||||||
MainPageAction({required this.name, this.action, this.description});
|
MainPageAction({
|
||||||
|
required this.name,
|
||||||
|
this.action,
|
||||||
|
this.description,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,22 @@ class MainPageFluent extends MainPageShared {
|
||||||
appBar: NavigationAppBar(
|
appBar: NavigationAppBar(
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
title: Text(pageTitle),
|
title: Text(pageTitle),
|
||||||
|
actions: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: popupMenu.map((i) => Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon({
|
||||||
|
'Setări': FluentIcons.settings,
|
||||||
|
'Despre aplicație': FluentIcons.info,
|
||||||
|
}[i.name]),
|
||||||
|
onPressed: i.action != null ? () => i.action!(context) : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)).toList(),
|
||||||
|
),
|
||||||
// centerTitle: true,
|
// centerTitle: true,
|
||||||
// actions: [
|
// actions: [
|
||||||
// PopupMenuButton<int>(
|
// PopupMenuButton<int>(
|
||||||
|
|
38
lib/pages/settings/setings_page.dart
Normal file
38
lib/pages/settings/setings_page.dart
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/settings_page_cupertino.dart';
|
||||||
|
import 'package:info_tren/pages/settings/settings_page_fluent.dart';
|
||||||
|
import 'package:info_tren/pages/settings/settings_page_material.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPage extends ConsumerWidget {
|
||||||
|
const SettingsPage({super.key,});
|
||||||
|
|
||||||
|
static const String routeName = '/settings';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final uiDesign = ref.watch(uiDesignProvider);
|
||||||
|
|
||||||
|
switch (uiDesign) {
|
||||||
|
case UiDesign.MATERIAL:
|
||||||
|
return const SettingsPageMaterial();
|
||||||
|
case UiDesign.CUPERTINO:
|
||||||
|
return const SettingsPageCupertino();
|
||||||
|
case UiDesign.FLUENT:
|
||||||
|
return const SettingsPageFluent();
|
||||||
|
default:
|
||||||
|
throw UnmatchedUiDesignException(uiDesign);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SettingsPageShared extends StatelessWidget {
|
||||||
|
final String pageTitle = 'Setări';
|
||||||
|
final String appearanceTitle = 'Aspect';
|
||||||
|
final String timeZoneTitle = 'Fus orar';
|
||||||
|
|
||||||
|
const SettingsPageShared({super.key});
|
||||||
|
|
||||||
|
}
|
96
lib/pages/settings/settings_page_cupertino.dart
Normal file
96
lib/pages/settings/settings_page_cupertino.dart
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPageCupertino extends SettingsPageShared {
|
||||||
|
const SettingsPageCupertino({super.key,});
|
||||||
|
|
||||||
|
Future<T?> singleChoice<T>({required BuildContext context, required List<T> choices, required String Function(T) labelBuilder, String? title}) async {
|
||||||
|
return await showCupertinoModalPopup<T>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return CupertinoActionSheet(
|
||||||
|
title: title != null ? Text(title) : null,
|
||||||
|
actions: choices.map((c) => CupertinoActionSheetAction(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(c);
|
||||||
|
},
|
||||||
|
child: Text(labelBuilder(c)),
|
||||||
|
)).toList(),
|
||||||
|
cancelButton: CupertinoActionSheetAction(
|
||||||
|
child: const Text('Anulare'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CupertinoPageScaffold(
|
||||||
|
navigationBar: CupertinoNavigationBar(
|
||||||
|
previousPageTitle: 'Info Tren',
|
||||||
|
middle: Text(pageTitle),
|
||||||
|
),
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final mq = MediaQuery.of(context);
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: mq.padding.top,
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||||
|
return CupertinoListTile(
|
||||||
|
title: Text(appearanceTitle),
|
||||||
|
trailing: Text(currentUiDesign.userInterfaceName),
|
||||||
|
onTap: () async {
|
||||||
|
final choice = await singleChoice(
|
||||||
|
context: context,
|
||||||
|
choices: UiDesign.values,
|
||||||
|
labelBuilder: (UiDesign ud) => ud.userInterfaceName,
|
||||||
|
title: appearanceTitle,
|
||||||
|
);
|
||||||
|
if (choice != null) {
|
||||||
|
ref.read(uiDesignProvider.notifier).set(choice);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentTZ = ref.watch(uiTimeZoneProvider);
|
||||||
|
return CupertinoListTile(
|
||||||
|
title: Text(timeZoneTitle),
|
||||||
|
trailing: Text(currentTZ.type.userInterfaceName),
|
||||||
|
onTap: () async {
|
||||||
|
final choice = await singleChoice(
|
||||||
|
context: context,
|
||||||
|
choices: UiTimeZoneType.values.where((tz) => tz != UiTimeZoneType.iana).toList(),
|
||||||
|
labelBuilder: (UiTimeZoneType utzt) => utzt.userInterfaceName,
|
||||||
|
title: timeZoneTitle,
|
||||||
|
);
|
||||||
|
if (choice != null) {
|
||||||
|
ref.read(uiTimeZoneProvider.notifier).set(UiTimeZone.fromSerString('${choice.name}\n'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
65
lib/pages/settings/settings_page_fluent.dart
Normal file
65
lib/pages/settings/settings_page_fluent.dart
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPageFluent extends SettingsPageShared {
|
||||||
|
const SettingsPageFluent({super.key,});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return NavigationView(
|
||||||
|
appBar: NavigationAppBar(
|
||||||
|
title: Text(pageTitle),
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(appearanceTitle),
|
||||||
|
trailing: ComboBox<UiDesign>(
|
||||||
|
items: UiDesign.values.map((d) => ComboBoxItem(
|
||||||
|
value: d,
|
||||||
|
child: Text(d.userInterfaceName),
|
||||||
|
)).toList(),
|
||||||
|
value: currentUiDesign,
|
||||||
|
onChanged: (newUiDesign) {
|
||||||
|
ref.read(uiDesignProvider.notifier).set(newUiDesign);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentTZ = ref.watch(uiTimeZoneProvider);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(timeZoneTitle),
|
||||||
|
trailing: ComboBox<UiTimeZoneType>(
|
||||||
|
items: UiTimeZoneType.values.where((tz) => tz != UiTimeZoneType.iana).map((tzt) => ComboBoxItem(
|
||||||
|
value: tzt,
|
||||||
|
child: Text(tzt.userInterfaceName),
|
||||||
|
)).toList(),
|
||||||
|
value: currentTZ.type,
|
||||||
|
onChanged: (newTZ) {
|
||||||
|
if (newTZ != null) {
|
||||||
|
ref.read(uiTimeZoneProvider.notifier).set(UiTimeZone.fromSerString('${newTZ.name}\n'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ref.read(uiTimeZoneProvider.notifier).set(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
66
lib/pages/settings/settings_page_material.dart
Normal file
66
lib/pages/settings/settings_page_material.dart
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/pages/settings/setings_page.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class SettingsPageMaterial extends SettingsPageShared {
|
||||||
|
const SettingsPageMaterial({super.key,});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(pageTitle),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentUiDesign = ref.watch(uiDesignProvider);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(appearanceTitle),
|
||||||
|
trailing: DropdownButton<UiDesign>(
|
||||||
|
items: UiDesign.values.map((d) => DropdownMenuItem(
|
||||||
|
value: d,
|
||||||
|
child: Text(d.userInterfaceName),
|
||||||
|
)).toList(),
|
||||||
|
value: currentUiDesign,
|
||||||
|
onChanged: (newUiDesign) {
|
||||||
|
ref.read(uiDesignProvider.notifier).set(newUiDesign);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final currentTZ = ref.watch(uiTimeZoneProvider);
|
||||||
|
return ListTile(
|
||||||
|
title: Text(timeZoneTitle),
|
||||||
|
trailing: DropdownButton<UiTimeZoneType>(
|
||||||
|
items: UiTimeZoneType.values.where((tz) => tz != UiTimeZoneType.iana).map((tzt) => DropdownMenuItem(
|
||||||
|
value: tzt,
|
||||||
|
child: Text(tzt.userInterfaceName),
|
||||||
|
)).toList(),
|
||||||
|
value: currentTZ.type,
|
||||||
|
onChanged: (newTZ) {
|
||||||
|
if (newTZ != null) {
|
||||||
|
ref.read(uiTimeZoneProvider.notifier).set(UiTimeZone.fromSerString('${newTZ.name}\n'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ref.read(uiTimeZoneProvider.notifier).set(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,8 +43,9 @@ class ViewStationPage extends HookConsumerWidget {
|
||||||
|
|
||||||
class ViewStationArguments {
|
class ViewStationArguments {
|
||||||
final String stationName;
|
final String stationName;
|
||||||
|
final DateTime? date;
|
||||||
|
|
||||||
const ViewStationArguments({required this.stationName});
|
const ViewStationArguments({required this.stationName, this.date});
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ViewStationPageShared extends StatelessWidget {
|
abstract class ViewStationPageShared extends StatelessWidget {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/components/loading/loading.dart';
|
import 'package:info_tren/components/loading/loading.dart';
|
||||||
import 'package:info_tren/components/refresh_future_builder.dart';
|
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||||
import 'package:info_tren/components/sliver_persistent_header_padding.dart';
|
import 'package:info_tren/components/sliver_persistent_header_padding.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
|
@ -66,18 +67,7 @@ class ViewStationPageCupertino extends ViewStationPageShared {
|
||||||
onTap: () => onTrainTapped(context, item.train),
|
onTap: () => onTrainTapped(context, item.train),
|
||||||
child: CupertinoFormRow(
|
child: CupertinoFormRow(
|
||||||
prefix: Text.rich(
|
prefix: Text.rich(
|
||||||
TextSpan(
|
trainIdSpan(rank: item.train.rank, number: item.train.number),
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: item.train.rank,
|
|
||||||
style: TextStyle(
|
|
||||||
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
TextSpan(text: item.train.number,),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
helper: Text.rich(
|
helper: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
|
@ -88,7 +78,13 @@ class ViewStationPageCupertino extends ViewStationPageShared {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'),
|
child: Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
|
final time = tz.convertDateTime(item.time);
|
||||||
|
return Text('${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}');
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -99,18 +95,7 @@ class ViewStationPageCupertino extends ViewStationPageShared {
|
||||||
onTap: () => onTrainTapped(context, item.train),
|
onTap: () => onTrainTapped(context, item.train),
|
||||||
child: CupertinoFormRow(
|
child: CupertinoFormRow(
|
||||||
prefix: Text.rich(
|
prefix: Text.rich(
|
||||||
TextSpan(
|
trainIdSpan(rank: item.train.rank, number: item.train.number),
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: item.train.rank,
|
|
||||||
style: TextStyle(
|
|
||||||
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
TextSpan(text: item.train.number,),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
helper: Text.rich(
|
helper: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
|
@ -121,7 +106,13 @@ class ViewStationPageCupertino extends ViewStationPageShared {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'),
|
child: Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
|
final time = tz.convertDateTime(item.time);
|
||||||
|
return Text('${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}');
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/components/badge/badge.dart';
|
import 'package:info_tren/components/badge/badge.dart';
|
||||||
import 'package:info_tren/components/loading/loading.dart';
|
import 'package:info_tren/components/loading/loading.dart';
|
||||||
import 'package:info_tren/components/refresh_future_builder.dart';
|
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
|
@ -141,8 +142,12 @@ class ViewStationPageFluent extends ViewStationPageShared {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Consumer(
|
||||||
'${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}',
|
builder: (context, ref, _) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
|
final time = tz.convertDateTime(item.time);
|
||||||
|
return Text(
|
||||||
|
'${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
inherit: true,
|
inherit: true,
|
||||||
fontFeatures: const [
|
fontFeatures: const [
|
||||||
|
@ -151,14 +156,17 @@ class ViewStationPageFluent extends ViewStationPageShared {
|
||||||
decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
|
decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
|
||||||
fontSize: item.status.delay != 0 ? 12 : null,
|
fontSize: item.status.delay != 0 ? 12 : null,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
if (item.status.delay != 0) Builder(
|
if (item.status.delay != 0) Consumer(
|
||||||
builder: (context) {
|
builder: (context, ref, _) {
|
||||||
final newTime = item.time.add(Duration(minutes: item.status.delay));
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
|
final newTime = tz.convertDateTime(item.time.add(Duration(minutes: item.status.delay)));
|
||||||
final delay = item.status.delay > 0;
|
final delay = item.status.delay > 0;
|
||||||
|
|
||||||
return Text(
|
return Text(
|
||||||
'${newTime.toLocal().hour.toString().padLeft(2, '0')}:${newTime.toLocal().minute.toString().padLeft(2, '0')}',
|
'${newTime.hour.toString().padLeft(2, '0')}:${newTime.minute.toString().padLeft(2, '0')}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
inherit: true,
|
inherit: true,
|
||||||
fontFeatures: const [
|
fontFeatures: const [
|
||||||
|
@ -177,18 +185,7 @@ class ViewStationPageFluent extends ViewStationPageShared {
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
// isThreeLine: item.status.delay != 0,
|
// isThreeLine: item.status.delay != 0,
|
||||||
title: Text.rich(
|
title: Text.rich(
|
||||||
TextSpan(
|
trainIdSpan(rank: item.train.rank, number: item.train.number),
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: item.train.rank,
|
|
||||||
style: TextStyle(
|
|
||||||
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
TextSpan(text: item.train.number,),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
subtitle: Text.rich(
|
subtitle: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart' hide Badge;
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/components/badge/badge.dart';
|
import 'package:info_tren/components/badge/badge.dart';
|
||||||
import 'package:info_tren/components/loading/loading.dart';
|
import 'package:info_tren/components/loading/loading.dart';
|
||||||
import 'package:info_tren/components/refresh_future_builder.dart';
|
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
|
@ -106,8 +107,12 @@ class ViewStationPageMaterial extends ViewStationPageShared {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Consumer(
|
||||||
'${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}',
|
builder: (context, ref, _) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
|
final time = tz.convertDateTime(item.time);
|
||||||
|
return Text(
|
||||||
|
'${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
inherit: true,
|
inherit: true,
|
||||||
fontFeatures: const [
|
fontFeatures: const [
|
||||||
|
@ -116,14 +121,17 @@ class ViewStationPageMaterial extends ViewStationPageShared {
|
||||||
decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
|
decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
|
||||||
fontSize: item.status.delay != 0 ? 12 : null,
|
fontSize: item.status.delay != 0 ? 12 : null,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
if (item.status.delay != 0) Builder(
|
if (item.status.delay != 0) Consumer(
|
||||||
builder: (context) {
|
builder: (context, ref, _) {
|
||||||
final newTime = item.time.add(Duration(minutes: item.status.delay));
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
|
final newTime = tz.convertDateTime(item.time.add(Duration(minutes: item.status.delay)));
|
||||||
final delay = item.status.delay > 0;
|
final delay = item.status.delay > 0;
|
||||||
|
|
||||||
return Text(
|
return Text(
|
||||||
'${newTime.toLocal().hour.toString().padLeft(2, '0')}:${newTime.toLocal().minute.toString().padLeft(2, '0')}',
|
'${newTime.hour.toString().padLeft(2, '0')}:${newTime.minute.toString().padLeft(2, '0')}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
inherit: true,
|
inherit: true,
|
||||||
fontFeatures: const [
|
fontFeatures: const [
|
||||||
|
@ -142,18 +150,7 @@ class ViewStationPageMaterial extends ViewStationPageShared {
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
isThreeLine: item.status.delay != 0,
|
isThreeLine: item.status.delay != 0,
|
||||||
title: Text.rich(
|
title: Text.rich(
|
||||||
TextSpan(
|
trainIdSpan(rank: item.train.rank, number: item.train.number),
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: item.train.rank,
|
|
||||||
style: TextStyle(
|
|
||||||
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
TextSpan(text: item.train.number,),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
subtitle: Text.rich(
|
subtitle: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/api/train_data.dart';
|
import 'package:info_tren/api/train_data.dart';
|
||||||
import 'package:info_tren/components/loading/loading.dart';
|
import 'package:info_tren/components/loading/loading.dart';
|
||||||
|
@ -9,59 +12,93 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info_fluent.dar
|
||||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
|
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
|
||||||
import 'package:info_tren/providers.dart';
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
|
class TrainInfo extends HookConsumerWidget {
|
||||||
class TrainInfo extends ConsumerWidget {
|
|
||||||
static String routeName = "/trainInfo/display";
|
static String routeName = "/trainInfo/display";
|
||||||
|
|
||||||
const TrainInfo({super.key,});
|
const TrainInfo({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final uiDesign = ref.watch(uiDesignProvider);
|
final uiDesign = ref.watch(uiDesignProvider);
|
||||||
final args = ref.watch(trainInfoArgumentsProvider);
|
final args = ref.watch(trainInfoArgumentsProvider);
|
||||||
final trainNumber = args.trainNumber;
|
final trainNumber = args.trainNumber;
|
||||||
final date = args.date;
|
final viewYesterday = useState(false);
|
||||||
|
final date = args.date ??
|
||||||
|
DateTime.now().copyWith(
|
||||||
|
hour: 12,
|
||||||
|
minute: 0,
|
||||||
|
second: 0,
|
||||||
|
millisecond: 0,
|
||||||
|
microsecond: 0,
|
||||||
|
);
|
||||||
|
final requestDate =
|
||||||
|
viewYesterday.value ? date.subtract(const Duration(days: 1)) : date;
|
||||||
|
final trainDataAsync = ref
|
||||||
|
.watch(trainInfoProvider(trainNumber: trainNumber, date: requestDate));
|
||||||
|
|
||||||
|
Future refresh() async {
|
||||||
|
ref.invalidate(
|
||||||
|
trainInfoProvider(trainNumber: trainNumber, date: requestDate),
|
||||||
|
);
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
}
|
||||||
|
|
||||||
return RefreshFutureBuilder<TrainData>(
|
|
||||||
futureCreator: () => getTrain(trainNumber, date: date),
|
|
||||||
builder: (context, refresh, replaceFutureBuilder, snapshot) {
|
|
||||||
void onViewYesterdayTrain() {
|
void onViewYesterdayTrain() {
|
||||||
replaceFutureBuilder(() => getTrain(trainNumber, date: DateTime.now().subtract(const Duration(days: 1))));
|
viewYesterday.value = !viewYesterday.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
|
useEffect(() {
|
||||||
return TrainInfoLoading(title: trainNumber.toString(), loadingText: "Se încarcă...",);
|
final handle = Timer.periodic(const Duration(minutes: 1), (timer) {
|
||||||
}
|
refresh();
|
||||||
else if (snapshot.state == RefreshFutureBuilderState.error) {
|
});
|
||||||
return TrainInfoError(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,);
|
return () {
|
||||||
}
|
handle.cancel();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return trainDataAsync.when(
|
||||||
|
data: (data) {
|
||||||
switch (uiDesign) {
|
switch (uiDesign) {
|
||||||
case UiDesign.MATERIAL:
|
case UiDesign.MATERIAL:
|
||||||
return TrainInfoMaterial(
|
return TrainInfoMaterial(
|
||||||
trainData: snapshot.data!,
|
trainData: data,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
|
isRefreshing: trainDataAsync.isRefreshing,
|
||||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||||
);
|
);
|
||||||
case UiDesign.CUPERTINO:
|
case UiDesign.CUPERTINO:
|
||||||
return TrainInfoCupertino(
|
return TrainInfoCupertino(
|
||||||
trainData: snapshot.data!,
|
trainData: data,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
|
isRefreshing: trainDataAsync.isRefreshing,
|
||||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||||
);
|
);
|
||||||
case UiDesign.FLUENT:
|
case UiDesign.FLUENT:
|
||||||
return TrainInfoFluent(
|
return TrainInfoFluent(
|
||||||
trainData: snapshot.data!,
|
trainData: data,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
|
isRefreshing: trainDataAsync.isRefreshing,
|
||||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
throw UnmatchedUiDesignException(uiDesign);
|
throw UnmatchedUiDesignException(uiDesign);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
error: (e, st) {
|
||||||
|
return TrainInfoError(
|
||||||
|
title: '$trainNumber - Error',
|
||||||
|
error: e,
|
||||||
|
refresh: refresh,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () {
|
||||||
|
return TrainInfoLoading(
|
||||||
|
title: trainNumber.toString(),
|
||||||
|
loadingText: "Se încarcă...",
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +169,9 @@ abstract class TrainInfoLoadingShared extends StatelessWidget {
|
||||||
required this.title,
|
required this.title,
|
||||||
String? loadingText,
|
String? loadingText,
|
||||||
super.key,
|
super.key,
|
||||||
}) : loadingWidget = Loading(text: loadingText,);
|
}) : loadingWidget = Loading(
|
||||||
|
text: loadingText,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TrainInfoError extends ConsumerWidget {
|
class TrainInfoError extends ConsumerWidget {
|
||||||
|
@ -181,7 +220,12 @@ abstract class TrainInfoErrorShared extends StatelessWidget {
|
||||||
final Object error;
|
final Object error;
|
||||||
final Future Function()? refresh;
|
final Future Function()? refresh;
|
||||||
|
|
||||||
const TrainInfoErrorShared({required this.title, required this.error, this.refresh, super.key,});
|
const TrainInfoErrorShared({
|
||||||
|
required this.title,
|
||||||
|
required this.error,
|
||||||
|
this.refresh,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class TrainInfoBody extends ConsumerWidget {
|
class TrainInfoBody extends ConsumerWidget {
|
||||||
|
@ -246,10 +290,15 @@ abstract class TrainInfoBodyShared extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class DisplayTrainYesterdayWarningCommon extends StatelessWidget {
|
abstract class DisplayTrainYesterdayWarningCommon extends StatelessWidget {
|
||||||
static const trainDidNotDepart = 'Acest tren nu a plecat încă din prima gară.';
|
static const trainDidNotDepart =
|
||||||
static const seeYesterdayTrain = 'Apasă aici pentru a vedea trenul care a plecat ieri.';
|
'Acest tren nu a plecat încă din prima gară.';
|
||||||
|
static const seeYesterdayTrain =
|
||||||
|
'Apasă aici pentru a vedea trenul care a plecat ieri.';
|
||||||
|
|
||||||
final void Function() onViewYesterdayTrain;
|
final void Function() onViewYesterdayTrain;
|
||||||
|
|
||||||
const DisplayTrainYesterdayWarningCommon(this.onViewYesterdayTrain, {super.key,});
|
const DisplayTrainYesterdayWarningCommon(
|
||||||
|
this.onViewYesterdayTrain, {
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:info_tren/components/cupertino_divider.dart';
|
import 'package:info_tren/components/cupertino_divider.dart';
|
||||||
import 'package:info_tren/components/sliver_persistent_header_padding.dart';
|
import 'package:info_tren/components/sliver_persistent_header_padding.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/train_info_constants.dart';
|
import 'package:info_tren/pages/train_info_page/train_info_constants.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||||
|
@ -214,7 +215,9 @@ class TrainInfoBodyCupertino extends TrainInfoBodyShared {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
SafeArea(
|
||||||
|
right: false,
|
||||||
|
child: Container(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
minWidth: 400,
|
minWidth: 400,
|
||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
|
@ -269,6 +272,7 @@ class TrainInfoBodyCupertino extends TrainInfoBodyShared {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: NestedScrollView(
|
child: NestedScrollView(
|
||||||
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||||
|
@ -559,18 +563,7 @@ class DisplayTrainID extends StatelessWidget {
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
TextSpan(
|
trainIdSpan(rank: trainData.rank, number: trainData.number),
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: trainData.rank,
|
|
||||||
style: TextStyle(
|
|
||||||
color: trainData.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
TextSpan(text: trainData.number,),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1058,8 +1051,8 @@ class DisplayTrainYesterdayWarningCupertino extends DisplayTrainYesterdayWarning
|
||||||
const TextSpan(text: '\n'),
|
const TextSpan(text: '\n'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
|
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: CupertinoColors.link,
|
color: CupertinoTheme.of(context).primaryColor,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = onViewYesterdayTrain,
|
..onTap = onViewYesterdayTrain,
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/components/badge/badge.dart';
|
import 'package:info_tren/components/badge/badge.dart';
|
||||||
|
import 'package:info_tren/components/cupertino_divider.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
class DisplayTrainStation extends StatelessWidget {
|
class DisplayTrainStation extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const DisplayTrainStation({required this.station, super.key,});
|
const DisplayTrainStation({
|
||||||
|
required this.station,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -13,6 +20,26 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
if (station.notes.whereType<TrainDataNoteDepartsAs>().isNotEmpty) ...[
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note =
|
||||||
|
station.notes.whereType<TrainDataNoteDepartsAs>().first;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: 'Trenul pleacă cu numărul '),
|
||||||
|
trainIdSpan(rank: note.rank, number: note.number),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const CupertinoDivider(),
|
||||||
|
],
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -20,8 +47,7 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Builder(
|
child: Builder(builder: (context) {
|
||||||
builder: (context) {
|
|
||||||
final departureStatus = station.departure?.status;
|
final departureStatus = station.departure?.status;
|
||||||
final arrivalStatus = station.arrival?.status;
|
final arrivalStatus = station.arrival?.status;
|
||||||
int delay;
|
int delay;
|
||||||
|
@ -29,12 +55,10 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
if (departureStatus == null) {
|
if (departureStatus == null) {
|
||||||
delay = arrivalStatus?.delay ?? 0;
|
delay = arrivalStatus?.delay ?? 0;
|
||||||
real = arrivalStatus?.real ?? false;
|
real = arrivalStatus?.real ?? false;
|
||||||
}
|
} else if (arrivalStatus == null) {
|
||||||
else if (arrivalStatus == null) {
|
|
||||||
delay = departureStatus.delay;
|
delay = departureStatus.delay;
|
||||||
real = departureStatus.real;
|
real = departureStatus.real;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
delay = departureStatus.delay;
|
delay = departureStatus.delay;
|
||||||
real = departureStatus.real;
|
real = departureStatus.real;
|
||||||
if (!real && arrivalStatus.real) {
|
if (!real && arrivalStatus.real) {
|
||||||
|
@ -54,8 +78,7 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
isDelayed: isDelayed,
|
isDelayed: isDelayed,
|
||||||
isOnTime: isOnTime,
|
isOnTime: isOnTime,
|
||||||
);
|
);
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Title(
|
Title(
|
||||||
|
@ -65,7 +88,9 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: station.platform == null ? Container() : Badge(text: station.platform!, caption: 'linia'),
|
child: station.platform == null
|
||||||
|
? Container()
|
||||||
|
: Badge(text: station.platform!, caption: 'linia'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -73,16 +98,61 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
Time(
|
Time(
|
||||||
station: station,
|
station: station,
|
||||||
),
|
),
|
||||||
|
if (station.notes.whereType<TrainDataNoteDetachingWagons>().isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note =
|
||||||
|
station.notes.whereType<TrainDataNoteDetachingWagons>().first;
|
||||||
|
return Text(
|
||||||
|
'Trenul detașează vagoane către ${note.station}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (station.notes.whereType<TrainDataNoteReceivingWagons>().isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note =
|
||||||
|
station.notes.whereType<TrainDataNoteReceivingWagons>().first;
|
||||||
|
return Text(
|
||||||
|
'Trenul primește vagoane de la ${note.station}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
Delay(
|
Delay(
|
||||||
station: station,
|
station: station,
|
||||||
),
|
),
|
||||||
|
if (station.notes
|
||||||
|
.whereType<TrainDataNoteTrainNumberChange>()
|
||||||
|
.isNotEmpty) ...[
|
||||||
|
const CupertinoDivider(),
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note = station.notes
|
||||||
|
.whereType<TrainDataNoteTrainNumberChange>()
|
||||||
|
.first;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: 'Trenul își schimbă numărul în '),
|
||||||
|
trainIdSpan(rank: note.rank, number: note.number),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Title extends StatelessWidget {
|
class Title extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Title({
|
const Title({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -95,7 +165,9 @@ class Title extends StatelessWidget {
|
||||||
station.name,
|
station.name,
|
||||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
|
fontWeight: MediaQuery.of(context).boldText
|
||||||
|
? FontWeight.w500
|
||||||
|
: FontWeight.w300,
|
||||||
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -104,7 +176,7 @@ class Title extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Time extends StatelessWidget {
|
class Time extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Time({
|
const Time({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -138,13 +210,27 @@ class Time extends StatelessWidget {
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 2,),
|
Container(
|
||||||
ArrivalTime(station: station,),
|
width: 2,
|
||||||
Expanded(child: Container(),),
|
),
|
||||||
StopTime(station: station,),
|
ArrivalTime(
|
||||||
Expanded(child: Container(),),
|
station: station,
|
||||||
DepartureTime(station: station,),
|
),
|
||||||
Container(width: 2,),
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
StopTime(
|
||||||
|
station: station,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
DepartureTime(
|
||||||
|
station: station,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"→",
|
"→",
|
||||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||||
|
@ -156,8 +242,8 @@ class Time extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrivalTime extends StatelessWidget {
|
class ArrivalTime extends ConsumerWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final bool finalStation;
|
final bool finalStation;
|
||||||
|
|
||||||
const ArrivalTime({
|
const ArrivalTime({
|
||||||
|
@ -167,7 +253,9 @@ class ArrivalTime extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
|
|
||||||
if (finalStation) {
|
if (finalStation) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
@ -178,21 +266,26 @@ class ArrivalTime extends StatelessWidget {
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 2,),
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
const Text("sosire la "),
|
const Text("sosire la "),
|
||||||
ArrivalTime(station: station,),
|
ArrivalTime(
|
||||||
Expanded(child: Container(),),
|
station: station,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final delay = station.arrival!.status?.delay ?? 0;
|
final delay = station.arrival!.status?.delay ?? 0;
|
||||||
final time = station.arrival!.scheduleTime.toLocal();
|
final time = tz.convertDateTime(station.arrival!.scheduleTime);
|
||||||
|
|
||||||
if (delay == 0) {
|
if (delay == 0) {
|
||||||
return Text("${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
|
return Text(
|
||||||
}
|
"${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
|
||||||
else if (delay > 0) {
|
} else if (delay > 0) {
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -213,8 +306,7 @@ class ArrivalTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -241,7 +333,7 @@ class ArrivalTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class StopTime extends StatelessWidget {
|
class StopTime extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const StopTime({
|
const StopTime({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -271,14 +363,12 @@ class StopTime extends StatelessWidget {
|
||||||
minutes ? '1 minut' : '1 secundă',
|
minutes ? '1 minut' : '1 secundă',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
}
|
} else if (stopsForInt < 20) {
|
||||||
else if (stopsForInt < 20) {
|
|
||||||
return Text(
|
return Text(
|
||||||
'$stopsForInt ${minutes ? 'minute' : 'seconde'}',
|
'$stopsForInt ${minutes ? 'minute' : 'seconde'}',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return Text(
|
return Text(
|
||||||
'$stopsForInt de ${minutes ? 'minute' : 'secunde'}',
|
'$stopsForInt de ${minutes ? 'minute' : 'secunde'}',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -291,8 +381,8 @@ class StopTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DepartureTime extends StatelessWidget {
|
class DepartureTime extends ConsumerWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final bool firstStation;
|
final bool firstStation;
|
||||||
|
|
||||||
const DepartureTime({
|
const DepartureTime({
|
||||||
|
@ -302,15 +392,22 @@ class DepartureTime extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
if (firstStation) {
|
if (firstStation) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(child: Container(),),
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
const Text("plecare la "),
|
const Text("plecare la "),
|
||||||
DepartureTime(station: station,),
|
DepartureTime(
|
||||||
Container(width: 2,),
|
station: station,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"→",
|
"→",
|
||||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||||
|
@ -319,15 +416,14 @@ class DepartureTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final delay = station.departure!.status?.delay ?? 0;
|
final delay = station.departure!.status?.delay ?? 0;
|
||||||
final time = station.departure!.scheduleTime.toLocal();
|
final time = tz.convertDateTime(station.departure!.scheduleTime);
|
||||||
|
|
||||||
if (delay == 0) {
|
if (delay == 0) {
|
||||||
return Text("${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
|
return Text(
|
||||||
}
|
"${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
|
||||||
else if (delay > 0) {
|
} else if (delay > 0) {
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -348,8 +444,7 @@ class DepartureTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -376,7 +471,7 @@ class DepartureTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Delay extends StatelessWidget {
|
class Delay extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Delay({
|
const Delay({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -404,8 +499,7 @@ class Delay extends StatelessWidget {
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
} else if (delay < 0) {
|
||||||
else if (delay < 0) {
|
|
||||||
return Text(
|
return Text(
|
||||||
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
|
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
|
||||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||||
|
@ -303,22 +304,7 @@ class DisplayTrainID extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
TextSpan(
|
trainIdSpan(rank: trainData.rank, number: trainData.number),
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: trainData.rank,
|
|
||||||
style: TextStyle(
|
|
||||||
color: trainData.rank.startsWith('IR')
|
|
||||||
? const Color.fromARGB(255, 255, 0, 0)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
TextSpan(
|
|
||||||
text: trainData.number,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: FluentTheme.of(context).typography.title?.copyWith(
|
style: FluentTheme.of(context).typography.title?.copyWith(
|
||||||
color: FluentTheme.of(context).typography.body?.color,
|
color: FluentTheme.of(context).typography.body?.color,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
@ -762,7 +748,7 @@ class DisplayTrainYesterdayWarningFluent
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
|
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue,
|
color: FluentTheme.of(context).accentColor,// Colors.blue,
|
||||||
),
|
),
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = onViewYesterdayTrain,
|
..onTap = onViewYesterdayTrain,
|
||||||
|
|
|
@ -1,16 +1,44 @@
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/components/badge/badge.dart';
|
import 'package:info_tren/components/badge/badge.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
class DisplayTrainStation extends StatelessWidget {
|
class DisplayTrainStation extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
|
|
||||||
const DisplayTrainStation({required this.station, this.onTap, super.key,});
|
const DisplayTrainStation({
|
||||||
|
required this.station,
|
||||||
|
this.onTap,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (station.notes.whereType<TrainDataNoteDepartsAs>().isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note =
|
||||||
|
station.notes.whereType<TrainDataNoteDepartsAs>().first;
|
||||||
return Padding(
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: 'Trenul pleacă cu numărul '),
|
||||||
|
trainIdSpan(rank: note.rank, number: note.number),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
padding: const EdgeInsets.all(2),
|
padding: const EdgeInsets.all(2),
|
||||||
child: HoverButton(
|
child: HoverButton(
|
||||||
onPressed: onTap,
|
onPressed: onTap,
|
||||||
|
@ -28,8 +56,7 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Builder(
|
child: Builder(builder: (context) {
|
||||||
builder: (context) {
|
|
||||||
final departureStatus = station.departure?.status;
|
final departureStatus = station.departure?.status;
|
||||||
final arrivalStatus = station.arrival?.status;
|
final arrivalStatus = station.arrival?.status;
|
||||||
int delay;
|
int delay;
|
||||||
|
@ -37,12 +64,10 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
if (departureStatus == null) {
|
if (departureStatus == null) {
|
||||||
delay = arrivalStatus?.delay ?? 0;
|
delay = arrivalStatus?.delay ?? 0;
|
||||||
real = arrivalStatus?.real ?? false;
|
real = arrivalStatus?.real ?? false;
|
||||||
}
|
} else if (arrivalStatus == null) {
|
||||||
else if (arrivalStatus == null) {
|
|
||||||
delay = departureStatus.delay;
|
delay = departureStatus.delay;
|
||||||
real = departureStatus.real;
|
real = departureStatus.real;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
delay = departureStatus.delay;
|
delay = departureStatus.delay;
|
||||||
real = departureStatus.real;
|
real = departureStatus.real;
|
||||||
if (!real && arrivalStatus.real) {
|
if (!real && arrivalStatus.real) {
|
||||||
|
@ -62,8 +87,7 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
isDelayed: isDelayed,
|
isDelayed: isDelayed,
|
||||||
isOnTime: isOnTime,
|
isOnTime: isOnTime,
|
||||||
);
|
);
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Title(
|
Title(
|
||||||
|
@ -75,7 +99,10 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
? Container()
|
? Container()
|
||||||
: Align(
|
: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Badge(text: station.platform!, caption: 'linia',),
|
child: Badge(
|
||||||
|
text: station.platform!,
|
||||||
|
caption: 'linia',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -83,6 +110,34 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
Time(
|
Time(
|
||||||
station: station,
|
station: station,
|
||||||
),
|
),
|
||||||
|
if (station.notes
|
||||||
|
.whereType<TrainDataNoteDetachingWagons>()
|
||||||
|
.isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note = station.notes
|
||||||
|
.whereType<TrainDataNoteDetachingWagons>()
|
||||||
|
.first;
|
||||||
|
return Text(
|
||||||
|
'Trenul detașează vagoane către ${note.station}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (station.notes
|
||||||
|
.whereType<TrainDataNoteReceivingWagons>()
|
||||||
|
.isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note = station.notes
|
||||||
|
.whereType<TrainDataNoteReceivingWagons>()
|
||||||
|
.first;
|
||||||
|
return Text(
|
||||||
|
'Trenul primește vagoane de la ${note.station}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
Delay(
|
Delay(
|
||||||
station: station,
|
station: station,
|
||||||
),
|
),
|
||||||
|
@ -91,12 +146,35 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (station.notes
|
||||||
|
.whereType<TrainDataNoteTrainNumberChange>()
|
||||||
|
.isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note = station.notes
|
||||||
|
.whereType<TrainDataNoteTrainNumberChange>()
|
||||||
|
.first;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: 'Trenul își schimbă numărul în '),
|
||||||
|
trainIdSpan(rank: note.rank, number: note.number),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Title extends StatelessWidget {
|
class Title extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Title({
|
const Title({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -109,7 +187,9 @@ class Title extends StatelessWidget {
|
||||||
station.name,
|
station.name,
|
||||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
|
fontWeight: MediaQuery.of(context).boldText
|
||||||
|
? FontWeight.w500
|
||||||
|
: FontWeight.w300,
|
||||||
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -118,7 +198,7 @@ class Title extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Time extends StatelessWidget {
|
class Time extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Time({
|
const Time({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -152,13 +232,27 @@ class Time extends StatelessWidget {
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 2,),
|
Container(
|
||||||
ArrivalTime(station: station,),
|
width: 2,
|
||||||
Expanded(child: Container(),),
|
),
|
||||||
StopTime(station: station,),
|
ArrivalTime(
|
||||||
Expanded(child: Container(),),
|
station: station,
|
||||||
DepartureTime(station: station,),
|
),
|
||||||
Container(width: 2,),
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
StopTime(
|
||||||
|
station: station,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
DepartureTime(
|
||||||
|
station: station,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"→",
|
"→",
|
||||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||||
|
@ -170,8 +264,8 @@ class Time extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrivalTime extends StatelessWidget {
|
class ArrivalTime extends ConsumerWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final bool finalStation;
|
final bool finalStation;
|
||||||
|
|
||||||
const ArrivalTime({
|
const ArrivalTime({
|
||||||
|
@ -181,7 +275,8 @@ class ArrivalTime extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
if (station.arrival == null) {
|
if (station.arrival == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
@ -195,21 +290,26 @@ class ArrivalTime extends StatelessWidget {
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 2,),
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
const Text("sosire la "),
|
const Text("sosire la "),
|
||||||
ArrivalTime(station: station,),
|
ArrivalTime(
|
||||||
Expanded(child: Container(),),
|
station: station,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final delay = station.arrival!.status?.delay ?? 0;
|
final delay = station.arrival!.status?.delay ?? 0;
|
||||||
final time = station.arrival!.scheduleTime.toLocal();
|
final time = tz.convertDateTime(station.arrival!.scheduleTime);
|
||||||
|
|
||||||
if (delay == 0) {
|
if (delay == 0) {
|
||||||
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
return Text(
|
||||||
}
|
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
||||||
else if (delay > 0) {
|
} else if (delay > 0) {
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -231,8 +331,7 @@ class ArrivalTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -260,7 +359,7 @@ class ArrivalTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class StopTime extends StatelessWidget {
|
class StopTime extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const StopTime({
|
const StopTime({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -289,14 +388,12 @@ class StopTime extends StatelessWidget {
|
||||||
"1 ${minutes ? 'minut' : 'secundă'}",
|
"1 ${minutes ? 'minut' : 'secundă'}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
}
|
} else if (stopsForInt < 20) {
|
||||||
else if (stopsForInt < 20) {
|
|
||||||
return Text(
|
return Text(
|
||||||
"$stopsForInt ${minutes ? 'minute' : 'secunde'}",
|
"$stopsForInt ${minutes ? 'minute' : 'secunde'}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return Text(
|
return Text(
|
||||||
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}",
|
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -309,8 +406,8 @@ class StopTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DepartureTime extends StatelessWidget {
|
class DepartureTime extends ConsumerWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final bool firstStation;
|
final bool firstStation;
|
||||||
|
|
||||||
const DepartureTime({
|
const DepartureTime({
|
||||||
|
@ -320,7 +417,8 @@ class DepartureTime extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
if (station.departure == null) {
|
if (station.departure == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
@ -328,10 +426,16 @@ class DepartureTime extends StatelessWidget {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(child: Container(),),
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
const Text("plecare la "),
|
const Text("plecare la "),
|
||||||
DepartureTime(station: station,),
|
DepartureTime(
|
||||||
Container(width: 2,),
|
station: station,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"→",
|
"→",
|
||||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||||
|
@ -340,15 +444,14 @@ class DepartureTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final delay = station.departure!.status?.delay ?? 0;
|
final delay = station.departure!.status?.delay ?? 0;
|
||||||
final time = station.departure!.scheduleTime.toLocal();
|
final time = tz.convertDateTime(station.departure!.scheduleTime);
|
||||||
|
|
||||||
if (delay == 0) {
|
if (delay == 0) {
|
||||||
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
return Text(
|
||||||
}
|
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
||||||
else if (delay > 0) {
|
} else if (delay > 0) {
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -370,8 +473,7 @@ class DepartureTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -398,9 +500,8 @@ class DepartureTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Delay extends StatelessWidget {
|
class Delay extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Delay({
|
const Delay({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -429,8 +530,7 @@ class Delay extends StatelessWidget {
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
} else if (delay < 0) {
|
||||||
else if (delay < 0) {
|
|
||||||
return Text(
|
return Text(
|
||||||
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
|
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
|
||||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:info_tren/components/slim_app_bar.dart';
|
import 'package:info_tren/components/slim_app_bar.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||||
|
@ -8,7 +9,11 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info_material_D
|
||||||
import 'package:info_tren/utils/state_to_string.dart';
|
import 'package:info_tren/utils/state_to_string.dart';
|
||||||
|
|
||||||
class TrainInfoLoadingMaterial extends TrainInfoLoadingShared {
|
class TrainInfoLoadingMaterial extends TrainInfoLoadingShared {
|
||||||
TrainInfoLoadingMaterial({required super.title, super.loadingText, super.key,});
|
TrainInfoLoadingMaterial({
|
||||||
|
required super.title,
|
||||||
|
super.loadingText,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -80,8 +85,29 @@ class TrainInfoMaterial extends TrainInfoShared {
|
||||||
: AppBar(
|
: AppBar(
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: Text(
|
title: Text(
|
||||||
"Informații despre ${trainData.rank} ${trainData.number}",
|
'Informații despre ${trainData.rank} ${trainData.number}',
|
||||||
),
|
),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
tooltip: 'Reîncarcă',
|
||||||
|
icon: (isRefreshing ?? false)
|
||||||
|
? const Center(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 16,
|
||||||
|
width: 16,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const Icon(Icons.refresh),
|
||||||
|
onPressed: (isRefreshing ?? false)
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
refresh?.call();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -171,8 +197,7 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
|
||||||
if (onViewYesterdayTrain != null &&
|
if (onViewYesterdayTrain != null &&
|
||||||
trainData.stations.first.departure!.scheduleTime
|
trainData.stations.first.departure!.scheduleTime
|
||||||
.compareTo(DateTime.now()) >
|
.compareTo(DateTime.now()) >
|
||||||
0)
|
0) ...[
|
||||||
...[
|
|
||||||
DisplayTrainYesterdayWarningMaterial(
|
DisplayTrainYesterdayWarningMaterial(
|
||||||
onViewYesterdayTrain!,
|
onViewYesterdayTrain!,
|
||||||
),
|
),
|
||||||
|
@ -192,10 +217,7 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: MediaQuery
|
height: MediaQuery.of(context).viewPadding.bottom,
|
||||||
.of(context)
|
|
||||||
.viewPadding
|
|
||||||
.bottom,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -203,8 +225,7 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
slivers: <Widget>[
|
slivers: <Widget>[
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
|
@ -283,8 +304,8 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
|
||||||
.compareTo(DateTime.now()) >
|
.compareTo(DateTime.now()) >
|
||||||
0) ...[
|
0) ...[
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: DisplayTrainYesterdayWarningMaterial(
|
child:
|
||||||
onViewYesterdayTrain!),
|
DisplayTrainYesterdayWarningMaterial(onViewYesterdayTrain!),
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Divider(
|
child: Divider(
|
||||||
|
@ -298,10 +319,7 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
|
||||||
),
|
),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: MediaQuery
|
height: MediaQuery.of(context).viewPadding.bottom,
|
||||||
.of(context)
|
|
||||||
.viewPadding
|
|
||||||
.bottom,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -313,27 +331,15 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
|
||||||
class DisplayTrainID extends StatelessWidget {
|
class DisplayTrainID extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
|
|
||||||
const DisplayTrainID({required this.trainData, super.key,});
|
const DisplayTrainID({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Text.rich(
|
return Text.rich(
|
||||||
TextSpan(
|
trainIdSpan(rank: trainData.rank, number: trainData.number),
|
||||||
children: [
|
|
||||||
TextSpan(
|
|
||||||
text: trainData.rank,
|
|
||||||
style: TextStyle(
|
|
||||||
color: trainData.rank.startsWith('IR')
|
|
||||||
? const Color.fromARGB(255, 255, 0, 0)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' '),
|
|
||||||
TextSpan(
|
|
||||||
text: trainData.number,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
style: (isSmallScreen(context)
|
style: (isSmallScreen(context)
|
||||||
? Theme.of(context).textTheme.headlineMedium
|
? Theme.of(context).textTheme.headlineMedium
|
||||||
: Theme.of(context).textTheme.displaySmall)
|
: Theme.of(context).textTheme.displaySmall)
|
||||||
|
@ -349,7 +355,10 @@ class DisplayTrainID extends StatelessWidget {
|
||||||
class DisplayTrainOperator extends StatelessWidget {
|
class DisplayTrainOperator extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
|
|
||||||
const DisplayTrainOperator({required this.trainData, super.key,});
|
const DisplayTrainOperator({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -367,7 +376,10 @@ class DisplayTrainOperator extends StatelessWidget {
|
||||||
class DisplayTrainRoute extends StatelessWidget {
|
class DisplayTrainRoute extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
|
|
||||||
const DisplayTrainRoute({required this.trainData, super.key,});
|
const DisplayTrainRoute({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -432,7 +444,10 @@ class DisplayTrainDeparture extends StatelessWidget {
|
||||||
class DisplayTrainLastInfo extends StatelessWidget {
|
class DisplayTrainLastInfo extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
|
|
||||||
const DisplayTrainLastInfo({required this.trainData, super.key,});
|
const DisplayTrainLastInfo({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -624,7 +639,10 @@ class DisplayTrainLastInfo extends StatelessWidget {
|
||||||
class DisplayTrainDestination extends StatelessWidget {
|
class DisplayTrainDestination extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
|
|
||||||
const DisplayTrainDestination({required this.trainData, super.key,});
|
const DisplayTrainDestination({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -728,7 +746,10 @@ class DisplayTrainDestination extends StatelessWidget {
|
||||||
class DisplayTrainRouteDistance extends StatelessWidget {
|
class DisplayTrainRouteDistance extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
|
|
||||||
const DisplayTrainRouteDistance({required this.trainData, super.key,});
|
const DisplayTrainRouteDistance({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -765,7 +786,10 @@ class DisplayTrainRouteDistance extends StatelessWidget {
|
||||||
class DisplayTrainRouteDuration extends StatelessWidget {
|
class DisplayTrainRouteDuration extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
|
|
||||||
const DisplayTrainRouteDuration({required this.trainData, super.key,});
|
const DisplayTrainRouteDuration({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -848,7 +872,10 @@ class DisplayTrainRouteDuration extends StatelessWidget {
|
||||||
|
|
||||||
class DisplayTrainYesterdayWarningMaterial
|
class DisplayTrainYesterdayWarningMaterial
|
||||||
extends DisplayTrainYesterdayWarningCommon {
|
extends DisplayTrainYesterdayWarningCommon {
|
||||||
const DisplayTrainYesterdayWarningMaterial(super.onViewYesterdayTrain, {super.key,});
|
const DisplayTrainYesterdayWarningMaterial(
|
||||||
|
super.onViewYesterdayTrain, {
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -884,7 +911,10 @@ class DisplayTrainYesterdayWarningMaterial
|
||||||
|
|
||||||
class DisplayTrainStations extends StatelessWidget {
|
class DisplayTrainStations extends StatelessWidget {
|
||||||
final TrainData trainData;
|
final TrainData trainData;
|
||||||
const DisplayTrainStations({required this.trainData, super.key,});
|
const DisplayTrainStations({
|
||||||
|
required this.trainData,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -898,7 +928,8 @@ class DisplayTrainStations extends StatelessWidget {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
ViewStationPage.routeName,
|
ViewStationPage.routeName,
|
||||||
arguments: ViewStationArguments(stationName: trainData.stations[index].name),
|
arguments: ViewStationArguments(
|
||||||
|
stationName: trainData.stations[index].name),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,17 +1,45 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart' hide Badge;
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:info_tren/components/train_id_text_span.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/components/badge/badge.dart';
|
import 'package:info_tren/components/badge/badge.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
|
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
|
||||||
|
import 'package:info_tren/providers.dart';
|
||||||
|
|
||||||
class DisplayTrainStation extends StatelessWidget {
|
class DisplayTrainStation extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final void Function()? onTap;
|
final void Function()? onTap;
|
||||||
|
|
||||||
const DisplayTrainStation({required this.station, this.onTap, super.key,});
|
const DisplayTrainStation({
|
||||||
|
required this.station,
|
||||||
|
this.onTap,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (station.notes.whereType<TrainDataNoteDepartsAs>().isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note =
|
||||||
|
station.notes.whereType<TrainDataNoteDepartsAs>().first;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: 'Trenul pleacă cu numărul '),
|
||||||
|
trainIdSpan(rank: note.rank, number: note.number),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Card(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -27,8 +55,7 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Builder(
|
child: Builder(builder: (context) {
|
||||||
builder: (context) {
|
|
||||||
final departureStatus = station.departure?.status;
|
final departureStatus = station.departure?.status;
|
||||||
final arrivalStatus = station.arrival?.status;
|
final arrivalStatus = station.arrival?.status;
|
||||||
int delay;
|
int delay;
|
||||||
|
@ -36,12 +63,10 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
if (departureStatus == null) {
|
if (departureStatus == null) {
|
||||||
delay = arrivalStatus?.delay ?? 0;
|
delay = arrivalStatus?.delay ?? 0;
|
||||||
real = arrivalStatus?.real ?? false;
|
real = arrivalStatus?.real ?? false;
|
||||||
}
|
} else if (arrivalStatus == null) {
|
||||||
else if (arrivalStatus == null) {
|
|
||||||
delay = departureStatus.delay;
|
delay = departureStatus.delay;
|
||||||
real = departureStatus.real;
|
real = departureStatus.real;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
delay = departureStatus.delay;
|
delay = departureStatus.delay;
|
||||||
real = departureStatus.real;
|
real = departureStatus.real;
|
||||||
if (!real && arrivalStatus.real) {
|
if (!real && arrivalStatus.real) {
|
||||||
|
@ -61,8 +86,7 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
isDelayed: isDelayed,
|
isDelayed: isDelayed,
|
||||||
isOnTime: isOnTime,
|
isOnTime: isOnTime,
|
||||||
);
|
);
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Title(
|
Title(
|
||||||
|
@ -74,7 +98,10 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
? Container()
|
? Container()
|
||||||
: Align(
|
: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Badge(text: station.platform!, caption: 'linia',),
|
child: Badge(
|
||||||
|
text: station.platform!,
|
||||||
|
caption: 'linia',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -82,6 +109,34 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
Time(
|
Time(
|
||||||
station: station,
|
station: station,
|
||||||
),
|
),
|
||||||
|
if (station.notes
|
||||||
|
.whereType<TrainDataNoteDetachingWagons>()
|
||||||
|
.isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note = station.notes
|
||||||
|
.whereType<TrainDataNoteDetachingWagons>()
|
||||||
|
.first;
|
||||||
|
return Text(
|
||||||
|
'Trenul detașează vagoane către ${note.station}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (station.notes
|
||||||
|
.whereType<TrainDataNoteReceivingWagons>()
|
||||||
|
.isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note = station.notes
|
||||||
|
.whereType<TrainDataNoteReceivingWagons>()
|
||||||
|
.first;
|
||||||
|
return Text(
|
||||||
|
'Trenul primește vagoane de la ${note.station}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
Delay(
|
Delay(
|
||||||
station: station,
|
station: station,
|
||||||
),
|
),
|
||||||
|
@ -89,12 +144,35 @@ class DisplayTrainStation extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
if (station.notes
|
||||||
|
.whereType<TrainDataNoteTrainNumberChange>()
|
||||||
|
.isNotEmpty)
|
||||||
|
Builder(
|
||||||
|
builder: (context) {
|
||||||
|
final note = station.notes
|
||||||
|
.whereType<TrainDataNoteTrainNumberChange>()
|
||||||
|
.first;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: 'Trenul își schimbă numărul în '),
|
||||||
|
trainIdSpan(rank: note.rank, number: note.number),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Title extends StatelessWidget {
|
class Title extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Title({
|
const Title({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -107,7 +185,9 @@ class Title extends StatelessWidget {
|
||||||
station.name,
|
station.name,
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
|
fontWeight: MediaQuery.of(context).boldText
|
||||||
|
? FontWeight.w500
|
||||||
|
: FontWeight.w300,
|
||||||
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -116,7 +196,7 @@ class Title extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Time extends StatelessWidget {
|
class Time extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Time({
|
const Time({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -150,13 +230,27 @@ class Time extends StatelessWidget {
|
||||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 2,),
|
Container(
|
||||||
ArrivalTime(station: station,),
|
width: 2,
|
||||||
Expanded(child: Container(),),
|
),
|
||||||
StopTime(station: station,),
|
ArrivalTime(
|
||||||
Expanded(child: Container(),),
|
station: station,
|
||||||
DepartureTime(station: station,),
|
),
|
||||||
Container(width: 2,),
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
StopTime(
|
||||||
|
station: station,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
DepartureTime(
|
||||||
|
station: station,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"→",
|
"→",
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
@ -168,8 +262,8 @@ class Time extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrivalTime extends StatelessWidget {
|
class ArrivalTime extends ConsumerWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final bool finalStation;
|
final bool finalStation;
|
||||||
|
|
||||||
const ArrivalTime({
|
const ArrivalTime({
|
||||||
|
@ -179,7 +273,8 @@ class ArrivalTime extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
if (station.arrival == null) {
|
if (station.arrival == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
@ -193,21 +288,26 @@ class ArrivalTime extends StatelessWidget {
|
||||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 2,),
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
const Text("sosire la "),
|
const Text("sosire la "),
|
||||||
ArrivalTime(station: station,),
|
ArrivalTime(
|
||||||
Expanded(child: Container(),),
|
station: station,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final delay = station.arrival!.status?.delay ?? 0;
|
final delay = station.arrival!.status?.delay ?? 0;
|
||||||
final time = station.arrival!.scheduleTime.toLocal();
|
final time = tz.convertDateTime(station.arrival!.scheduleTime);
|
||||||
|
|
||||||
if (delay == 0) {
|
if (delay == 0) {
|
||||||
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
return Text(
|
||||||
}
|
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
||||||
else if (delay > 0) {
|
} else if (delay > 0) {
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -228,8 +328,7 @@ class ArrivalTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -256,7 +355,7 @@ class ArrivalTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class StopTime extends StatelessWidget {
|
class StopTime extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const StopTime({
|
const StopTime({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -285,14 +384,12 @@ class StopTime extends StatelessWidget {
|
||||||
"1 ${minutes ? 'minut' : 'secundă'}",
|
"1 ${minutes ? 'minut' : 'secundă'}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
}
|
} else if (stopsForInt < 20) {
|
||||||
else if (stopsForInt < 20) {
|
|
||||||
return Text(
|
return Text(
|
||||||
"$stopsForInt ${minutes ? 'minute' : 'secunde'}",
|
"$stopsForInt ${minutes ? 'minute' : 'secunde'}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return Text(
|
return Text(
|
||||||
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}",
|
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
@ -305,8 +402,8 @@ class StopTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DepartureTime extends StatelessWidget {
|
class DepartureTime extends ConsumerWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
final bool firstStation;
|
final bool firstStation;
|
||||||
|
|
||||||
const DepartureTime({
|
const DepartureTime({
|
||||||
|
@ -316,7 +413,8 @@ class DepartureTime extends StatelessWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final tz = ref.watch(uiTimeZoneProvider);
|
||||||
if (station.departure == null) {
|
if (station.departure == null) {
|
||||||
return Container();
|
return Container();
|
||||||
}
|
}
|
||||||
|
@ -324,10 +422,16 @@ class DepartureTime extends StatelessWidget {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(child: Container(),),
|
Expanded(
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
const Text("plecare la "),
|
const Text("plecare la "),
|
||||||
DepartureTime(station: station,),
|
DepartureTime(
|
||||||
Container(width: 2,),
|
station: station,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"→",
|
"→",
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
@ -336,15 +440,14 @@ class DepartureTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final delay = station.departure!.status?.delay ?? 0;
|
final delay = station.departure!.status?.delay ?? 0;
|
||||||
final time = station.departure!.scheduleTime.toLocal();
|
final time = tz.convertDateTime(station.departure!.scheduleTime);
|
||||||
|
|
||||||
if (delay == 0) {
|
if (delay == 0) {
|
||||||
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
return Text(
|
||||||
}
|
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
||||||
else if (delay > 0) {
|
} else if (delay > 0) {
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -365,8 +468,7 @@ class DepartureTime extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
final oldDate = time;
|
final oldDate = time;
|
||||||
final newDate = oldDate.add(Duration(minutes: delay));
|
final newDate = oldDate.add(Duration(minutes: delay));
|
||||||
|
|
||||||
|
@ -392,9 +494,8 @@ class DepartureTime extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Delay extends StatelessWidget {
|
class Delay extends StatelessWidget {
|
||||||
final Station station;
|
final TrainDataStation station;
|
||||||
|
|
||||||
const Delay({
|
const Delay({
|
||||||
required this.station,
|
required this.station,
|
||||||
|
@ -422,8 +523,7 @@ class Delay extends StatelessWidget {
|
||||||
fontStyle: FontStyle.italic,
|
fontStyle: FontStyle.italic,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
} else if (delay < 0) {
|
||||||
else if (delay < 0) {
|
|
||||||
return Text(
|
return Text(
|
||||||
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
|
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
|
|
@ -1,41 +1,87 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:info_tren/api/station_data.dart';
|
import 'package:info_tren/api/station_data.dart';
|
||||||
|
import 'package:info_tren/api/train_data.dart';
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||||
import 'package:info_tren/utils/default_ui_design.dart';
|
import 'package:info_tren/utils/default_ui_design.dart';
|
||||||
import 'package:info_tren/utils/iterable_extensions.dart';
|
import 'package:info_tren/utils/iterable_extensions.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
part 'providers.g.dart';
|
||||||
|
|
||||||
final sharedPreferencesProvider = Provider<SharedPreferences>(
|
final sharedPreferencesProvider = Provider<SharedPreferences>(
|
||||||
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
||||||
);
|
);
|
||||||
|
|
||||||
final uiDesignProvider = Provider((ref) {
|
class UiDesignNotifier extends StateNotifier<UiDesign> {
|
||||||
final sharedPreferences = ref.watch(sharedPreferencesProvider);
|
final SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
|
UiDesignNotifier({required this.sharedPreferences,}) : super(UiDesign.MATERIAL) {
|
||||||
final stored = sharedPreferences.getString('uiDesign');
|
final stored = sharedPreferences.getString('uiDesign');
|
||||||
final design = UiDesign.values.where((element) => element.name == stored).firstOrNull ?? defaultUiDesign;
|
final design = UiDesign.values.where((element) => element.name == stored).firstOrNull ?? defaultUiDesign;
|
||||||
return design;
|
state = design;
|
||||||
});
|
}
|
||||||
Future<void> setUiDesign(Ref ref, UiDesign? design) async {
|
|
||||||
final sharedPreferences = ref.watch(sharedPreferencesProvider);
|
void set(UiDesign? design) async {
|
||||||
if (design != null) {
|
if (design != null) {
|
||||||
await sharedPreferences.setString('uiDesign', design.name);
|
await sharedPreferences.setString('uiDesign', design.name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await sharedPreferences.remove('uiDesign');
|
await sharedPreferences.remove('uiDesign');
|
||||||
}
|
}
|
||||||
ref.invalidate(uiDesignProvider);
|
state = design ?? defaultUiDesign;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
final uiDesignProvider = StateNotifierProvider<UiDesignNotifier, UiDesign>(
|
||||||
|
(ref) => UiDesignNotifier(
|
||||||
|
sharedPreferences: ref.watch(sharedPreferencesProvider),
|
||||||
|
),
|
||||||
|
dependencies: [sharedPreferencesProvider],
|
||||||
|
);
|
||||||
|
|
||||||
|
class UiTimeZoneNotifier extends StateNotifier<UiTimeZone> {
|
||||||
|
final SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
|
UiTimeZoneNotifier({required this.sharedPreferences,}) : super(const RoUiTimeZone()) {
|
||||||
|
final stored = sharedPreferences.getString('uiTimeZone');
|
||||||
|
if (stored != null) {
|
||||||
|
try {
|
||||||
|
state = UiTimeZone.fromSerString(stored);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
log('Invalid UiTimeZone ser: $stored, error: $e', level: 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(UiTimeZone? timeZone) async {
|
||||||
|
if (timeZone != null) {
|
||||||
|
await sharedPreferences.setString('uiTimeZone', timeZone.toSerString());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await sharedPreferences.remove('uiTimeZone');
|
||||||
|
}
|
||||||
|
state = timeZone ?? const LocalUiTimeZone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final uiTimeZoneProvider = StateNotifierProvider<UiTimeZoneNotifier, UiTimeZone>(
|
||||||
|
(ref) => UiTimeZoneNotifier(
|
||||||
|
sharedPreferences: ref.watch(sharedPreferencesProvider),
|
||||||
|
),
|
||||||
|
dependencies: [sharedPreferencesProvider],
|
||||||
|
);
|
||||||
|
|
||||||
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
|
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
|
||||||
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
(_) => throw UnimplementedError('Please override in ProviderScope'),
|
||||||
);
|
);
|
||||||
|
|
||||||
final stationDataProvider = FutureProvider.family((ref, String stationName) async {
|
final stationDataProvider = FutureProvider.family((ref, ViewStationArguments args) async {
|
||||||
final data = await getStationData(stationName);
|
final data = await getStationData(args.stationName, args.date);
|
||||||
|
|
||||||
final timer = Timer(const Duration(minutes: 2), () {
|
final timer = Timer(const Duration(minutes: 2), () {
|
||||||
ref.invalidateSelf();
|
ref.invalidateSelf();
|
||||||
|
@ -51,6 +97,9 @@ final viewStationArgumentsProvider = Provider<ViewStationArguments>(
|
||||||
);
|
);
|
||||||
final viewStationDataProvider = Provider((ref) {
|
final viewStationDataProvider = Provider((ref) {
|
||||||
final args = ref.watch(viewStationArgumentsProvider);
|
final args = ref.watch(viewStationArgumentsProvider);
|
||||||
final data = ref.watch(stationDataProvider(args.stationName));
|
final data = ref.watch(stationDataProvider(args));
|
||||||
return data;
|
return data;
|
||||||
}, dependencies: [viewStationArgumentsProvider, stationDataProvider]);
|
}, dependencies: [viewStationArgumentsProvider, stationDataProvider]);
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
Future<TrainData> trainInfo(TrainInfoRef ref, {required String trainNumber, DateTime? date}) => getTrain(trainNumber, date: date);
|
||||||
|
|
120
lib/providers.g.dart
Normal file
120
lib/providers.g.dart
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'providers.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$trainInfoHash() => r'd25aabc3ba656acf6497ec6831e11892178b22c9';
|
||||||
|
|
||||||
|
/// Copied from Dart SDK
|
||||||
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
|
static int combine(int hash, int value) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + value);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
|
return hash ^ (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finish(int hash) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef TrainInfoRef = FutureProviderRef<TrainData>;
|
||||||
|
|
||||||
|
/// See also [trainInfo].
|
||||||
|
@ProviderFor(trainInfo)
|
||||||
|
const trainInfoProvider = TrainInfoFamily();
|
||||||
|
|
||||||
|
/// See also [trainInfo].
|
||||||
|
class TrainInfoFamily extends Family<AsyncValue<TrainData>> {
|
||||||
|
/// See also [trainInfo].
|
||||||
|
const TrainInfoFamily();
|
||||||
|
|
||||||
|
/// See also [trainInfo].
|
||||||
|
TrainInfoProvider call({
|
||||||
|
required String trainNumber,
|
||||||
|
DateTime? date,
|
||||||
|
}) {
|
||||||
|
return TrainInfoProvider(
|
||||||
|
trainNumber: trainNumber,
|
||||||
|
date: date,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TrainInfoProvider getProviderOverride(
|
||||||
|
covariant TrainInfoProvider provider,
|
||||||
|
) {
|
||||||
|
return call(
|
||||||
|
trainNumber: provider.trainNumber,
|
||||||
|
date: provider.date,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'trainInfoProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [trainInfo].
|
||||||
|
class TrainInfoProvider extends FutureProvider<TrainData> {
|
||||||
|
/// See also [trainInfo].
|
||||||
|
TrainInfoProvider({
|
||||||
|
required this.trainNumber,
|
||||||
|
this.date,
|
||||||
|
}) : super.internal(
|
||||||
|
(ref) => trainInfo(
|
||||||
|
ref,
|
||||||
|
trainNumber: trainNumber,
|
||||||
|
date: date,
|
||||||
|
),
|
||||||
|
from: trainInfoProvider,
|
||||||
|
name: r'trainInfoProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$trainInfoHash,
|
||||||
|
dependencies: TrainInfoFamily._dependencies,
|
||||||
|
allTransitiveDependencies: TrainInfoFamily._allTransitiveDependencies,
|
||||||
|
);
|
||||||
|
|
||||||
|
final String trainNumber;
|
||||||
|
final DateTime? date;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is TrainInfoProvider &&
|
||||||
|
other.trainNumber == trainNumber &&
|
||||||
|
other.date == date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, trainNumber.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, date.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions
|
|
@ -1,12 +1,7 @@
|
||||||
import 'package:info_tren/models.dart';
|
import 'package:info_tren/models.dart';
|
||||||
|
|
||||||
String stateToString(TrainDataState state) {
|
String stateToString(TrainDataState state) => switch (state) {
|
||||||
switch(state) {
|
TrainDataState.passing => 'trecere fără oprire',
|
||||||
case TrainDataState.PASSING:
|
TrainDataState.arrival => 'sosire',
|
||||||
return 'trecere fără oprire';
|
TrainDataState.departure => 'plecare',
|
||||||
case TrainDataState.ARRIVAL:
|
};
|
||||||
return 'sosire';
|
|
||||||
case TrainDataState.DEPARTURE:
|
|
||||||
return 'plecare';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,9 +6,13 @@
|
||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <dynamic_color/dynamic_color_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
|
||||||
|
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
dynamic_color
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
8
linux/infotren.desktop
Normal file
8
linux/infotren.desktop
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Encoding=UTF-8
|
||||||
|
Name=Info Tren
|
||||||
|
Comment=
|
||||||
|
Exec=
|
||||||
|
Icon=
|
||||||
|
Terminal=False
|
39
linux/install.sh
Executable file
39
linux/install.sh
Executable file
|
@ -0,0 +1,39 @@
|
||||||
|
#! /bin/sh
|
||||||
|
install_dir="$1"
|
||||||
|
if [ -z "$install_dir" ]; then
|
||||||
|
echo "Please specify a directory to install InfoTren to."
|
||||||
|
echo "Example:"
|
||||||
|
echo " $0 ~/infotren"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$install_dir" ]; then
|
||||||
|
if [ -d $(dirname "$install_dir") ]; then
|
||||||
|
mkdir "$install_dir"
|
||||||
|
else
|
||||||
|
echo "$install_dir doesn't exist. Please specify a directory to install InfoTren to."
|
||||||
|
echo "Example:"
|
||||||
|
echo " $0 ~/infotren"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f ./infotren.desktop ]; then
|
||||||
|
if [ -f "$(dirname $0)/infotren.desktop" ]; then
|
||||||
|
cd "$(dirname $0)"
|
||||||
|
else
|
||||||
|
echo "Run this script from inside the infotren directory."
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing InfoTren to $install_dir"
|
||||||
|
cp -r . "$install_dir"
|
||||||
|
if [ -z "$XDG_DATA_HOME" ]; then
|
||||||
|
XDG_DATA_HOME=~/.local/share
|
||||||
|
fi
|
||||||
|
if [ ! -d "$XDG_DATA_HOME/applications" ]; then
|
||||||
|
mkdir -p "$XDG_DATA_HOME/applications"
|
||||||
|
fi
|
||||||
|
echo "Installing infotren.desktop to $XDG_DATA_HOME/applications/infotren.desktop"
|
||||||
|
cat infotren.desktop | sed "s|Exec=|Exec=$install_dir/info_tren|g" > "$XDG_DATA_HOME/applications/infotren.desktop"
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|
40
macos/Podfile
Normal file
40
macos/Podfile
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
platform :osx, '10.14'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_macos_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
use_modular_headers!
|
||||||
|
|
||||||
|
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_macos_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
41
macos/Podfile.lock
Normal file
41
macos/Podfile.lock
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
PODS:
|
||||||
|
- dynamic_color (0.0.2):
|
||||||
|
- FlutterMacOS
|
||||||
|
- FlutterMacOS (1.0.0)
|
||||||
|
- package_info_plus (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- url_launcher_macos (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`)
|
||||||
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
|
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||||
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
dynamic_color:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos
|
||||||
|
FlutterMacOS:
|
||||||
|
:path: Flutter/ephemeral
|
||||||
|
package_info_plus:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||||
|
shared_preferences_foundation:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||||
|
url_launcher_macos:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f
|
||||||
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
|
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
|
||||||
|
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
|
||||||
|
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
|
||||||
|
|
||||||
|
COCOAPODS: 1.12.1
|
|
@ -3,7 +3,7 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 51;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXAggregateTarget section */
|
/* Begin PBXAggregateTarget section */
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
|
||||||
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
|
||||||
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
|
||||||
|
520B64251BD594DD8421C698 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE5B44A3963C12914D375BF2 /* Pods_Runner.framework */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -52,6 +53,7 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
0796070C1CDACAE0CA888A94 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||||
33CC10ED2044A3C60003C045 /* Info Tren.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Info Tren.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
33CC10ED2044A3C60003C045 /* Info Tren.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Info Tren.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -68,6 +70,9 @@
|
||||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
|
982BB43264C0B37351AE0773 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
BE5B44A3963C12914D375BF2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
FCA310A2438DD41F2A155FE5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -75,6 +80,7 @@
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
520B64251BD594DD8421C698 /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -99,6 +105,7 @@
|
||||||
33CEB47122A05771004F2AC0 /* Flutter */,
|
33CEB47122A05771004F2AC0 /* Flutter */,
|
||||||
33CC10EE2044A3C60003C045 /* Products */,
|
33CC10EE2044A3C60003C045 /* Products */,
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||||
|
EB1CD289E0F53193A2CB7A73 /* Pods */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -148,10 +155,22 @@
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BE5B44A3963C12914D375BF2 /* Pods_Runner.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
EB1CD289E0F53193A2CB7A73 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
FCA310A2438DD41F2A155FE5 /* Pods-Runner.debug.xcconfig */,
|
||||||
|
0796070C1CDACAE0CA888A94 /* Pods-Runner.release.xcconfig */,
|
||||||
|
982BB43264C0B37351AE0773 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
@ -159,11 +178,13 @@
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
85962951F5B916ED962A8FBD /* [CP] Check Pods Manifest.lock */,
|
||||||
33CC10E92044A3C60003C045 /* Sources */,
|
33CC10E92044A3C60003C045 /* Sources */,
|
||||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||||
33CC10EB2044A3C60003C045 /* Resources */,
|
33CC10EB2044A3C60003C045 /* Resources */,
|
||||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||||
|
907AE34F1B4BFF5379ADD9D0 /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -182,7 +203,7 @@
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0920;
|
LastSwiftUpdateCheck = 0920;
|
||||||
LastUpgradeCheck = 0930;
|
LastUpgradeCheck = 1300;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
33CC10EC2044A3C60003C045 = {
|
33CC10EC2044A3C60003C045 = {
|
||||||
|
@ -235,6 +256,7 @@
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
3399D490228B24CF009A79C7 /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
alwaysOutOfDate = 1;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -270,6 +292,45 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||||
};
|
};
|
||||||
|
85962951F5B916ED962A8FBD /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
907AE34F1B4BFF5379ADD9D0 /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
@ -344,7 +405,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
@ -423,7 +484,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
@ -470,7 +531,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.11;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1000"
|
LastUpgradeVersion = "1300"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
|
|
@ -4,4 +4,7 @@
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|
649
pubspec.lock
649
pubspec.lock
File diff suppressed because it is too large
Load diff
15
pubspec.yaml
15
pubspec.yaml
|
@ -11,10 +11,10 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform
|
||||||
# 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 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: 2.7.9
|
version: 2.7.11
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <3.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
|
@ -30,18 +30,23 @@ dependencies:
|
||||||
url_launcher: ^6.1.5
|
url_launcher: ^6.1.5
|
||||||
flutter_hooks: ^0.18.5+1
|
flutter_hooks: ^0.18.5+1
|
||||||
hooks_riverpod: ^2.0.2
|
hooks_riverpod: ^2.0.2
|
||||||
freezed_annotation: ^2.2.0
|
freezed_annotation: ^2.4.1
|
||||||
json_annotation: ^4.7.0
|
json_annotation: ^4.8.1
|
||||||
shared_preferences: ^2.0.15
|
shared_preferences: ^2.0.15
|
||||||
fluent_ui: ^4.0.3+1
|
fluent_ui: ^4.0.3+1
|
||||||
|
timezone: ^0.9.0
|
||||||
|
dynamic_color: ^1.6.6
|
||||||
|
riverpod_annotation: ^2.1.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
# flutter_test:
|
# flutter_test:
|
||||||
# sdk: flutter
|
# sdk: flutter
|
||||||
build_runner: ^2.1.0
|
build_runner: ^2.1.0
|
||||||
json_serializable: ^6.5.4
|
json_serializable: ^6.5.4
|
||||||
freezed: 2.2.0
|
freezed: ^2.4.1
|
||||||
flutter_lints: ^2.0.1
|
flutter_lints: ^2.0.1
|
||||||
|
riverpod_lint: ^1.3.2
|
||||||
|
riverpod_generator: ^2.2.3
|
||||||
|
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
|
|
17
windows/.gitignore
vendored
Normal file
17
windows/.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
flutter/ephemeral/
|
||||||
|
|
||||||
|
# Visual Studio user-specific files.
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Visual Studio build-related files.
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
101
windows/CMakeLists.txt
Normal file
101
windows/CMakeLists.txt
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
# Project-level configuration.
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(info_tren LANGUAGES CXX)
|
||||||
|
|
||||||
|
# The name of the executable created for the application. Change this to change
|
||||||
|
# the on-disk name of your application.
|
||||||
|
set(BINARY_NAME "info_tren")
|
||||||
|
|
||||||
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||||
|
# versions of CMake.
|
||||||
|
cmake_policy(SET CMP0063 NEW)
|
||||||
|
|
||||||
|
# Define build configuration option.
|
||||||
|
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
if(IS_MULTICONFIG)
|
||||||
|
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
|
||||||
|
CACHE STRING "" FORCE)
|
||||||
|
else()
|
||||||
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
|
set(CMAKE_BUILD_TYPE "Debug" CACHE
|
||||||
|
STRING "Flutter build mode" FORCE)
|
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
|
||||||
|
"Debug" "Profile" "Release")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
# Define settings for the Profile build mode.
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
|
||||||
|
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
|
||||||
|
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
|
||||||
|
# Use Unicode for all projects.
|
||||||
|
add_definitions(-DUNICODE -D_UNICODE)
|
||||||
|
|
||||||
|
# Compilation settings that should be applied to most targets.
|
||||||
|
#
|
||||||
|
# Be cautious about adding new options here, as plugins use this function by
|
||||||
|
# default. In most cases, you should add new options to specific targets instead
|
||||||
|
# of modifying this function.
|
||||||
|
function(APPLY_STANDARD_SETTINGS TARGET)
|
||||||
|
target_compile_features(${TARGET} PUBLIC cxx_std_17)
|
||||||
|
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
|
||||||
|
target_compile_options(${TARGET} PRIVATE /EHsc)
|
||||||
|
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
|
||||||
|
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Flutter library and tool build rules.
|
||||||
|
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
|
||||||
|
add_subdirectory(${FLUTTER_MANAGED_DIR})
|
||||||
|
|
||||||
|
# Application build; see runner/CMakeLists.txt.
|
||||||
|
add_subdirectory("runner")
|
||||||
|
|
||||||
|
# Generated plugin build rules, which manage building the plugins and adding
|
||||||
|
# them to the application.
|
||||||
|
include(flutter/generated_plugins.cmake)
|
||||||
|
|
||||||
|
|
||||||
|
# === Installation ===
|
||||||
|
# Support files are copied into place next to the executable, so that it can
|
||||||
|
# run in place. This is done instead of making a separate bundle (as on Linux)
|
||||||
|
# so that building and running from within Visual Studio will work.
|
||||||
|
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
|
||||||
|
# Make the "install" step default, as it's required to run.
|
||||||
|
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
|
||||||
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
|
||||||
|
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
|
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
|
||||||
|
if(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
|
||||||
|
COMPONENT Runtime)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Fully re-copy the assets directory on each build to avoid having stale files
|
||||||
|
# from a previous install.
|
||||||
|
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
|
||||||
|
install(CODE "
|
||||||
|
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
|
||||||
|
" COMPONENT Runtime)
|
||||||
|
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
|
||||||
|
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
|
||||||
|
|
||||||
|
# Install the AOT library on non-Debug builds only.
|
||||||
|
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
|
||||||
|
CONFIGURATIONS Profile;Release
|
||||||
|
COMPONENT Runtime)
|
104
windows/flutter/CMakeLists.txt
Normal file
104
windows/flutter/CMakeLists.txt
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# This file controls Flutter-level build steps. It should not be edited.
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
|
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
|
||||||
|
|
||||||
|
# Configuration provided via flutter tool.
|
||||||
|
include(${EPHEMERAL_DIR}/generated_config.cmake)
|
||||||
|
|
||||||
|
# TODO: Move the rest of this into files in ephemeral. See
|
||||||
|
# https://github.com/flutter/flutter/issues/57146.
|
||||||
|
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
|
||||||
|
|
||||||
|
# === Flutter Library ===
|
||||||
|
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
|
||||||
|
|
||||||
|
# Published to parent scope for install step.
|
||||||
|
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
|
||||||
|
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
|
||||||
|
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
|
||||||
|
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_LIBRARY_HEADERS
|
||||||
|
"flutter_export.h"
|
||||||
|
"flutter_windows.h"
|
||||||
|
"flutter_messenger.h"
|
||||||
|
"flutter_plugin_registrar.h"
|
||||||
|
"flutter_texture_registrar.h"
|
||||||
|
)
|
||||||
|
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
|
||||||
|
add_library(flutter INTERFACE)
|
||||||
|
target_include_directories(flutter INTERFACE
|
||||||
|
"${EPHEMERAL_DIR}"
|
||||||
|
)
|
||||||
|
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
|
||||||
|
add_dependencies(flutter flutter_assemble)
|
||||||
|
|
||||||
|
# === Wrapper ===
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_CORE
|
||||||
|
"core_implementations.cc"
|
||||||
|
"standard_codec.cc"
|
||||||
|
)
|
||||||
|
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
|
||||||
|
"plugin_registrar.cc"
|
||||||
|
)
|
||||||
|
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
|
||||||
|
list(APPEND CPP_WRAPPER_SOURCES_APP
|
||||||
|
"flutter_engine.cc"
|
||||||
|
"flutter_view_controller.cc"
|
||||||
|
)
|
||||||
|
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
|
||||||
|
|
||||||
|
# Wrapper sources needed for a plugin.
|
||||||
|
add_library(flutter_wrapper_plugin STATIC
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
|
)
|
||||||
|
apply_standard_settings(flutter_wrapper_plugin)
|
||||||
|
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||||
|
POSITION_INDEPENDENT_CODE ON)
|
||||||
|
set_target_properties(flutter_wrapper_plugin PROPERTIES
|
||||||
|
CXX_VISIBILITY_PRESET hidden)
|
||||||
|
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
|
||||||
|
target_include_directories(flutter_wrapper_plugin PUBLIC
|
||||||
|
"${WRAPPER_ROOT}/include"
|
||||||
|
)
|
||||||
|
add_dependencies(flutter_wrapper_plugin flutter_assemble)
|
||||||
|
|
||||||
|
# Wrapper sources needed for the runner.
|
||||||
|
add_library(flutter_wrapper_app STATIC
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
|
)
|
||||||
|
apply_standard_settings(flutter_wrapper_app)
|
||||||
|
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
|
||||||
|
target_include_directories(flutter_wrapper_app PUBLIC
|
||||||
|
"${WRAPPER_ROOT}/include"
|
||||||
|
)
|
||||||
|
add_dependencies(flutter_wrapper_app flutter_assemble)
|
||||||
|
|
||||||
|
# === Flutter tool backend ===
|
||||||
|
# _phony_ is a non-existent file to force this command to run every time,
|
||||||
|
# since currently there's no way to get a full input/output list from the
|
||||||
|
# flutter tool.
|
||||||
|
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
|
||||||
|
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
|
${PHONY_OUTPUT}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E env
|
||||||
|
${FLUTTER_TOOL_ENVIRONMENT}
|
||||||
|
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
|
||||||
|
windows-x64 $<CONFIG>
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
add_custom_target(flutter_assemble DEPENDS
|
||||||
|
"${FLUTTER_LIBRARY}"
|
||||||
|
${FLUTTER_LIBRARY_HEADERS}
|
||||||
|
${CPP_WRAPPER_SOURCES_CORE}
|
||||||
|
${CPP_WRAPPER_SOURCES_PLUGIN}
|
||||||
|
${CPP_WRAPPER_SOURCES_APP}
|
||||||
|
)
|
17
windows/flutter/generated_plugin_registrant.cc
Normal file
17
windows/flutter/generated_plugin_registrant.cc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <dynamic_color/dynamic_color_plugin_c_api.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
DynamicColorPluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
|
}
|
15
windows/flutter/generated_plugin_registrant.h
Normal file
15
windows/flutter/generated_plugin_registrant.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#ifndef GENERATED_PLUGIN_REGISTRANT_
|
||||||
|
#define GENERATED_PLUGIN_REGISTRANT_
|
||||||
|
|
||||||
|
#include <flutter/plugin_registry.h>
|
||||||
|
|
||||||
|
// Registers Flutter plugins.
|
||||||
|
void RegisterPlugins(flutter::PluginRegistry* registry);
|
||||||
|
|
||||||
|
#endif // GENERATED_PLUGIN_REGISTRANT_
|
25
windows/flutter/generated_plugins.cmake
Normal file
25
windows/flutter/generated_plugins.cmake
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#
|
||||||
|
# Generated file, do not edit.
|
||||||
|
#
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
dynamic_color
|
||||||
|
url_launcher_windows
|
||||||
|
)
|
||||||
|
|
||||||
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
)
|
||||||
|
|
||||||
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|
||||||
|
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||||
|
endforeach(plugin)
|
||||||
|
|
||||||
|
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
|
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||||
|
endforeach(ffi_plugin)
|
39
windows/runner/CMakeLists.txt
Normal file
39
windows/runner/CMakeLists.txt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
project(runner LANGUAGES CXX)
|
||||||
|
|
||||||
|
# Define the application target. To change its name, change BINARY_NAME in the
|
||||||
|
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
|
||||||
|
# work.
|
||||||
|
#
|
||||||
|
# Any new source files that you add to the application should be added here.
|
||||||
|
add_executable(${BINARY_NAME} WIN32
|
||||||
|
"flutter_window.cpp"
|
||||||
|
"main.cpp"
|
||||||
|
"utils.cpp"
|
||||||
|
"win32_window.cpp"
|
||||||
|
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
|
||||||
|
"Runner.rc"
|
||||||
|
"runner.exe.manifest"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Apply the standard set of build settings. This can be removed for applications
|
||||||
|
# that need different build settings.
|
||||||
|
apply_standard_settings(${BINARY_NAME})
|
||||||
|
|
||||||
|
# Add preprocessor definitions for the build version.
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
|
||||||
|
|
||||||
|
# Disable Windows macros that collide with C++ standard library functions.
|
||||||
|
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
|
||||||
|
|
||||||
|
# Add dependency libraries and include directories. Add any application-specific
|
||||||
|
# dependencies here.
|
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
|
||||||
|
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||||
|
|
||||||
|
# Run the Flutter tool portions of the build. This must not be removed.
|
||||||
|
add_dependencies(${BINARY_NAME} flutter_assemble)
|
121
windows/runner/Runner.rc
Normal file
121
windows/runner/Runner.rc
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
// Microsoft Visual C++ generated resource script.
|
||||||
|
//
|
||||||
|
#pragma code_page(65001)
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 2 resource.
|
||||||
|
//
|
||||||
|
#include "winres.h"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// English (United States) resources
|
||||||
|
|
||||||
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TEXTINCLUDE
|
||||||
|
//
|
||||||
|
|
||||||
|
1 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"resource.h\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
2 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"#include ""winres.h""\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
3 TEXTINCLUDE
|
||||||
|
BEGIN
|
||||||
|
"\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Icon
|
||||||
|
//
|
||||||
|
|
||||||
|
// Icon with lowest ID value placed first to ensure application icon
|
||||||
|
// remains consistent on all systems.
|
||||||
|
IDI_APP_ICON ICON "resources\\app_icon.ico"
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Version
|
||||||
|
//
|
||||||
|
|
||||||
|
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
|
||||||
|
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
|
||||||
|
#else
|
||||||
|
#define VERSION_AS_NUMBER 1,0,0,0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(FLUTTER_VERSION)
|
||||||
|
#define VERSION_AS_STRING FLUTTER_VERSION
|
||||||
|
#else
|
||||||
|
#define VERSION_AS_STRING "1.0.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VS_VERSION_INFO VERSIONINFO
|
||||||
|
FILEVERSION VERSION_AS_NUMBER
|
||||||
|
PRODUCTVERSION VERSION_AS_NUMBER
|
||||||
|
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||||
|
#ifdef _DEBUG
|
||||||
|
FILEFLAGS VS_FF_DEBUG
|
||||||
|
#else
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
#endif
|
||||||
|
FILEOS VOS__WINDOWS32
|
||||||
|
FILETYPE VFT_APP
|
||||||
|
FILESUBTYPE 0x0L
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904e4"
|
||||||
|
BEGIN
|
||||||
|
VALUE "CompanyName", "ro.dcdev" "\0"
|
||||||
|
VALUE "FileDescription", "info_tren" "\0"
|
||||||
|
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||||
|
VALUE "InternalName", "info_tren" "\0"
|
||||||
|
VALUE "LegalCopyright", "Copyright (C) 2022 ro.dcdev. All rights reserved." "\0"
|
||||||
|
VALUE "OriginalFilename", "info_tren.exe" "\0"
|
||||||
|
VALUE "ProductName", "info_tren" "\0"
|
||||||
|
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x409, 1252
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // English (United States) resources
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 3 resource.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#endif // not APSTUDIO_INVOKED
|
61
windows/runner/flutter_window.cpp
Normal file
61
windows/runner/flutter_window.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include "flutter_window.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "flutter/generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
|
||||||
|
: project_(project) {}
|
||||||
|
|
||||||
|
FlutterWindow::~FlutterWindow() {}
|
||||||
|
|
||||||
|
bool FlutterWindow::OnCreate() {
|
||||||
|
if (!Win32Window::OnCreate()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT frame = GetClientArea();
|
||||||
|
|
||||||
|
// The size here must match the window dimensions to avoid unnecessary surface
|
||||||
|
// creation / destruction in the startup path.
|
||||||
|
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
|
||||||
|
frame.right - frame.left, frame.bottom - frame.top, project_);
|
||||||
|
// Ensure that basic setup of the controller was successful.
|
||||||
|
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
RegisterPlugins(flutter_controller_->engine());
|
||||||
|
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlutterWindow::OnDestroy() {
|
||||||
|
if (flutter_controller_) {
|
||||||
|
flutter_controller_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Win32Window::OnDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT
|
||||||
|
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
|
||||||
|
WPARAM const wparam,
|
||||||
|
LPARAM const lparam) noexcept {
|
||||||
|
// Give Flutter, including plugins, an opportunity to handle window messages.
|
||||||
|
if (flutter_controller_) {
|
||||||
|
std::optional<LRESULT> result =
|
||||||
|
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
|
||||||
|
lparam);
|
||||||
|
if (result) {
|
||||||
|
return *result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (message) {
|
||||||
|
case WM_FONTCHANGE:
|
||||||
|
flutter_controller_->engine()->ReloadSystemFonts();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
|
||||||
|
}
|
33
windows/runner/flutter_window.h
Normal file
33
windows/runner/flutter_window.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef RUNNER_FLUTTER_WINDOW_H_
|
||||||
|
#define RUNNER_FLUTTER_WINDOW_H_
|
||||||
|
|
||||||
|
#include <flutter/dart_project.h>
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "win32_window.h"
|
||||||
|
|
||||||
|
// A window that does nothing but host a Flutter view.
|
||||||
|
class FlutterWindow : public Win32Window {
|
||||||
|
public:
|
||||||
|
// Creates a new FlutterWindow hosting a Flutter view running |project|.
|
||||||
|
explicit FlutterWindow(const flutter::DartProject& project);
|
||||||
|
virtual ~FlutterWindow();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Win32Window:
|
||||||
|
bool OnCreate() override;
|
||||||
|
void OnDestroy() override;
|
||||||
|
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
|
||||||
|
LPARAM const lparam) noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The project to run.
|
||||||
|
flutter::DartProject project_;
|
||||||
|
|
||||||
|
// The Flutter instance hosted by this window.
|
||||||
|
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RUNNER_FLUTTER_WINDOW_H_
|
43
windows/runner/main.cpp
Normal file
43
windows/runner/main.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include <flutter/dart_project.h>
|
||||||
|
#include <flutter/flutter_view_controller.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "flutter_window.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
||||||
|
_In_ wchar_t *command_line, _In_ int show_command) {
|
||||||
|
// Attach to console when present (e.g., 'flutter run') or create a
|
||||||
|
// new console when running with a debugger.
|
||||||
|
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
|
||||||
|
CreateAndAttachConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize COM, so that it is available for use in the library and/or
|
||||||
|
// plugins.
|
||||||
|
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
|
|
||||||
|
flutter::DartProject project(L"data");
|
||||||
|
|
||||||
|
std::vector<std::string> command_line_arguments =
|
||||||
|
GetCommandLineArguments();
|
||||||
|
|
||||||
|
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
|
||||||
|
|
||||||
|
FlutterWindow window(project);
|
||||||
|
Win32Window::Point origin(10, 10);
|
||||||
|
Win32Window::Size size(1280, 720);
|
||||||
|
if (!window.CreateAndShow(L"Info Tren", origin, size)) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
window.SetQuitOnClose(true);
|
||||||
|
|
||||||
|
::MSG msg;
|
||||||
|
while (::GetMessage(&msg, nullptr, 0, 0)) {
|
||||||
|
::TranslateMessage(&msg);
|
||||||
|
::DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
::CoUninitialize();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
16
windows/runner/resource.h
Normal file
16
windows/runner/resource.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Visual C++ generated include file.
|
||||||
|
// Used by Runner.rc
|
||||||
|
//
|
||||||
|
#define IDI_APP_ICON 101
|
||||||
|
|
||||||
|
// Next default values for new objects
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
#endif
|
||||||
|
#endif
|
BIN
windows/runner/resources/app_icon.ico
Normal file
BIN
windows/runner/resources/app_icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
20
windows/runner/runner.exe.manifest
Normal file
20
windows/runner/runner.exe.manifest
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<windowsSettings>
|
||||||
|
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||||
|
</windowsSettings>
|
||||||
|
</application>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows 10 and Windows 11 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
</assembly>
|
64
windows/runner/utils.cpp
Normal file
64
windows/runner/utils.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <flutter_windows.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void CreateAndAttachConsole() {
|
||||||
|
if (::AllocConsole()) {
|
||||||
|
FILE *unused;
|
||||||
|
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
|
||||||
|
_dup2(_fileno(stdout), 1);
|
||||||
|
}
|
||||||
|
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
|
||||||
|
_dup2(_fileno(stdout), 2);
|
||||||
|
}
|
||||||
|
std::ios::sync_with_stdio();
|
||||||
|
FlutterDesktopResyncOutputStreams();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> GetCommandLineArguments() {
|
||||||
|
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
|
||||||
|
int argc;
|
||||||
|
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
|
||||||
|
if (argv == nullptr) {
|
||||||
|
return std::vector<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> command_line_arguments;
|
||||||
|
|
||||||
|
// Skip the first argument as it's the binary name.
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
::LocalFree(argv);
|
||||||
|
|
||||||
|
return command_line_arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
|
||||||
|
if (utf16_string == nullptr) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
int target_length = ::WideCharToMultiByte(
|
||||||
|
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||||
|
-1, nullptr, 0, nullptr, nullptr);
|
||||||
|
std::string utf8_string;
|
||||||
|
if (target_length == 0 || target_length > utf8_string.max_size()) {
|
||||||
|
return utf8_string;
|
||||||
|
}
|
||||||
|
utf8_string.resize(target_length);
|
||||||
|
int converted_length = ::WideCharToMultiByte(
|
||||||
|
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
|
||||||
|
-1, utf8_string.data(),
|
||||||
|
target_length, nullptr, nullptr);
|
||||||
|
if (converted_length == 0) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
return utf8_string;
|
||||||
|
}
|
19
windows/runner/utils.h
Normal file
19
windows/runner/utils.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef RUNNER_UTILS_H_
|
||||||
|
#define RUNNER_UTILS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Creates a console for the process, and redirects stdout and stderr to
|
||||||
|
// it for both the runner and the Flutter library.
|
||||||
|
void CreateAndAttachConsole();
|
||||||
|
|
||||||
|
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
|
||||||
|
// encoded in UTF-8. Returns an empty std::string on failure.
|
||||||
|
std::string Utf8FromUtf16(const wchar_t* utf16_string);
|
||||||
|
|
||||||
|
// Gets the command line arguments passed in as a std::vector<std::string>,
|
||||||
|
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
|
||||||
|
std::vector<std::string> GetCommandLineArguments();
|
||||||
|
|
||||||
|
#endif // RUNNER_UTILS_H_
|
245
windows/runner/win32_window.cpp
Normal file
245
windows/runner/win32_window.cpp
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
#include "win32_window.h"
|
||||||
|
|
||||||
|
#include <flutter_windows.h>
|
||||||
|
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
|
||||||
|
|
||||||
|
// The number of Win32Window objects that currently exist.
|
||||||
|
static int g_active_window_count = 0;
|
||||||
|
|
||||||
|
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
|
||||||
|
|
||||||
|
// Scale helper to convert logical scaler values to physical using passed in
|
||||||
|
// scale factor
|
||||||
|
int Scale(int source, double scale_factor) {
|
||||||
|
return static_cast<int>(source * scale_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
|
||||||
|
// This API is only needed for PerMonitor V1 awareness mode.
|
||||||
|
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
|
||||||
|
HMODULE user32_module = LoadLibraryA("User32.dll");
|
||||||
|
if (!user32_module) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto enable_non_client_dpi_scaling =
|
||||||
|
reinterpret_cast<EnableNonClientDpiScaling*>(
|
||||||
|
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
|
||||||
|
if (enable_non_client_dpi_scaling != nullptr) {
|
||||||
|
enable_non_client_dpi_scaling(hwnd);
|
||||||
|
FreeLibrary(user32_module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Manages the Win32Window's window class registration.
|
||||||
|
class WindowClassRegistrar {
|
||||||
|
public:
|
||||||
|
~WindowClassRegistrar() = default;
|
||||||
|
|
||||||
|
// Returns the singleton registar instance.
|
||||||
|
static WindowClassRegistrar* GetInstance() {
|
||||||
|
if (!instance_) {
|
||||||
|
instance_ = new WindowClassRegistrar();
|
||||||
|
}
|
||||||
|
return instance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the name of the window class, registering the class if it hasn't
|
||||||
|
// previously been registered.
|
||||||
|
const wchar_t* GetWindowClass();
|
||||||
|
|
||||||
|
// Unregisters the window class. Should only be called if there are no
|
||||||
|
// instances of the window.
|
||||||
|
void UnregisterWindowClass();
|
||||||
|
|
||||||
|
private:
|
||||||
|
WindowClassRegistrar() = default;
|
||||||
|
|
||||||
|
static WindowClassRegistrar* instance_;
|
||||||
|
|
||||||
|
bool class_registered_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
|
||||||
|
|
||||||
|
const wchar_t* WindowClassRegistrar::GetWindowClass() {
|
||||||
|
if (!class_registered_) {
|
||||||
|
WNDCLASS window_class{};
|
||||||
|
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||||
|
window_class.lpszClassName = kWindowClassName;
|
||||||
|
window_class.style = CS_HREDRAW | CS_VREDRAW;
|
||||||
|
window_class.cbClsExtra = 0;
|
||||||
|
window_class.cbWndExtra = 0;
|
||||||
|
window_class.hInstance = GetModuleHandle(nullptr);
|
||||||
|
window_class.hIcon =
|
||||||
|
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
|
||||||
|
window_class.hbrBackground = 0;
|
||||||
|
window_class.lpszMenuName = nullptr;
|
||||||
|
window_class.lpfnWndProc = Win32Window::WndProc;
|
||||||
|
RegisterClass(&window_class);
|
||||||
|
class_registered_ = true;
|
||||||
|
}
|
||||||
|
return kWindowClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowClassRegistrar::UnregisterWindowClass() {
|
||||||
|
UnregisterClass(kWindowClassName, nullptr);
|
||||||
|
class_registered_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Win32Window::Win32Window() {
|
||||||
|
++g_active_window_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Win32Window::~Win32Window() {
|
||||||
|
--g_active_window_count;
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Win32Window::CreateAndShow(const std::wstring& title,
|
||||||
|
const Point& origin,
|
||||||
|
const Size& size) {
|
||||||
|
Destroy();
|
||||||
|
|
||||||
|
const wchar_t* window_class =
|
||||||
|
WindowClassRegistrar::GetInstance()->GetWindowClass();
|
||||||
|
|
||||||
|
const POINT target_point = {static_cast<LONG>(origin.x),
|
||||||
|
static_cast<LONG>(origin.y)};
|
||||||
|
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
|
||||||
|
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
|
||||||
|
double scale_factor = dpi / 96.0;
|
||||||
|
|
||||||
|
HWND window = CreateWindow(
|
||||||
|
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||||||
|
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
|
||||||
|
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
|
||||||
|
nullptr, nullptr, GetModuleHandle(nullptr), this);
|
||||||
|
|
||||||
|
if (!window) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OnCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
|
||||||
|
UINT const message,
|
||||||
|
WPARAM const wparam,
|
||||||
|
LPARAM const lparam) noexcept {
|
||||||
|
if (message == WM_NCCREATE) {
|
||||||
|
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
|
||||||
|
SetWindowLongPtr(window, GWLP_USERDATA,
|
||||||
|
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
|
||||||
|
|
||||||
|
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
|
||||||
|
EnableFullDpiSupportIfAvailable(window);
|
||||||
|
that->window_handle_ = window;
|
||||||
|
} else if (Win32Window* that = GetThisFromHandle(window)) {
|
||||||
|
return that->MessageHandler(window, message, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(window, message, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT
|
||||||
|
Win32Window::MessageHandler(HWND hwnd,
|
||||||
|
UINT const message,
|
||||||
|
WPARAM const wparam,
|
||||||
|
LPARAM const lparam) noexcept {
|
||||||
|
switch (message) {
|
||||||
|
case WM_DESTROY:
|
||||||
|
window_handle_ = nullptr;
|
||||||
|
Destroy();
|
||||||
|
if (quit_on_close_) {
|
||||||
|
PostQuitMessage(0);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WM_DPICHANGED: {
|
||||||
|
auto newRectSize = reinterpret_cast<RECT*>(lparam);
|
||||||
|
LONG newWidth = newRectSize->right - newRectSize->left;
|
||||||
|
LONG newHeight = newRectSize->bottom - newRectSize->top;
|
||||||
|
|
||||||
|
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
|
||||||
|
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case WM_SIZE: {
|
||||||
|
RECT rect = GetClientArea();
|
||||||
|
if (child_content_ != nullptr) {
|
||||||
|
// Size and position the child window.
|
||||||
|
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
|
||||||
|
rect.bottom - rect.top, TRUE);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_ACTIVATE:
|
||||||
|
if (child_content_ != nullptr) {
|
||||||
|
SetFocus(child_content_);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProc(window_handle_, message, wparam, lparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32Window::Destroy() {
|
||||||
|
OnDestroy();
|
||||||
|
|
||||||
|
if (window_handle_) {
|
||||||
|
DestroyWindow(window_handle_);
|
||||||
|
window_handle_ = nullptr;
|
||||||
|
}
|
||||||
|
if (g_active_window_count == 0) {
|
||||||
|
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
|
||||||
|
return reinterpret_cast<Win32Window*>(
|
||||||
|
GetWindowLongPtr(window, GWLP_USERDATA));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32Window::SetChildContent(HWND content) {
|
||||||
|
child_content_ = content;
|
||||||
|
SetParent(content, window_handle_);
|
||||||
|
RECT frame = GetClientArea();
|
||||||
|
|
||||||
|
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
|
||||||
|
frame.bottom - frame.top, true);
|
||||||
|
|
||||||
|
SetFocus(child_content_);
|
||||||
|
}
|
||||||
|
|
||||||
|
RECT Win32Window::GetClientArea() {
|
||||||
|
RECT frame;
|
||||||
|
GetClientRect(window_handle_, &frame);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND Win32Window::GetHandle() {
|
||||||
|
return window_handle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32Window::SetQuitOnClose(bool quit_on_close) {
|
||||||
|
quit_on_close_ = quit_on_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Win32Window::OnCreate() {
|
||||||
|
// No-op; provided for subclasses.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Win32Window::OnDestroy() {
|
||||||
|
// No-op; provided for subclasses.
|
||||||
|
}
|
98
windows/runner/win32_window.h
Normal file
98
windows/runner/win32_window.h
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#ifndef RUNNER_WIN32_WINDOW_H_
|
||||||
|
#define RUNNER_WIN32_WINDOW_H_
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
|
||||||
|
// inherited from by classes that wish to specialize with custom
|
||||||
|
// rendering and input handling
|
||||||
|
class Win32Window {
|
||||||
|
public:
|
||||||
|
struct Point {
|
||||||
|
unsigned int x;
|
||||||
|
unsigned int y;
|
||||||
|
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Size {
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
Size(unsigned int width, unsigned int height)
|
||||||
|
: width(width), height(height) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Win32Window();
|
||||||
|
virtual ~Win32Window();
|
||||||
|
|
||||||
|
// Creates and shows a win32 window with |title| and position and size using
|
||||||
|
// |origin| and |size|. New windows are created on the default monitor. Window
|
||||||
|
// sizes are specified to the OS in physical pixels, hence to ensure a
|
||||||
|
// consistent size to will treat the width height passed in to this function
|
||||||
|
// as logical pixels and scale to appropriate for the default monitor. Returns
|
||||||
|
// true if the window was created successfully.
|
||||||
|
bool CreateAndShow(const std::wstring& title,
|
||||||
|
const Point& origin,
|
||||||
|
const Size& size);
|
||||||
|
|
||||||
|
// Release OS resources associated with window.
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
// Inserts |content| into the window tree.
|
||||||
|
void SetChildContent(HWND content);
|
||||||
|
|
||||||
|
// Returns the backing Window handle to enable clients to set icon and other
|
||||||
|
// window properties. Returns nullptr if the window has been destroyed.
|
||||||
|
HWND GetHandle();
|
||||||
|
|
||||||
|
// If true, closing this window will quit the application.
|
||||||
|
void SetQuitOnClose(bool quit_on_close);
|
||||||
|
|
||||||
|
// Return a RECT representing the bounds of the current client area.
|
||||||
|
RECT GetClientArea();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Processes and route salient window messages for mouse handling,
|
||||||
|
// size change and DPI. Delegates handling of these to member overloads that
|
||||||
|
// inheriting classes can handle.
|
||||||
|
virtual LRESULT MessageHandler(HWND window,
|
||||||
|
UINT const message,
|
||||||
|
WPARAM const wparam,
|
||||||
|
LPARAM const lparam) noexcept;
|
||||||
|
|
||||||
|
// Called when CreateAndShow is called, allowing subclass window-related
|
||||||
|
// setup. Subclasses should return false if setup fails.
|
||||||
|
virtual bool OnCreate();
|
||||||
|
|
||||||
|
// Called when Destroy is called.
|
||||||
|
virtual void OnDestroy();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class WindowClassRegistrar;
|
||||||
|
|
||||||
|
// OS callback called by message pump. Handles the WM_NCCREATE message which
|
||||||
|
// is passed when the non-client area is being created and enables automatic
|
||||||
|
// non-client DPI scaling so that the non-client area automatically
|
||||||
|
// responsponds to changes in DPI. All other messages are handled by
|
||||||
|
// MessageHandler.
|
||||||
|
static LRESULT CALLBACK WndProc(HWND const window,
|
||||||
|
UINT const message,
|
||||||
|
WPARAM const wparam,
|
||||||
|
LPARAM const lparam) noexcept;
|
||||||
|
|
||||||
|
// Retrieves a class instance pointer for |window|
|
||||||
|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
|
||||||
|
|
||||||
|
bool quit_on_close_ = false;
|
||||||
|
|
||||||
|
// window handle for top level window.
|
||||||
|
HWND window_handle_ = nullptr;
|
||||||
|
|
||||||
|
// window handle for hosted content.
|
||||||
|
HWND child_content_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RUNNER_WIN32_WINDOW_H_
|
Loading…
Add table
Reference in a new issue