Transition WebView to API #1
|
@ -1,3 +1,8 @@
|
|||
v2.0.7
|
||||
Switched from WebView to API
|
||||
Updated app to latest Flutter
|
||||
Tweaks
|
||||
|
||||
v2.0.6
|
||||
Brought feature parity with iOS _(except for v2.0.2, which is iOS specific)_.
|
||||
|
||||
|
|
29
analysis_options.yaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
# This file configures the analyzer, which statically analyzes Dart code to
|
||||
# check for errors, warnings, and lints.
|
||||
#
|
||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||
# invoked from the command line by running `flutter analyze`.
|
||||
|
||||
# The following line activates a set of recommended lints for Flutter apps,
|
||||
# packages, and plugins designed to encourage good coding practices.
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
linter:
|
||||
# The lint rules applied to this project can be customized in the
|
||||
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||
# included above or to enable additional rules. A list of all available lints
|
||||
# and their documentation is published at
|
||||
# https://dart-lang.github.io/linter/lints/index.html.
|
||||
#
|
||||
# Instead of disabling a lint rule for the entire project in the
|
||||
# section below, it can also be suppressed for a single line of code
|
||||
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||
# producing the lint.
|
||||
rules:
|
||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
13
android/.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
|
@ -38,7 +38,7 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "ml.dandevelop.info_tren"
|
||||
applicationId "xyz.dcdevelop.infotren"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package xyz.dcdevelop.info_tren
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
12
android/app/src/main/res/drawable-v21/launch_background.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
18
android/app/src/main/res/values-night/styles.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
BIN
fonts/ah/ah-Bold.ttf
Normal file
BIN
fonts/ah/ah-BoldItalic.ttf
Normal file
BIN
fonts/ah/ah-Italic.ttf
Normal file
BIN
fonts/ah/ah-Regular.ttf
Normal file
33
ios/.gitignore
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
*.perspectivev3
|
||||
**/*sync/
|
||||
.sconsign.dblite
|
||||
.tags*
|
||||
**/.vagrant/
|
||||
**/DerivedData/
|
||||
Icon?
|
||||
**/Pods/
|
||||
**/.symlinks/
|
||||
profile
|
||||
xcuserdata
|
||||
**/.generated/
|
||||
Flutter/App.framework
|
||||
Flutter/Flutter.framework
|
||||
Flutter/Flutter.podspec
|
||||
Flutter/Generated.xcconfig
|
||||
Flutter/ephemeral/
|
||||
Flutter/app.flx
|
||||
Flutter/app.zip
|
||||
Flutter/flutter_assets/
|
||||
Flutter/flutter_export_environment.sh
|
||||
ServiceDefinitions.json
|
||||
Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.pbxuser
|
||||
!default.perspectivev3
|
|
@ -3,7 +3,7 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>App</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
@ -21,6 +21,6 @@
|
|||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>8.0</string>
|
||||
<string>9.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
#
|
||||
# NOTE: This podspec is NOT to be published. It is only used as a local source!
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
#
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'Flutter'
|
||||
s.version = '1.0.0'
|
||||
s.summary = 'High-performance, high-fidelity mobile apps.'
|
||||
s.homepage = 'https://flutter.io'
|
||||
s.license = { :type => 'MIT' }
|
||||
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
|
||||
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
|
||||
s.ios.deployment_target = '8.0'
|
||||
# Framework linking is handled by Flutter tooling, not CocoaPods.
|
||||
# Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs.
|
||||
s.vendored_frameworks = 'path/to/nothing'
|
||||
end
|
|
@ -1,2 +1 @@
|
|||
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
|
|
|
@ -5,7 +5,6 @@ export "FLUTTER_APPLICATION_PATH=/Users/dan.cojocaru/info_tren"
|
|||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_TARGET=/Users/dan.cojocaru/info_tren/lib/main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
|
||||
export "FLUTTER_BUILD_NAME=2.0.6"
|
||||
export "FLUTTER_BUILD_NUMBER=2.0.6"
|
||||
export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ=="
|
||||
|
|
41
ios/Podfile
|
@ -1,41 +0,0 @@
|
|||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
|
||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||
|
||||
project 'Runner', {
|
||||
'Debug' => :debug,
|
||||
'Profile' => :release,
|
||||
'Release' => :release,
|
||||
}
|
||||
|
||||
def flutter_root
|
||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||
unless File.exist?(generated_xcode_build_settings_path)
|
||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||
end
|
||||
|
||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||
return matches[1].strip if matches
|
||||
end
|
||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||
end
|
||||
|
||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||
|
||||
flutter_ios_podfile_setup
|
||||
|
||||
target 'Runner' do
|
||||
use_frameworks!
|
||||
use_modular_headers!
|
||||
|
||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
end
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
PODS:
|
||||
- Flutter (1.0.0)
|
||||
- webview_flutter (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- webview_flutter (from `.symlinks/plugins/webview_flutter/ios`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
webview_flutter:
|
||||
:path: ".symlinks/plugins/webview_flutter/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
|
||||
webview_flutter: 1aa7604e6cdb451a9b7ed2c37d5454c0b440246b
|
||||
|
||||
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
|
||||
|
||||
COCOAPODS: 1.10.1
|
|
@ -3,15 +3,13 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
722F441253D3B79676E4DE80 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F72320B12B1F4015789BBC8E /* Pods_Runner.framework */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||
|
@ -33,12 +31,9 @@
|
|||
/* Begin PBXFileReference section */
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
313F1E773DA06364A0C4F20A /* 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>"; };
|
||||
636963D381657D3BAEDC0A47 /* 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>"; };
|
||||
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>"; };
|
||||
74CD890ACD2E394E606FCBEB /* 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>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||
|
@ -47,7 +42,6 @@
|
|||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
F72320B12B1F4015789BBC8E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -55,21 +49,12 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
722F441253D3B79676E4DE80 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0B24EBF53F1DCC708FA961FD /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F72320B12B1F4015789BBC8E /* Pods_Runner.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -87,8 +72,6 @@
|
|||
9740EEB11CF90186004384FC /* Flutter */,
|
||||
97C146F01CF9000F007C117D /* Runner */,
|
||||
97C146EF1CF9000F007C117D /* Products */,
|
||||
A2E7A2EB20EFBBAC4AB0299B /* Pods */,
|
||||
0B24EBF53F1DCC708FA961FD /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
@ -107,7 +90,6 @@
|
|||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
|
||||
97C147021CF9000F007C117D /* Info.plist */,
|
||||
97C146F11CF9000F007C117D /* Supporting Files */,
|
||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
|
||||
|
@ -116,23 +98,6 @@
|
|||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
97C146F11CF9000F007C117D /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A2E7A2EB20EFBBAC4AB0299B /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
313F1E773DA06364A0C4F20A /* Pods-Runner.debug.xcconfig */,
|
||||
74CD890ACD2E394E606FCBEB /* Pods-Runner.release.xcconfig */,
|
||||
636963D381657D3BAEDC0A47 /* Pods-Runner.profile.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -140,14 +105,12 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
0D525F98970BF5A8EFFD825C /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
1B7EDCF8AB293318D8391906 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
@ -165,18 +128,16 @@
|
|||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = "The Chromium Authors";
|
||||
ORGANIZATIONNAME = "";
|
||||
TargetAttributes = {
|
||||
97C146ED1CF9000F007C117D = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
DevelopmentTeam = NF9A3KMT8Q;
|
||||
LastSwiftMigration = 0910;
|
||||
ProvisioningStyle = Automatic;
|
||||
LastSwiftMigration = 1100;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
|
@ -200,7 +161,6 @@
|
|||
files = (
|
||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
|
||||
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
|
||||
);
|
||||
|
@ -209,46 +169,6 @@
|
|||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
0D525F98970BF5A8EFFD825C /* [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;
|
||||
};
|
||||
1B7EDCF8AB293318D8391906 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/webview_flutter/webview_flutter.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/webview_flutter.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -352,9 +272,10 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
@ -365,27 +286,19 @@
|
|||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = NF9A3KMT8Q;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 2.0.7;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = ml.dandevelop.infoTren;
|
||||
MARKETING_VERSION = 2.0.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.dcdevelop.infotren;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
|
@ -437,7 +350,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
|
@ -486,10 +399,12 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SUPPORTED_PLATFORMS = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
@ -501,29 +416,19 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = NF9A3KMT8Q;
|
||||
CURRENT_PROJECT_VERSION = 2.0.7;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = ml.dandevelop.infoTren;
|
||||
MARKETING_VERSION = 2.0.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.dcdevelop.infotren;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = On;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -534,28 +439,18 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
DEVELOPMENT_TEAM = NF9A3KMT8Q;
|
||||
CURRENT_PROJECT_VERSION = 2.0.7;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = ml.dandevelop.infoTren;
|
||||
MARKETING_VERSION = 2.0.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = xyz.dcdevelop.infotren;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_SWIFT3_OBJC_INFERENCE = On;
|
||||
SWIFT_VERSION = 4.0;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
|
@ -4,7 +4,4 @@
|
|||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -5,7 +5,7 @@ import Flutter
|
|||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
@ -17,24 +17,13 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>cfr-scrapper.herokuapp.com</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
|
@ -42,6 +31,8 @@
|
|||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
|
@ -52,7 +43,5 @@
|
|||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>io.flutter.embedded_views_preview</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
9
lib/api/train_data.dart
Normal file
|
@ -0,0 +1,9 @@
|
|||
import 'package:http/http.dart' as http;
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
|
||||
const AUTHORITY = 'scraper.infotren.dcdevelop.xyz';
|
||||
|
||||
Future<TrainData> getTrain(int trainNumber) async {
|
||||
final response = await http.get(Uri.https(AUTHORITY, 'train/$trainNumber'));
|
||||
return trainDataFromJson(response.body);
|
||||
}
|
60
lib/components/cupertino_divider.dart
Normal file
|
@ -0,0 +1,60 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/pages/train_info_page/train_info_constants.dart';
|
||||
|
||||
class CupertinoDivider extends StatelessWidget {
|
||||
final Color color;
|
||||
|
||||
CupertinoDivider({Key? key, Color? color}):
|
||||
color = color ?? FOREGROUND_DARK_GREY,
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 1,
|
||||
),
|
||||
Container(
|
||||
height: 1,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 1,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CupertinoVerticalDivider extends StatelessWidget {
|
||||
final Color color;
|
||||
|
||||
CupertinoVerticalDivider({Key? key, Color? color}):
|
||||
color = color ?? FOREGROUND_DARK_GREY,
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 1,
|
||||
),
|
||||
Container(
|
||||
width: 1,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 1,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
42
lib/components/future_display.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
import 'package:info_tren/utils/default_ui_design.dart';
|
||||
|
||||
class FutureDisplay<T> extends StatelessWidget {
|
||||
final UiDesign? uiDesign;
|
||||
final Future<T> future;
|
||||
final Widget Function<T>(BuildContext context, T data) builder;
|
||||
final Widget Function(BuildContext context, Object error, StackTrace? st)? errorBuilder;
|
||||
|
||||
FutureDisplay({Key? key, required this.future, required this.builder, this.errorBuilder, this.uiDesign}): super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final uiDesign = this.uiDesign ?? defaultUiDesign;
|
||||
return FutureBuilder(
|
||||
future: future,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) return builder(context, snapshot.data);
|
||||
if (snapshot.hasError) return (errorBuilder != null ? errorBuilder!(context, snapshot.error!, snapshot.stackTrace) : throw snapshot.error!);
|
||||
if (snapshot.connectionState == ConnectionState.done) return Container();
|
||||
|
||||
Widget loadingWidget;
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
loadingWidget = CircularProgressIndicator();
|
||||
break;
|
||||
case UiDesign.CUPERTINO:
|
||||
loadingWidget = CupertinoActivityIndicator();
|
||||
break;
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: loadingWidget,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
31
lib/components/loading/loading.dart
Normal file
|
@ -0,0 +1,31 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:info_tren/components/loading/loading_cupertino.dart';
|
||||
import 'package:info_tren/components/loading/loading_material.dart';
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
import 'package:info_tren/utils/default_ui_design.dart';
|
||||
|
||||
class Loading extends StatelessWidget {
|
||||
static const DEFAULT_TEXT = 'Loading...';
|
||||
|
||||
final UiDesign? uiDesign;
|
||||
final String? text;
|
||||
const Loading({ Key? key, this.text, this.uiDesign }) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final uiDesign = this.uiDesign ?? defaultUiDesign;
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return LoadingMaterial(text: text ?? DEFAULT_TEXT,);
|
||||
case UiDesign.CUPERTINO:
|
||||
return LoadingCupertino(text: text ?? DEFAULT_TEXT,);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class LoadingCommon extends StatelessWidget {
|
||||
final String text;
|
||||
LoadingCommon({required this.text});
|
||||
}
|
28
lib/components/loading/loading_cupertino.dart
Normal file
|
@ -0,0 +1,28 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/src/widgets/framework.dart';
|
||||
import 'package:info_tren/components/loading/loading.dart';
|
||||
|
||||
class LoadingCupertino extends LoadingCommon {
|
||||
LoadingCupertino({required String text}) : super(text: text,);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: CupertinoActivityIndicator(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(text),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
26
lib/components/loading/loading_material.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/components/loading/loading.dart';
|
||||
|
||||
class LoadingMaterial extends LoadingCommon {
|
||||
LoadingMaterial({required String text}) : super(text: text,);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(text),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
117
lib/components/refresh_future_builder.dart
Normal file
|
@ -0,0 +1,117 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class RefreshFutureBuilder<T> extends StatefulWidget {
|
||||
final Future<T> Function()? futureCreator;
|
||||
final T? initialData;
|
||||
final Widget Function(BuildContext context, Future Function() refresh, RefreshFutureBuilderSnapshot<T> snapshot) builder;
|
||||
|
||||
const RefreshFutureBuilder({ Key? key, this.futureCreator, this.initialData, required this.builder }) : super(key: key);
|
||||
|
||||
@override
|
||||
_RefreshFutureBuilderState<T> createState() => _RefreshFutureBuilderState<T>();
|
||||
}
|
||||
|
||||
class _RefreshFutureBuilderState<T> extends State<RefreshFutureBuilder<T>> {
|
||||
late RefreshFutureBuilderSnapshot<T> snapshot;
|
||||
Future<T> Function()? futureCreator;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
snapshot = widget.initialData != null ? RefreshFutureBuilderSnapshot.initial(widget.initialData!) : RefreshFutureBuilderSnapshot.nothing();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (futureCreator != widget.futureCreator) {
|
||||
futureCreator = widget.futureCreator;
|
||||
runFuture();
|
||||
}
|
||||
}
|
||||
|
||||
Future runFuture() async {
|
||||
if (futureCreator == null) {
|
||||
return;
|
||||
}
|
||||
// Set state to signify loading
|
||||
setState(() {
|
||||
switch (snapshot.state) {
|
||||
case RefreshFutureBuilderState.none:
|
||||
snapshot = RefreshFutureBuilderSnapshot.waiting();
|
||||
break;
|
||||
case RefreshFutureBuilderState.initial:
|
||||
snapshot = RefreshFutureBuilderSnapshot.refresh(snapshot.data);
|
||||
break;
|
||||
case RefreshFutureBuilderState.waiting:
|
||||
return;
|
||||
case RefreshFutureBuilderState.error:
|
||||
snapshot = RefreshFutureBuilderSnapshot.refresh(null, snapshot.error, snapshot.stackTrace);
|
||||
break;
|
||||
case RefreshFutureBuilderState.done:
|
||||
snapshot = RefreshFutureBuilderSnapshot.refresh(snapshot.data);
|
||||
break;
|
||||
case RefreshFutureBuilderState.refreshing:
|
||||
return;
|
||||
case RefreshFutureBuilderState.refreshError:
|
||||
snapshot = RefreshFutureBuilderSnapshot.refresh(null, snapshot.error, snapshot.stackTrace);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
try {
|
||||
final data = await futureCreator!();
|
||||
setState(() {
|
||||
snapshot = RefreshFutureBuilderSnapshot.withData(data);
|
||||
});
|
||||
}
|
||||
catch (e, st) {
|
||||
setState(() {
|
||||
if (snapshot.state == RefreshFutureBuilderState.waiting) {
|
||||
snapshot = RefreshFutureBuilderSnapshot.withError(e, st);
|
||||
}
|
||||
else {
|
||||
snapshot = RefreshFutureBuilderSnapshot.refreshError(snapshot.data, e, st);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.builder(
|
||||
context,
|
||||
runFuture,
|
||||
snapshot,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RefreshFutureBuilderSnapshot<T> {
|
||||
final RefreshFutureBuilderState state;
|
||||
final T? data;
|
||||
final Object? error;
|
||||
final StackTrace? stackTrace;
|
||||
|
||||
bool get hasData => data != null;
|
||||
bool get hasError => error != null;
|
||||
|
||||
const RefreshFutureBuilderSnapshot._(this.state, this.data, this.error, this.stackTrace);
|
||||
const RefreshFutureBuilderSnapshot.nothing() : state = RefreshFutureBuilderState.none, data = null, error = null, stackTrace = null;
|
||||
const RefreshFutureBuilderSnapshot.initial(this.data) : state = RefreshFutureBuilderState.initial, error = null, stackTrace = null;
|
||||
const RefreshFutureBuilderSnapshot.waiting() : state = RefreshFutureBuilderState.waiting, data = null, error = null, stackTrace = null;
|
||||
const RefreshFutureBuilderSnapshot.withError(this.error, [this.stackTrace]) : state = RefreshFutureBuilderState.error, data = null;
|
||||
const RefreshFutureBuilderSnapshot.withData(this.data) : state = RefreshFutureBuilderState.done, error = null, stackTrace = null;
|
||||
const RefreshFutureBuilderSnapshot.refresh(this.data, [this.error, this.stackTrace]) : state = RefreshFutureBuilderState.refreshing;
|
||||
const RefreshFutureBuilderSnapshot.refreshError(this.data, this.error, this.stackTrace) : state = RefreshFutureBuilderState.refreshError;
|
||||
}
|
||||
|
||||
enum RefreshFutureBuilderState {
|
||||
none,
|
||||
initial,
|
||||
waiting,
|
||||
error,
|
||||
done,
|
||||
refreshing,
|
||||
refreshError,
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/widgets.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_material.dart';
|
||||
import 'package:info_tren/models/train_operator_lines.dart';
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
import 'package:info_tren/utils/default_ui_design.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class SelectTrainSuggestions extends StatefulWidget {
|
||||
final UiDesign? uiDesign;
|
||||
final String userInput;
|
||||
final void Function(int trainNumber) onTrainSelected;
|
||||
|
||||
const SelectTrainSuggestions({ Key? key, required this.uiDesign, required this.userInput, required this.onTrainSelected }) : super(key: key);
|
||||
|
||||
@override
|
||||
SelectTrainSuggestionsState createState() {
|
||||
final uiDesign = this.uiDesign ?? defaultUiDesign;
|
||||
switch(uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return SelectTrainSuggestionsStateMaterial();
|
||||
case UiDesign.CUPERTINO:
|
||||
return SelectTrainSuggestionsStateCupertino();
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SelectTrainSuggestionsState extends State<SelectTrainSuggestions> {
|
||||
late String userInput;
|
||||
|
||||
List<TrainOperatorLines> operators = [];
|
||||
|
||||
Future loadOperators(BuildContext context) async {
|
||||
operators = [];
|
||||
|
||||
final operatorsString = await DefaultAssetBundle.of(context).loadString("assets/lines/files.txt");
|
||||
final operatorsFilesList = operatorsString.split("\n");
|
||||
|
||||
final decoder = JsonDecoder();
|
||||
|
||||
for (final operatorFile in operatorsFilesList) {
|
||||
final operatorString = await DefaultAssetBundle.of(context).loadString("assets/lines/$operatorFile");
|
||||
final operatorData = decoder.convert(operatorString);
|
||||
final _operator = TrainOperatorLines.fromJson(operatorData);
|
||||
operators.add(_operator);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
userInput = widget.userInput;
|
||||
|
||||
loadOperators(context).then((_) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (userInput != widget.userInput) {
|
||||
setState(() {
|
||||
userInput = widget.userInput;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String getUseCurrentInputWidgetText(int currentInput) => 'Caută trenul cu numărul $currentInput';
|
||||
Widget getUseCurrentInputWidget(int currentInput, void Function(int) onTrainSelected);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var sliversTuple = operators.map(
|
||||
(op) => Tuple2(
|
||||
getFilteredLines(op, userInput),
|
||||
op.operator,
|
||||
)
|
||||
).where((tuple) => tuple.item1.isNotEmpty).toList();
|
||||
if (userInput.isNotEmpty) sliversTuple.sort((a, b) {
|
||||
final aTrain = a.item1.first;
|
||||
final bTrain = b.item1.first;
|
||||
|
||||
final inputAsRegExp = RegExp(userInput);
|
||||
|
||||
final matchOnA = inputAsRegExp.firstMatch(aTrain.number)!;
|
||||
final matchOnB = inputAsRegExp.firstMatch(bTrain.number)!;
|
||||
|
||||
if (matchOnA.start != matchOnB.start) return matchOnA.start - matchOnB.start;
|
||||
|
||||
if (aTrain.number.length != bTrain.number.length) return aTrain.number.length - bTrain.number.length;
|
||||
|
||||
return aTrain.number.compareTo(bTrain.number);
|
||||
});
|
||||
var slivers = sliversTuple.map((tuple) => OperatorAutocompleteSliver(
|
||||
uiDesign: widget.uiDesign,
|
||||
operatorName: tuple.item2,
|
||||
trains: tuple.item1,
|
||||
onTrainSelected: widget.onTrainSelected,
|
||||
)).toList();
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
...slivers,
|
||||
SliverToBoxAdapter(
|
||||
child: int.tryParse(userInput) != null ? getUseCurrentInputWidget(int.parse(userInput), widget.onTrainSelected) : Container(),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<TrainOperatorTrainDescription> getFilteredLines(TrainOperatorLines _operator, String currentInput) {
|
||||
if (currentInput.isNotEmpty) {
|
||||
final filteredLines = _operator.trains
|
||||
.where((elem) => elem.number.contains(currentInput))
|
||||
.toList();
|
||||
|
||||
filteredLines.sort((a, b) {
|
||||
final inputAsRegExp = RegExp(currentInput);
|
||||
|
||||
final matchOnA = inputAsRegExp.firstMatch(a.number)!;
|
||||
final matchOnB = inputAsRegExp.firstMatch(b.number)!;
|
||||
|
||||
if (matchOnA.start != matchOnB.start) return matchOnA.start - matchOnB.start;
|
||||
|
||||
if (a.number.length != b.number.length) return a.number.length - b.number.length;
|
||||
|
||||
return a.number.compareTo(b.number);
|
||||
});
|
||||
|
||||
return filteredLines;
|
||||
}
|
||||
else {
|
||||
return _operator.trains;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OperatorAutocompleteSliver extends StatelessWidget {
|
||||
final UiDesign? uiDesign;
|
||||
final String operatorName;
|
||||
final List<TrainOperatorTrainDescription> trains;
|
||||
final void Function(int) onTrainSelected;
|
||||
|
||||
const OperatorAutocompleteSliver({ Key? key, required this.uiDesign, required this.operatorName, required this.trains, required this.onTrainSelected }) : super(key: key);
|
||||
|
||||
Widget mapTrainToItem(TrainOperatorTrainDescription train) {
|
||||
final uiDesign = this.uiDesign ?? defaultUiDesign;
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return OperatorAutocompleteTileMaterial(
|
||||
onTrainSelected: onTrainSelected,
|
||||
operatorName: operatorName,
|
||||
train: train,
|
||||
);
|
||||
case UiDesign.CUPERTINO:
|
||||
return OperatorAutocompleteTileCupertino(
|
||||
onTrainSelected: onTrainSelected,
|
||||
operatorName: operatorName,
|
||||
train: train,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (trains.isEmpty) {
|
||||
return SliverToBoxAdapter(child: Container(),);
|
||||
}
|
||||
|
||||
return SliverPrototypeExtentList(
|
||||
prototypeItem: Column(
|
||||
children: <Widget>[
|
||||
mapTrainToItem(TrainOperatorTrainDescription()),
|
||||
],
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
mapTrainToItem(trains[index]),
|
||||
],
|
||||
);
|
||||
},
|
||||
childCount: trains.length,
|
||||
addSemanticIndexes: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class OperatorAutocompleteTile extends StatelessWidget {
|
||||
final String operatorName;
|
||||
final TrainOperatorTrainDescription train;
|
||||
final void Function(int) onTrainSelected;
|
||||
|
||||
const OperatorAutocompleteTile({ Key? key, required this.onTrainSelected, required this.operatorName, required this.train }) : super(key: key);
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||
import 'package:info_tren/models/train_operator_lines.dart';
|
||||
|
||||
class SelectTrainSuggestionsStateCupertino extends SelectTrainSuggestionsState {
|
||||
@override
|
||||
Widget getUseCurrentInputWidget(int currentInput, void Function(int p1) onTrainSelected) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
onTrainSelected(currentInput);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(getUseCurrentInputWidgetText(currentInput)),
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OperatorAutocompleteTileCupertino extends OperatorAutocompleteTile {
|
||||
OperatorAutocompleteTileCupertino({
|
||||
Key? key,
|
||||
required String operatorName,
|
||||
required void Function(int) onTrainSelected,
|
||||
required TrainOperatorTrainDescription train
|
||||
}): super(
|
||||
onTrainSelected: onTrainSelected,
|
||||
operatorName: operatorName,
|
||||
train: train,
|
||||
key: key,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTrainSelected(train.internalNumber);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
operatorName,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text(
|
||||
"${train.rang} ${train.number}",
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||
import 'package:info_tren/models/train_operator_lines.dart';
|
||||
|
||||
class SelectTrainSuggestionsStateMaterial extends SelectTrainSuggestionsState {
|
||||
@override
|
||||
Widget getUseCurrentInputWidget(int currentInput, void Function(int) onTrainSelected) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
ListTile(
|
||||
title: Text(getUseCurrentInputWidgetText(currentInput)),
|
||||
onTap: () {
|
||||
onTrainSelected(currentInput);
|
||||
},
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OperatorAutocompleteTileMaterial extends OperatorAutocompleteTile {
|
||||
OperatorAutocompleteTileMaterial({
|
||||
Key? key,
|
||||
required String operatorName,
|
||||
required void Function(int) onTrainSelected,
|
||||
required TrainOperatorTrainDescription train
|
||||
}): super(
|
||||
onTrainSelected: onTrainSelected,
|
||||
operatorName: operatorName,
|
||||
train: train,
|
||||
key: key,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
title: Text("${train.rang} ${train.number}"),
|
||||
subtitle: Text(operatorName),
|
||||
onTap: () {
|
||||
onTrainSelected(train.internalNumber);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
62
lib/components/slim_app_bar.dart
Normal file
|
@ -0,0 +1,62 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class SlimAppBar extends StatelessWidget {
|
||||
final String title;
|
||||
final double size;
|
||||
// final Function onBackTap;
|
||||
|
||||
SlimAppBar({
|
||||
required this.title,
|
||||
this.size = 24,
|
||||
// this.onBackTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: size,
|
||||
child: Container(
|
||||
color:
|
||||
Theme.of(context).appBarTheme.color ??
|
||||
Theme.of(context).primaryColor,
|
||||
child: InkWell(
|
||||
onTap: (ModalRoute.of(context)?.canPop ?? false)
|
||||
? () => Navigator.of(context).pop()
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: size,
|
||||
width: size,
|
||||
child: (ModalRoute.of(context)?.canPop ?? false)
|
||||
? BackButtonIcon()
|
||||
: null,
|
||||
),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
Theme.of(context).appBarTheme.textTheme?.caption?.copyWith(color: Theme.of(context).appBarTheme.textTheme?.bodyText2?.color) ??
|
||||
Theme.of(context).textTheme.caption?.copyWith(color: Theme.of(context).textTheme.bodyText2?.color),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: size,
|
||||
width: size,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class HiddenWebView extends StatelessWidget {
|
||||
final WebView webView;
|
||||
final Widget child;
|
||||
|
||||
HiddenWebView({@required this.child, this.webView});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: <Widget>[
|
||||
Offstage(
|
||||
offstage: true,
|
||||
child: webView,
|
||||
),
|
||||
Positioned.fill(child: child)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
226
lib/main.dart
|
@ -2,195 +2,73 @@ import 'dart:io' show Platform;
|
|||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
import 'package:info_tren/train_info_page/train_info.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_cupertino.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_material.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_prompt.dart';
|
||||
// import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
import 'package:info_tren/pages/main/main_page.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';
|
||||
|
||||
|
||||
|
||||
void main() => runApp(StartPoint());
|
||||
void main() {
|
||||
// final store = createStore();
|
||||
// runApp(
|
||||
// StoreProvider(
|
||||
// store: store,
|
||||
// child: StartPoint(),
|
||||
// )
|
||||
// );
|
||||
runApp(
|
||||
StartPoint(),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, WidgetBuilder> routesByUiDesign(UiDesign uiDesign) => {
|
||||
Navigator.defaultRouteName: (context) {
|
||||
return MainPage(uiDesign: uiDesign,);
|
||||
},
|
||||
SelectTrainPage.routeName: (context) {
|
||||
return SelectTrainPage(uiDesign: uiDesign);
|
||||
},
|
||||
TrainInfo.routeName: (context) {
|
||||
return TrainInfo(
|
||||
trainNumber: ModalRoute.of(context)!.settings.arguments as int,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
class StartPoint extends StatelessWidget {
|
||||
final String appTitle = 'Info Tren';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Platform.isAndroid) {
|
||||
if (Platform.isIOS) {
|
||||
return CupertinoApp(
|
||||
title: appTitle,
|
||||
theme: CupertinoThemeData(
|
||||
primaryColor: Colors.blue.shade600,
|
||||
brightness: Brightness.dark,
|
||||
// textTheme: CupertinoTextThemeData(
|
||||
// textStyle: TextStyle(
|
||||
// fontFamily: 'Atkinson Hyperlegible',
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
routes: routesByUiDesign(UiDesign.CUPERTINO),
|
||||
);
|
||||
}
|
||||
else {
|
||||
return MaterialApp(
|
||||
title: 'Info Tren',
|
||||
title: appTitle,
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
brightness: Brightness.dark,
|
||||
primaryColor: Colors.blue.shade600,
|
||||
accentColor: Colors.blue.shade700,
|
||||
// fontFamily: 'Atkinson Hyperlegible',
|
||||
),
|
||||
// home: MainPageMaterial(),
|
||||
routes: {
|
||||
Navigator.defaultRouteName: (context) {
|
||||
return MainPageMaterial();
|
||||
},
|
||||
TrainInfoPromptCommon.routeName: (context) {
|
||||
return TrainInfoPromptMaterial();
|
||||
},
|
||||
TrainInfo.routeName: (context) {
|
||||
return TrainDataWebViewAdapter(
|
||||
builder: (context) {
|
||||
return TrainInfoMaterial(
|
||||
trainNumber: ModalRoute.of(context).settings.arguments as int,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
routes: routesByUiDesign(UiDesign.MATERIAL),
|
||||
);
|
||||
}
|
||||
else if (Platform.isIOS) {
|
||||
return CupertinoApp(
|
||||
title: "Info Tren",
|
||||
theme: CupertinoThemeData(
|
||||
primaryColor: Colors.blue.shade600,
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
// home: MainPageCupertino(),
|
||||
routes: {
|
||||
Navigator.defaultRouteName: (context) {
|
||||
return MainPageCupertino();
|
||||
},
|
||||
TrainInfoPromptCommon.routeName: (context) {
|
||||
return TrainInfoPromptCupertino();
|
||||
},
|
||||
TrainInfo.routeName: (context) {
|
||||
return TrainDataWebViewAdapter(
|
||||
builder: (context) {
|
||||
return TrainInfoCupertino(
|
||||
trainNumber: ModalRoute.of(context).settings.arguments as int,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
mixin MainPageAction {
|
||||
onTrainInfoPageInvoke(BuildContext context) {
|
||||
Navigator.of(context).pushNamed(TrainInfoPromptCommon.routeName);
|
||||
}
|
||||
|
||||
onStationBoardPageInvoke(BuildContext context) {
|
||||
|
||||
}
|
||||
|
||||
onRoutePlanPageInvoke(BuildContext context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MainPageMaterial extends StatelessWidget with MainPageAction {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Info Tren"),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
child: Text(
|
||||
"Informații despre tren",
|
||||
style: Theme.of(context).textTheme.button.copyWith(fontSize: 18),
|
||||
),
|
||||
onPressed: () {
|
||||
onTrainInfoPageInvoke(context);
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text(
|
||||
"Tabelă plecari/sosiri",
|
||||
style: Theme.of(context).textTheme.button.copyWith(fontSize: 18),
|
||||
),
|
||||
// TODO: Implement departure/arrival
|
||||
onPressed: null,
|
||||
// onPressed: () {
|
||||
// onStationBoardPageInvoke(context);
|
||||
// },
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Text(
|
||||
"Planificare rută",
|
||||
style: Theme.of(context).textTheme.button.copyWith(fontSize: 18),
|
||||
),
|
||||
// TODO: Implement route planning
|
||||
onPressed: null,
|
||||
// onPressed: () {
|
||||
// onRoutePlanPageInvoke(context);
|
||||
// },
|
||||
)
|
||||
].map((w) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: w,
|
||||
),
|
||||
)).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MainPageCupertino extends StatelessWidget with MainPageAction {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text("Info Tren"),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
CupertinoButton.filled(
|
||||
child: Text("Informații despre tren"),
|
||||
onPressed: () {
|
||||
onTrainInfoPageInvoke(context);
|
||||
},
|
||||
),
|
||||
CupertinoButton.filled(
|
||||
child: Text("Tabelă plecari/sosiri"),
|
||||
// TODO: Implement departure/arrival
|
||||
onPressed: null,
|
||||
// onPressed: () {
|
||||
// onStationBoardPageInvoke(context);
|
||||
// },
|
||||
),
|
||||
CupertinoButton.filled(
|
||||
child: Text("Planificare rută"),
|
||||
// TODO: Implement route planning
|
||||
onPressed: null,
|
||||
// onPressed: () {
|
||||
// onRoutePlanPageInvoke(context);
|
||||
// },
|
||||
),
|
||||
].map((w) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: w,
|
||||
),
|
||||
)).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'train_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
TrainData _$TrainDataFromJson(Map<String, dynamic> json) {
|
||||
return TrainData(
|
||||
rang: json['rang'] as String,
|
||||
trainNumber: json['tren'] as String,
|
||||
operator: json['operator'] as String,
|
||||
lastInfo: json['ultima_informatie'] == null
|
||||
? null
|
||||
: LastInfo.fromJson(
|
||||
json['ultima_informatie'] as Map<String, dynamic>),
|
||||
state: json['stare'] as String,
|
||||
route: json['relatia'] as String,
|
||||
tripLength: json['durata_calatoriei'] as String,
|
||||
stations: (json['stations'] as List)
|
||||
?.map((e) => e == null
|
||||
? null
|
||||
: StationEntry.fromJson(e as Map<String, dynamic>))
|
||||
?.toList(),
|
||||
nextStop: json['urmatoarea_oprire'] == null
|
||||
? null
|
||||
: StopInfo.fromJson(
|
||||
json['urmatoarea_oprire'] as Map<String, dynamic>),
|
||||
distance: json['distanta'] as String,
|
||||
destination: json['destinatie'] == null
|
||||
? null
|
||||
: StopInfo.fromJson(json['destinatie'] as Map<String, dynamic>));
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$TrainDataToJson(TrainData instance) => <String, dynamic>{
|
||||
'rang': instance.rang,
|
||||
'tren': instance.trainNumber,
|
||||
'operator': instance.operator,
|
||||
'relatia': instance.route,
|
||||
'stare': instance.state,
|
||||
'ultima_informatie': instance.lastInfo,
|
||||
'destinatie': instance.destination,
|
||||
'urmatoarea_oprire': instance.nextStop,
|
||||
'durata_calatoriei': instance.tripLength,
|
||||
'distanta': instance.distance,
|
||||
'stations': instance.stations
|
||||
};
|
||||
|
||||
LastInfo _$LastInfoFromJson(Map<String, dynamic> json) {
|
||||
return LastInfo(
|
||||
dateAndTime: json['data_si_ora'] as String,
|
||||
delay: json['intarziere'] as int,
|
||||
event: json['eveniment'] as String,
|
||||
station: json['statia'] as String);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$LastInfoToJson(LastInfo instance) => <String, dynamic>{
|
||||
'statia': instance.station,
|
||||
'eveniment': instance.event,
|
||||
'data_si_ora': instance.dateAndTime,
|
||||
'intarziere': instance.delay
|
||||
};
|
||||
|
||||
StopInfo _$StopInfoFromJson(Map<String, dynamic> json) {
|
||||
return StopInfo(
|
||||
station: json['statia'] as String,
|
||||
dateAndTime: json['data_si_ora'] as String);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$StopInfoToJson(StopInfo instance) => <String, dynamic>{
|
||||
'statia': instance.station,
|
||||
'data_si_ora': instance.dateAndTime
|
||||
};
|
||||
|
||||
StationEntry _$StationEntryFromJson(Map<String, dynamic> json) {
|
||||
return StationEntry(
|
||||
name: json['statia'] as String,
|
||||
delay: json['intarziere'] as int,
|
||||
realOrEstimate: json['real/estimat'] as String,
|
||||
arrivalTime: json['sosire'] as String,
|
||||
departureTime: json['plecare'] as String,
|
||||
km: json['km'] as String,
|
||||
observations: json['observatii'] as String,
|
||||
waitTime: json['stationeaza_pentru'] as String);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$StationEntryToJson(StationEntry instance) =>
|
||||
<String, dynamic>{
|
||||
'km': instance.km,
|
||||
'statia': instance.name,
|
||||
'sosire': instance.arrivalTime,
|
||||
'stationeaza_pentru': instance.waitTime,
|
||||
'plecare': instance.departureTime,
|
||||
'real/estimat': instance.realOrEstimate,
|
||||
'intarziere': instance.delay,
|
||||
'observatii': instance.observations
|
||||
};
|
42
lib/models/train_operator_lines.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'train_operator_lines.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class TrainOperatorLines {
|
||||
@JsonKey(name: "short_name")
|
||||
final String shortName;
|
||||
final String operator;
|
||||
@JsonKey(name: "versiune")
|
||||
final String version;
|
||||
@JsonKey(name: "trenuri")
|
||||
final List<TrainOperatorTrainDescription> trains;
|
||||
|
||||
TrainOperatorLines({
|
||||
required this.operator,
|
||||
this.shortName = "",
|
||||
required this.version,
|
||||
required this.trains,
|
||||
});
|
||||
|
||||
factory TrainOperatorLines.fromJson(Map<String, dynamic> json) => _$TrainOperatorLinesFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$TrainOperatorLinesToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class TrainOperatorTrainDescription {
|
||||
final String rang;
|
||||
@JsonKey(name: "numar")
|
||||
final String number;
|
||||
@JsonKey(name: "numar_intern")
|
||||
final int internalNumber;
|
||||
|
||||
TrainOperatorTrainDescription({
|
||||
this.number = '',
|
||||
this.rang = '',
|
||||
this.internalNumber = 0,
|
||||
});
|
||||
|
||||
factory TrainOperatorTrainDescription.fromJson(Map<String, dynamic> json) => _$TrainOperatorTrainDescriptionFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$TrainOperatorTrainDescriptionToJson(this);
|
||||
}
|
42
lib/models/train_operator_lines.g.dart
Normal file
|
@ -0,0 +1,42 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'train_operator_lines.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
TrainOperatorLines _$TrainOperatorLinesFromJson(Map<String, dynamic> json) =>
|
||||
TrainOperatorLines(
|
||||
operator: json['operator'] as String,
|
||||
shortName: json['short_name'] as String? ?? "",
|
||||
version: json['versiune'] as String,
|
||||
trains: (json['trenuri'] as List<dynamic>)
|
||||
.map((e) =>
|
||||
TrainOperatorTrainDescription.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TrainOperatorLinesToJson(TrainOperatorLines instance) =>
|
||||
<String, dynamic>{
|
||||
'short_name': instance.shortName,
|
||||
'operator': instance.operator,
|
||||
'versiune': instance.version,
|
||||
'trenuri': instance.trains,
|
||||
};
|
||||
|
||||
TrainOperatorTrainDescription _$TrainOperatorTrainDescriptionFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
TrainOperatorTrainDescription(
|
||||
number: json['numar'] as String? ?? '',
|
||||
rang: json['rang'] as String? ?? '',
|
||||
internalNumber: json['numar_intern'] as int? ?? 0,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TrainOperatorTrainDescriptionToJson(
|
||||
TrainOperatorTrainDescription instance) =>
|
||||
<String, dynamic>{
|
||||
'rang': instance.rang,
|
||||
'numar': instance.number,
|
||||
'numar_intern': instance.internalNumber,
|
||||
};
|
15
lib/models/ui_design.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
enum UiDesign {
|
||||
MATERIAL,
|
||||
CUPERTINO
|
||||
}
|
||||
|
||||
class UnmatchedUiDesignException implements Exception {
|
||||
final UiDesign uiDesign;
|
||||
|
||||
UnmatchedUiDesignException(this.uiDesign);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return '$uiDesign was not matched';
|
||||
}
|
||||
}
|
68
lib/pages/main/main_page.dart
Normal file
|
@ -0,0 +1,68 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
import 'package:info_tren/pages/main/main_page_cupertino.dart';
|
||||
import 'package:info_tren/pages/main/main_page_material.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||
import 'package:info_tren/utils/default_ui_design.dart';
|
||||
|
||||
class MainPage extends StatelessWidget {
|
||||
final UiDesign? uiDesign;
|
||||
|
||||
const MainPage({ Key? key, this.uiDesign }) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final uiDesign = this.uiDesign ?? defaultUiDesign;
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return MainPageMaterial();
|
||||
case UiDesign.CUPERTINO:
|
||||
return MainPageCupertino();
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MainPageShared extends StatelessWidget {
|
||||
final String pageTitle = 'Info Tren';
|
||||
|
||||
List<MainPageOption> get options => [
|
||||
MainPageOption(
|
||||
name: 'Informații despre tren',
|
||||
action: (BuildContext context) {
|
||||
onTrainInfoPageInvoke(context);
|
||||
},
|
||||
),
|
||||
MainPageOption(
|
||||
name: 'Tabelă plecari/sosiri',
|
||||
// TODO: Implement departure/arrival
|
||||
action: null,
|
||||
),
|
||||
MainPageOption(
|
||||
name: 'Planificare rută',
|
||||
// TODO: Implement route planning
|
||||
action: null,
|
||||
),
|
||||
];
|
||||
|
||||
onTrainInfoPageInvoke(BuildContext context) {
|
||||
Navigator.of(context).pushNamed(SelectTrainPage.routeName);
|
||||
}
|
||||
|
||||
onStationBoardPageInvoke(BuildContext context) {
|
||||
|
||||
}
|
||||
|
||||
onRoutePlanPageInvoke(BuildContext context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class MainPageOption {
|
||||
final String name;
|
||||
final void Function(BuildContext context)? action;
|
||||
|
||||
MainPageOption({required this.name, this.action});
|
||||
}
|
30
lib/pages/main/main_page_cupertino.dart
Normal file
|
@ -0,0 +1,30 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/pages/main/main_page.dart';
|
||||
|
||||
class MainPageCupertino extends MainPageShared {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(pageTitle),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: options.map((option) => CupertinoButton.filled(
|
||||
child: Text(option.name),
|
||||
onPressed: option.action == null ? null : () => option.action!(context),
|
||||
)).map((w) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: w,
|
||||
),
|
||||
)).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
34
lib/pages/main/main_page_material.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/pages/main/main_page.dart';
|
||||
|
||||
class MainPageMaterial extends MainPageShared {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(pageTitle),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: options.map((option) => ElevatedButton(
|
||||
child: Text(
|
||||
option.name,
|
||||
style: Theme.of(context).textTheme.button?.copyWith(fontSize: 18),
|
||||
),
|
||||
onPressed: option.action != null ? () => option.action!(context) : null,
|
||||
)).map((w) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: w,
|
||||
),
|
||||
)).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
61
lib/pages/train_info_page/select_train/select_train.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train_cupertino.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train_material.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:tuple/tuple.dart';
|
||||
|
||||
typedef TrainSelectedCallback(int trainNumber);
|
||||
|
||||
class SelectTrainPage extends StatefulWidget {
|
||||
final UiDesign? uiDesign;
|
||||
|
||||
SelectTrainPage({Key? key, this.uiDesign}) : super(key: key);
|
||||
|
||||
static String routeName = "/trainInfo/selectTrain";
|
||||
|
||||
void onTrainSelected(BuildContext context, int selection) {
|
||||
Navigator.of(context).pushNamed(TrainInfo.routeName, arguments: selection);
|
||||
}
|
||||
|
||||
@override
|
||||
SelectTrainPageState createState() {
|
||||
final uiDesign = this.uiDesign ?? defaultUiDesign;
|
||||
switch(uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return SelectTrainPageStateMaterial();
|
||||
case UiDesign.CUPERTINO:
|
||||
return SelectTrainPageStateCupertino();
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SelectTrainPageState extends State<SelectTrainPage> {
|
||||
final String pageTitle = 'Informații despre tren';
|
||||
final String textFieldLabel = 'Numărul trenului';
|
||||
|
||||
TextEditingController trainNoController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void onTextChanged() {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Widget get suggestionsList => SelectTrainSuggestions(
|
||||
uiDesign: widget.uiDesign,
|
||||
userInput: trainNoController.text,
|
||||
onTrainSelected: (trainNumber) => widget.onTrainSelected(context, trainNumber),
|
||||
key: ValueKey(trainNoController.text),
|
||||
);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||
|
||||
class SelectTrainPageStateCupertino extends SelectTrainPageState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(pageTitle),
|
||||
),
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: CupertinoTextField(
|
||||
controller: trainNoController,
|
||||
autofocus: true,
|
||||
placeholder: textFieldLabel,
|
||||
textInputAction: TextInputAction.search,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (_) => onTextChanged(),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: suggestionsList,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||
|
||||
class SelectTrainPageStateMaterial extends SelectTrainPageState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(pageTitle),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: TextField(
|
||||
controller: trainNoController,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: textFieldLabel,
|
||||
),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
textInputAction: TextInputAction.search,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (_) => onTextChanged(),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: suggestionsList,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_constants.dart';
|
||||
import 'package:info_tren/pages/train_info_page/train_info_constants.dart';
|
||||
|
||||
import 'dart:io' show Platform;
|
||||
|
69
lib/pages/train_info_page/view_train/train_info.dart
Normal file
|
@ -0,0 +1,69 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/api/train_data.dart';
|
||||
import 'package:info_tren/components/loading/loading.dart';
|
||||
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_cupertino.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
|
||||
import 'package:info_tren/utils/default_ui_design.dart';
|
||||
|
||||
|
||||
class TrainInfo extends StatelessWidget {
|
||||
static String routeName = "/trainInfo/display";
|
||||
|
||||
final UiDesign? uiDesign;
|
||||
final int trainNumber;
|
||||
|
||||
TrainInfo({Key? key, required this.trainNumber, this.uiDesign}): super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final uiDesign = this.uiDesign ?? defaultUiDesign;
|
||||
|
||||
return RefreshFutureBuilder<TrainData>(
|
||||
futureCreator: () => getTrain(trainNumber),
|
||||
builder: (context, refresh, snapshot) {
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
|
||||
return TrainInfoLoadingMaterial(title: trainNumber.toString(),);
|
||||
}
|
||||
else if (snapshot.state == RefreshFutureBuilderState.error) {
|
||||
return TrainInfoErrorMaterial(title: '$trainNumber - Error', error: snapshot.error!,);
|
||||
}
|
||||
|
||||
return TrainInfoMaterial(trainData: snapshot.data!,);
|
||||
case UiDesign.CUPERTINO:
|
||||
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
|
||||
return TrainInfoLoadingCupertino(title: trainNumber.toString(),);
|
||||
}
|
||||
else if (snapshot.state == RefreshFutureBuilderState.error) {
|
||||
return TrainInfoErrorCupertino(title: '$trainNumber - Error', error: snapshot.error!,);
|
||||
}
|
||||
|
||||
return TrainInfoCupertino(trainData: snapshot.data!,);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TrainInfoLoading extends StatelessWidget {
|
||||
final String title;
|
||||
final Widget loadingWidget;
|
||||
|
||||
TrainInfoLoading({required this.title, String? loadingText, UiDesign? uiDesign}) : loadingWidget = Loading(uiDesign: uiDesign, text: loadingText,);
|
||||
}
|
||||
|
||||
abstract class TrainInfoError extends StatelessWidget {
|
||||
final String title;
|
||||
final Object error;
|
||||
|
||||
TrainInfoError({required this.title, required this.error});
|
||||
}
|
741
lib/pages/train_info_page/view_train/train_info_cupertino.dart
Normal file
|
@ -0,0 +1,741 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/components/cupertino_divider.dart';
|
||||
import 'package:info_tren/models/train_data.dart' hide State;
|
||||
import 'package:info_tren/models/ui_design.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_cupertino_DisplayTrainStation.dart';
|
||||
import 'package:info_tren/utils/state_to_string.dart';
|
||||
|
||||
class TrainInfoLoadingCupertino extends TrainInfoLoading {
|
||||
TrainInfoLoadingCupertino({required String title, String? loadingText}) : super(title: title, loadingText: loadingText, uiDesign: UiDesign.CUPERTINO);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(title),
|
||||
),
|
||||
child: Center(
|
||||
child: loadingWidget,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoErrorCupertino extends TrainInfoError {
|
||||
TrainInfoErrorCupertino({required Object error, required String title,}) : super(error: error, title: title,);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text(title),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(error.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoCupertino extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
TrainInfoCupertino({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text("Informații despre ${trainData.rank} ${trainData.number}"),
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final topPadding = MediaQuery.of(context).padding.top;
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: topPadding,
|
||||
),
|
||||
child: Container(),
|
||||
),
|
||||
),
|
||||
DisplayTrainID(trainData: trainData,),
|
||||
DisplayTrainOperator(trainData: trainData,),
|
||||
DisplayTrainRoute(trainData: trainData,),
|
||||
DisplayTrainDeparture(trainData: trainData,),
|
||||
SliverToBoxAdapter(
|
||||
child: CupertinoDivider(
|
||||
color: FOREGROUND_WHITE,
|
||||
),
|
||||
),
|
||||
DisplayTrainLastInfo(trainData: trainData,),
|
||||
SliverToBoxAdapter(
|
||||
child: CupertinoDivider(),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
// Expanded(
|
||||
// child: DisplayTrainNextStop(trainData: trainData,),
|
||||
// ),
|
||||
Expanded(
|
||||
child: DisplayTrainDestination(trainData: trainData,),
|
||||
),
|
||||
SizedBox(
|
||||
height: double.infinity,
|
||||
child: CupertinoVerticalDivider(),
|
||||
),
|
||||
Expanded(child: DisplayTrainRouteDistance(trainData: trainData,),),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// SliverToBoxAdapter(
|
||||
// child: CupertinoDivider(),
|
||||
// ),
|
||||
// SliverToBoxAdapter(
|
||||
// child: IntrinsicHeight(
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// // Expanded(
|
||||
// // child: DisplayTrainRouteDuration(trainData: trainData,),
|
||||
// // ),
|
||||
// Expanded(child: Container(),),
|
||||
// SizedBox(
|
||||
// height: double.infinity,
|
||||
// child: CupertinoVerticalDivider(),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: DisplayTrainRouteDistance(trainData: trainData,),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
SliverToBoxAdapter(
|
||||
child: CupertinoDivider(
|
||||
color: FOREGROUND_WHITE,
|
||||
),
|
||||
),
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// return CupertinoPageScaffold(
|
||||
// navigationBar: CupertinoNavigationBar(
|
||||
// middle: Text(title ?? ""),
|
||||
// ),
|
||||
// child: SafeArea(
|
||||
// bottom: false,
|
||||
// child: FutureBuilder<OnDemandTrainData>(
|
||||
// future: TrainDataWebViewAdapter.of(context).trainData(onInvalidation: () {
|
||||
// Navigator.of(context).pop();
|
||||
// }),
|
||||
// builder: (context, snapshot) {
|
||||
// if (!snapshot.hasData) {
|
||||
// return Center(
|
||||
// child: CupertinoActivityIndicator(),
|
||||
// );
|
||||
// }
|
||||
|
||||
// try {
|
||||
// Future.wait([
|
||||
// snapshot.data.rang,
|
||||
// snapshot.data.trainNumber
|
||||
// ]).then((values) {
|
||||
// setState(() {
|
||||
// title = "Informații despre ${values[0]} ${values[1]}";
|
||||
// });
|
||||
// });
|
||||
|
||||
// return CustomScrollView(
|
||||
// slivers: <Widget>[
|
||||
// DisplayTrainID(data: snapshot.data,),
|
||||
// DisplayTrainOperator(data: snapshot.data,),
|
||||
// DisplayTrainRoute(data: snapshot.data,),
|
||||
// DisplayTrainDeparture(data: snapshot.data,),
|
||||
// SliverToBoxAdapter(
|
||||
// child: CupertinoDivider(
|
||||
// color: FOREGROUND_WHITE,
|
||||
// ),
|
||||
// ),
|
||||
// DisplayTrainLastInfo(data: snapshot.data,),
|
||||
// SliverToBoxAdapter(
|
||||
// child: CupertinoDivider(),
|
||||
// ),
|
||||
// SliverToBoxAdapter(
|
||||
// child: IntrinsicHeight(
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// Expanded(
|
||||
// child: DisplayTrainNextStop(data: snapshot.data,),
|
||||
// ),
|
||||
// SizedBox(
|
||||
// height: double.infinity,
|
||||
// child: CupertinoVerticalDivider(),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: DisplayTrainDestination(data: snapshot.data,),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// SliverToBoxAdapter(
|
||||
// child: CupertinoDivider(),
|
||||
// ),
|
||||
// SliverToBoxAdapter(
|
||||
// child: IntrinsicHeight(
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// Expanded(
|
||||
// child: DisplayTrainRouteDuration(data: snapshot.data,),
|
||||
// ),
|
||||
// SizedBox(
|
||||
// height: double.infinity,
|
||||
// child: CupertinoVerticalDivider(),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: DisplayTrainRouteDistance(data: snapshot.data,),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// SliverToBoxAdapter(
|
||||
// child: CupertinoDivider(
|
||||
// color: FOREGROUND_WHITE,
|
||||
// ),
|
||||
// ),
|
||||
// DisplayTrainStations(
|
||||
// data: snapshot.data,
|
||||
// pageLoadFuture: TrainDataWebViewAdapter.of(context).nextLoadFuture,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// on OnDemandInvalidatedException {
|
||||
// Navigator.of(context).pop();
|
||||
// print("Got OnDemandInvalidatedException!");
|
||||
// return Container();
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainID extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
DisplayTrainID({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
"${trainData.rank} ${trainData.number}",
|
||||
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRoute extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainRoute({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.route.from,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Center(child: Text("-")),
|
||||
Expanded(child: Container(),),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.route.to,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainOperator extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainOperator({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
child: Text(
|
||||
trainData.operator,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 14,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainDeparture extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainDeparture({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
// "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
|
||||
"Plecare în ${trainData.date}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontWeight: FontWeight.w200,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainLastInfo extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainLastInfo({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (trainData.status == null) {
|
||||
return SliverToBoxAdapter(child: Container(),);
|
||||
}
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
"Ultima informație",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.status!.station,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
stateToString(trainData.status!.state),
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// FutureDisplay<DateTime>(
|
||||
// future: trainData.lastInfo.dateAndTime,
|
||||
// builder: (context, dt) {
|
||||
// return Text(
|
||||
// "Raportat în ${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year.toString().padLeft(4, '0')}, la ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}",
|
||||
// textAlign: TextAlign.center,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final data = trainData.status!.delay;
|
||||
|
||||
if (data == 0) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
if (data > 0) {
|
||||
return Text(
|
||||
"$data minute întârziere",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 14,
|
||||
color: CupertinoColors.destructiveRed,
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"${-data} minute mai devreme",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 12,
|
||||
color: CupertinoColors.activeGreen,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class DisplayTrainNextStop extends StatelessWidget {
|
||||
// final TrainData trainData;
|
||||
//
|
||||
// DisplayTrainNextStop({required this.trainData});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return FutureBuilder(
|
||||
// future: trainData.nextStop.stationName,
|
||||
// builder: (context, snapshot) {
|
||||
// if (!snapshot.hasData) return Container();
|
||||
//
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(4),
|
||||
// child: Text(
|
||||
// "Următoarea oprire",
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
// fontSize: 20,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// ),
|
||||
// CupertinoDivider(
|
||||
// color: Color.fromRGBO(15, 15, 15, 1),
|
||||
// ),
|
||||
// FutureDisplay(
|
||||
// future: trainData.nextStop.stationName,
|
||||
// builder: (context, station) {
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
|
||||
// child: Text(
|
||||
// station,
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// FutureDisplay<DateTime>(
|
||||
// future: trainData.nextStop.arrival,
|
||||
// builder: (context, arrival) {
|
||||
// const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
|
||||
//
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// Text(
|
||||
// "în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
// fontSize: 14,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// Text(
|
||||
// "la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
// fontSize: 14,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
// )
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class DisplayTrainDestination extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainDestination({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
"Destinația",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
CupertinoDivider(
|
||||
color: Color.fromRGBO(15, 15, 15, 1),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
|
||||
child: Text(
|
||||
trainData.stations.last.name,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final arrival = trainData.stations.last.arrival!.scheduleTime;
|
||||
final delay = trainData.stations.last.arrival!.status?.delay ?? 0;
|
||||
final parts = arrival.split(':');
|
||||
final arrivalDT = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, int.parse(parts[0]), int.parse(parts[1]));
|
||||
final arrivalWithDelay = arrivalDT.add(Duration(minutes: delay));
|
||||
final arrivalWithDelayString = '${arrivalWithDelay.hour}:${arrivalWithDelay.minute.toString().padLeft(2, "0")}';
|
||||
// const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
// Text(
|
||||
// "în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
// fontSize: 14,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
text: 'la',
|
||||
children: [
|
||||
TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: '$arrival',
|
||||
style: delay == 0 ? null : TextStyle(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
if (delay != 0) ...[
|
||||
TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: '$arrivalWithDelayString',
|
||||
style: TextStyle(
|
||||
color: delay > 0 ? CupertinoColors.destructiveRed : CupertinoColors.activeGreen,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
// "la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRouteDistance extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainRouteDistance({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Distanța rutei",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
"${trainData.stations.last.km} km",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class DisplayTrainRouteDuration extends StatelessWidget {
|
||||
// final TrainData trainData;
|
||||
//
|
||||
// DisplayTrainRouteDuration({required this.trainData});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// Text(
|
||||
// "Durata rutei",
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// FutureDisplay<Duration>(
|
||||
// future: trainData.routeDuration,
|
||||
// builder: (context, duration) {
|
||||
// var durationString = StringBuffer();
|
||||
//
|
||||
// bool firstWritten = false;
|
||||
//
|
||||
// if (duration.inDays > 0) {
|
||||
// firstWritten = true;
|
||||
// if (duration.inDays == 1) durationString.write("1 zi");
|
||||
// else durationString.write("${duration.inDays} zile");
|
||||
// duration -= Duration(days: duration.inDays);
|
||||
// }
|
||||
//
|
||||
// if (duration.inHours > 0) {
|
||||
// if (firstWritten) {
|
||||
// durationString.write(", ");
|
||||
// }
|
||||
// firstWritten = true;
|
||||
// if (duration.inHours == 1) durationString.write("1 oră");
|
||||
// else durationString.write("${duration.inHours} ore");
|
||||
// duration -= Duration(hours: duration.inHours);
|
||||
// }
|
||||
//
|
||||
// if (duration.inMinutes > 0) {
|
||||
// if (firstWritten) {
|
||||
// durationString.write(", ");
|
||||
// }
|
||||
// firstWritten = true;
|
||||
// if (duration.inMinutes == 1) durationString.write("1 minut");
|
||||
// else durationString.write("${duration.inMinutes} minute");
|
||||
// duration -= Duration(minutes: duration.inMinutes);
|
||||
// }
|
||||
//
|
||||
// return Text(
|
||||
// durationString.toString(),
|
||||
// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
// fontSize: 16,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class DisplayTrainStations extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainStations({required this.trainData,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index.isOdd) {
|
||||
return CupertinoDivider();
|
||||
}
|
||||
else {
|
||||
final itemIndex = index ~/ 2;
|
||||
return IndexedSemantics(
|
||||
child: DisplayTrainStation(
|
||||
station: trainData.stations[itemIndex],
|
||||
),
|
||||
index: itemIndex,
|
||||
);
|
||||
}
|
||||
},
|
||||
childCount: trainData.stations.length * 2 - 1,
|
||||
addSemanticIndexes: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,466 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
import 'package:info_tren/pages/train_info_page/train_info_constants.dart';
|
||||
|
||||
class DisplayTrainStation extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
DisplayTrainStation({required this.station});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final delay = station.departure?.status?.delay ?? station.arrival?.status?.delay;
|
||||
final real = station.departure?.status?.real ?? station.arrival?.status?.real;
|
||||
|
||||
final isDelayed = delay != null && delay > 0 && real == true;
|
||||
final isOnTime = delay != null && delay <= 0 && real == true;
|
||||
final isNotScheduled = false;
|
||||
|
||||
return KmBadge(
|
||||
station: station,
|
||||
isNotScheduled: isNotScheduled,
|
||||
isDelayed: isDelayed,
|
||||
isOnTime: isOnTime,
|
||||
);
|
||||
}
|
||||
),
|
||||
Expanded(
|
||||
child: Title(
|
||||
station: station,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Time(
|
||||
station: station,
|
||||
),
|
||||
Delay(
|
||||
station: station,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KmBadge extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool isNotScheduled;
|
||||
final bool isOnTime;
|
||||
final bool isDelayed;
|
||||
|
||||
KmBadge({
|
||||
required this.station,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color foregroundColor = FOREGROUND_WHITE;
|
||||
Color? backgroundColor;
|
||||
|
||||
if (isNotScheduled) {
|
||||
foregroundColor = Color.fromRGBO(225, 175, 30, 1);
|
||||
backgroundColor = Color.fromRGBO(80, 40, 10, 1);
|
||||
}
|
||||
else if (isOnTime) {
|
||||
foregroundColor = Color.fromRGBO(130, 175, 65, 1);
|
||||
backgroundColor = Color.fromRGBO(40, 80, 10, 1);
|
||||
}
|
||||
else if (isDelayed) {
|
||||
foregroundColor = Color.fromRGBO(225, 75, 30, 1);
|
||||
backgroundColor = Color.fromRGBO(80, 20, 10, 1);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: foregroundColor,
|
||||
),
|
||||
color: backgroundColor,
|
||||
// color: CupertinoColors.activeOrange,
|
||||
),
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
station.km.toString(),
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
|
||||
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"km",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 10,
|
||||
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Title extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
Title({
|
||||
required this.station
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
station.name,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
|
||||
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Time extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
Time({
|
||||
required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival == null) {
|
||||
// Plecare
|
||||
return DepartureTime(
|
||||
station: station,
|
||||
firstStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (station.departure == null) {
|
||||
// Sosire
|
||||
return ArrivalTime(
|
||||
station: station,
|
||||
finalStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
StopTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrivalTime extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool finalStation;
|
||||
|
||||
ArrivalTime({
|
||||
required this.station,
|
||||
this.finalStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (finalStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
Text("sosire la "),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final delay = station.arrival!.status?.delay ?? 0;
|
||||
final time = station.arrival!.scheduleTime;
|
||||
|
||||
if (delay == 0) {
|
||||
return Text("$time");
|
||||
}
|
||||
else if (delay > 0) {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.subtract(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.activeGreen,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StopTime extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
StopTime({
|
||||
required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final stopsFor = station.stoppingTime!;
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"staționează pentru",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
int stopsForInt = stopsFor;
|
||||
if (stopsForInt == 1) {
|
||||
return Text(
|
||||
"1 minut",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else if (stopsForInt < 20) {
|
||||
return Text(
|
||||
"$stopsFor minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"$stopsFor de minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DepartureTime extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool firstStation;
|
||||
|
||||
DepartureTime({
|
||||
required this.station,
|
||||
this.firstStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (firstStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(child: Container(),),
|
||||
Text("plecare la "),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final delay = station.departure!.status?.delay ?? 0;
|
||||
final time = station.departure!.scheduleTime;
|
||||
|
||||
if (delay == 0) {
|
||||
return Text("$time");
|
||||
}
|
||||
else if (delay > 0) {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.subtract(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.activeGreen,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Delay extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
Delay({
|
||||
required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival?.status == null && station.departure?.status == null) {
|
||||
return Container();
|
||||
}
|
||||
var delay = station.arrival?.status?.delay;
|
||||
if (station.departure?.status?.real == true) {
|
||||
delay = station.departure?.status?.delay;
|
||||
}
|
||||
|
||||
if (delay == 0 || delay == null) return Container();
|
||||
else if (delay > 0) {
|
||||
return Text(
|
||||
"$delay minute întârziere",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
fontSize: 14,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
else if (delay < 0) {
|
||||
return Text(
|
||||
"${-delay} minute mai devreme",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.activeGreen,
|
||||
fontSize: 14,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
}
|
||||
}
|
659
lib/pages/train_info_page/view_train/train_info_material.dart
Normal file
|
@ -0,0 +1,659 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/components/slim_app_bar.dart';
|
||||
import 'package:info_tren/models/train_data.dart' hide State;
|
||||
import 'package:info_tren/models/ui_design.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_material_DisplayTrainStation.dart';
|
||||
import 'package:info_tren/utils/state_to_string.dart';
|
||||
|
||||
class TrainInfoLoadingMaterial extends TrainInfoLoading {
|
||||
TrainInfoLoadingMaterial({required String title, String? loadingText}) : super(title: title, loadingText: loadingText, uiDesign: UiDesign.MATERIAL);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(title),
|
||||
),
|
||||
body: Center(
|
||||
child: loadingWidget,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoErrorMaterial extends TrainInfoError {
|
||||
TrainInfoErrorMaterial({required Object error, required String title,}) : super(error: error, title: title,);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(title),
|
||||
),
|
||||
body: Center(
|
||||
child: Text(error.toString()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool isSmallScreen(BuildContext context) => MediaQuery.of(context).size.height <= 425;
|
||||
|
||||
class TrainInfoMaterial extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
TrainInfoMaterial({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return Scaffold(
|
||||
appBar: isSmallScreen(context) ? null : AppBar(
|
||||
centerTitle: true,
|
||||
title: Text("Informații despre ${trainData.rank} ${trainData.number}"),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
if (isSmallScreen(context))
|
||||
SlimAppBar(
|
||||
title: 'INFO TREN - ${trainData.rank} ${trainData.number}'
|
||||
),
|
||||
Expanded(
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainID(trainData: trainData,),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainOperator(trainData: trainData,),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: DisplayTrainRoute(trainData: trainData,),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainDeparture(trainData: trainData,),
|
||||
),
|
||||
// SliverToBoxAdapter(
|
||||
// child: Divider(
|
||||
// color: Colors.white70,
|
||||
// height: isSmallScreen(context) ? 8 : 16,
|
||||
// ),
|
||||
// ),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainLastInfo(trainData: trainData,),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
// Expanded(child: DisplayTrainNextStop(trainData: trainData,)),
|
||||
Expanded(child: DisplayTrainDestination(trainData: trainData,)),
|
||||
Expanded(child: DisplayTrainRouteDistance(trainData: trainData,),),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// SliverToBoxAdapter(
|
||||
// child: IntrinsicHeight(
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// // Expanded(child: DisplayTrainRouteDuration(trainData: trainData,)),
|
||||
// Expanded(child: Container(),),
|
||||
// Expanded(child: DisplayTrainRouteDistance(trainData: trainData,)),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
SliverToBoxAdapter(
|
||||
child: Divider(
|
||||
color: Colors.white70,
|
||||
height: isSmallScreen(context) ? 8 : 16,
|
||||
),
|
||||
),
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainID extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainID({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
"${trainData.rank} ${trainData.number}",
|
||||
style: (isSmallScreen(context)
|
||||
? Theme.of(context).textTheme.headline4
|
||||
: Theme.of(context).textTheme.headline3)?.copyWith(
|
||||
color: Theme.of(context).textTheme.bodyText2?.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainOperator extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainOperator({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
trainData.operator,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRoute extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainRoute({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.route.from,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Center(child: Text("-")),
|
||||
Expanded(child: Container(),),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.route.to,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainDeparture extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainDeparture({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
// "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
|
||||
"Plecare în ${trainData.date}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontWeight: FontWeight.w200,
|
||||
fontSize: isSmallScreen(context) ? 14 : 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainLastInfo extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainLastInfo({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (trainData.status == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
"Ultima informație",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 20 : 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.status!.station,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
stateToString(trainData.status!.state),
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
// FutureDisplay<DateTime>(
|
||||
// future: trainData.lastInfo.dateAndTime,
|
||||
// builder: (context, dt) {
|
||||
// return Text(
|
||||
// "Raportat în ${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year.toString().padLeft(4, '0')}, la ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}",
|
||||
// textAlign: TextAlign.center,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
Expanded(child: Container(),),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final data = trainData.status!.delay;
|
||||
if (data == 0) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
if (data > 0) {
|
||||
return Text(
|
||||
"$data minute întârziere",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 14 : 16,
|
||||
color: Colors.red.shade300,
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"${-data} minute mai devreme",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 14 : 16,
|
||||
color: Colors.green.shade300,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class DisplayTrainNextStop extends StatelessWidget {
|
||||
// final OnDemandTrainData trainData;
|
||||
//
|
||||
// DisplayTrainNextStop({@required this.trainData});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return FutureBuilder(
|
||||
// future: trainData.nextStop.stationName,
|
||||
// builder: (context, snapshot) {
|
||||
// if (!snapshot.hasData) return Container(height: 0,);
|
||||
//
|
||||
// return Card(
|
||||
// child: Center(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(2),
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(4),
|
||||
// child: Text(
|
||||
// "Următoarea oprire",
|
||||
// style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 18 : 20,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// ),
|
||||
// FutureDisplay(
|
||||
// future: trainData.nextStop.stationName,
|
||||
// builder: (context, station) {
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
|
||||
// child: Text(
|
||||
// station,
|
||||
// style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// FutureDisplay<DateTime>(
|
||||
// future: trainData.nextStop.arrival,
|
||||
// builder: (context, arrival) {
|
||||
// const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
|
||||
//
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// Text(
|
||||
// "în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
|
||||
// style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// Text(
|
||||
// "la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
|
||||
// style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// },
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class DisplayTrainDestination extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainDestination({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final destination = trainData.stations.last;
|
||||
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
"Destinația",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 20 : 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
|
||||
child: Text(
|
||||
destination.name,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final arrival = destination.arrival!.scheduleTime;
|
||||
final delay = trainData.stations.last.arrival!.status?.delay ?? 0;
|
||||
final parts = arrival.split(':');
|
||||
final arrivalDT = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, int.parse(parts[0]), int.parse(parts[1]));
|
||||
final arrivalWithDelay = arrivalDT.add(Duration(minutes: delay));
|
||||
final arrivalWithDelayString = '${arrivalWithDelay.hour}:${arrivalWithDelay.minute.toString().padLeft(2, "0")}';
|
||||
// const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
// Text(
|
||||
// "în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
|
||||
// style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
Text.rich(
|
||||
// "la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
|
||||
TextSpan(
|
||||
text: 'la',
|
||||
children: [
|
||||
TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: '$arrival',
|
||||
style: delay == 0 ? null : TextStyle(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
if (delay != 0) ...[
|
||||
TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: '$arrivalWithDelayString',
|
||||
style: TextStyle(
|
||||
color: delay > 0 ? Colors.red.shade300 : Colors.green.shade300,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 14 : 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRouteDistance extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainRouteDistance({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Distanța rutei",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 20 : 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
"${trainData.stations.last.km} km",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 20,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class DisplayTrainRouteDuration extends StatelessWidget {
|
||||
// final TrainData trainData;
|
||||
//
|
||||
// DisplayTrainRouteDuration({required this.trainData});
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Card(
|
||||
// child: Center(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(2),
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// Text(
|
||||
// "Durata rutei",
|
||||
// style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// FutureDisplay<Duration>(
|
||||
// future: trainData.routeDuration,
|
||||
// builder: (context, duration) {
|
||||
// var durationString = StringBuffer();
|
||||
//
|
||||
// bool firstWritten = false;
|
||||
//
|
||||
// if (duration.inDays > 0) {
|
||||
// firstWritten = true;
|
||||
// if (duration.inDays == 1) durationString.write("1 zi");
|
||||
// else durationString.write("${duration.inDays} zile");
|
||||
// duration -= Duration(days: duration.inDays);
|
||||
// }
|
||||
//
|
||||
// if (duration.inHours > 0) {
|
||||
// if (firstWritten) {
|
||||
// durationString.write(", ");
|
||||
// }
|
||||
// firstWritten = true;
|
||||
// if (duration.inHours == 1) durationString.write("1 oră");
|
||||
// else durationString.write("${duration.inHours} ore");
|
||||
// duration -= Duration(hours: duration.inHours);
|
||||
// }
|
||||
//
|
||||
// if (duration.inMinutes > 0) {
|
||||
// if (firstWritten) {
|
||||
// durationString.write(", ");
|
||||
// }
|
||||
// firstWritten = true;
|
||||
// if (duration.inMinutes == 1) durationString.write("1 minut");
|
||||
// else durationString.write("${duration.inMinutes} minute");
|
||||
// duration -= Duration(minutes: duration.inMinutes);
|
||||
// }
|
||||
//
|
||||
// return Text(
|
||||
// durationString.toString(),
|
||||
// style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 14 : 16,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class DisplayTrainStations extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
DisplayTrainStations({required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return IndexedSemantics(
|
||||
child: DisplayTrainStation(
|
||||
station: trainData.stations[index],
|
||||
),
|
||||
index: index,
|
||||
);
|
||||
},
|
||||
childCount: trainData.stations.length,
|
||||
addSemanticIndexes: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,476 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart' show isSmallScreen;
|
||||
|
||||
class DisplayTrainStation extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
DisplayTrainStation({required this.station});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final delay = station.departure?.status?.delay ?? station.arrival?.status?.delay;
|
||||
final real = station.departure?.status?.real ?? station.arrival?.status?.real;
|
||||
|
||||
final isDelayed = delay != null && delay > 0 && real == true;
|
||||
final isOnTime = delay != null && delay <= 0 && real == true;
|
||||
final isNotScheduled = false;
|
||||
|
||||
return KmBadge(
|
||||
station: station,
|
||||
isNotScheduled: isNotScheduled,
|
||||
isDelayed: isDelayed,
|
||||
isOnTime: isOnTime,
|
||||
);
|
||||
}
|
||||
),
|
||||
Expanded(
|
||||
child: Title(
|
||||
station: station,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Time(
|
||||
station: station,
|
||||
),
|
||||
Delay(
|
||||
station: station,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KmBadge extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool isNotScheduled;
|
||||
final bool isOnTime;
|
||||
final bool isDelayed;
|
||||
|
||||
KmBadge({
|
||||
required this.station,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color foregroundColor = Colors.white70;
|
||||
Color? backgroundColor;
|
||||
|
||||
if (isNotScheduled) {
|
||||
foregroundColor = Colors.orange.shade300;
|
||||
backgroundColor = Colors.orange.shade900.withOpacity(0.3);
|
||||
}
|
||||
else if (isOnTime) {
|
||||
foregroundColor = Colors.green.shade300;
|
||||
backgroundColor = Colors.green.shade900.withOpacity(0.3);
|
||||
}
|
||||
else if (isDelayed) {
|
||||
foregroundColor = Colors.red.shade300;
|
||||
backgroundColor = Colors.red.shade900.withOpacity(0.3);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: foregroundColor,
|
||||
),
|
||||
color: backgroundColor,
|
||||
),
|
||||
width: isSmallScreen(context) ? 42 : 48,
|
||||
height: isSmallScreen(context) ? 42 : 48,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Text(
|
||||
station.km.toString(),
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 16 : 20,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"km",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: 10,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Title extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
Title({
|
||||
required this.station
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
station.name,
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
|
||||
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Time extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
Time({
|
||||
required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival == null) {
|
||||
// Plecare
|
||||
return DepartureTime(
|
||||
station: station,
|
||||
firstStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (station.departure == null) {
|
||||
// Sosire
|
||||
return ArrivalTime(
|
||||
station: station,
|
||||
finalStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
StopTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrivalTime extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool finalStation;
|
||||
|
||||
ArrivalTime({
|
||||
required this.station,
|
||||
this.finalStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival == null) {
|
||||
return Container();
|
||||
}
|
||||
if (finalStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
Text("sosire la "),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final delay = station.arrival!.status?.delay ?? 0;
|
||||
final time = station.arrival!.scheduleTime;
|
||||
|
||||
if (delay == 0) {
|
||||
return Text("$time");
|
||||
}
|
||||
else if (delay > 0) {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
color: Colors.red.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
color: Colors.green.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StopTime extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
StopTime({
|
||||
required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"staționează pentru",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
int stopsForInt = station.stoppingTime!;
|
||||
if (stopsForInt == 1) {
|
||||
return Text(
|
||||
"1 minut",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else if (stopsForInt < 20) {
|
||||
return Text(
|
||||
"${station.stoppingTime} minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"${station.stoppingTime} de minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DepartureTime extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool firstStation;
|
||||
|
||||
DepartureTime({
|
||||
required this.station,
|
||||
this.firstStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.departure == null) {
|
||||
return Container();
|
||||
}
|
||||
if (firstStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(child: Container(),),
|
||||
Text("plecare la "),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final delay = station.departure!.status?.delay ?? 0;
|
||||
final time = station.departure!.scheduleTime;
|
||||
|
||||
if (delay == 0) {
|
||||
return Text("$time");
|
||||
}
|
||||
else if (delay > 0) {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
color: Colors.red.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = time.split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final oldDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
color: Colors.green.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Delay extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
Delay({
|
||||
required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival?.status == null && station.departure?.status == null) {
|
||||
return Container();
|
||||
}
|
||||
var delay = station.arrival?.status?.delay;
|
||||
if (station.departure?.status?.real == true) {
|
||||
delay = station.departure?.status?.delay;
|
||||
}
|
||||
|
||||
if (delay == 0 || delay == null) return Container();
|
||||
else if (delay > 0) {
|
||||
return Text(
|
||||
"$delay minute întârziere",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
color: Colors.red.shade300,
|
||||
fontSize: 14,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
else if (delay < 0) {
|
||||
return Text(
|
||||
"${-delay} minute mai devreme",
|
||||
style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
color: Colors.green.shade300,
|
||||
fontSize: 14,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ enum NotchStyle {
|
|||
}
|
||||
|
||||
class StopListLine extends StatelessWidget {
|
||||
final StationEntry station;
|
||||
final Station station;
|
||||
final int width;
|
||||
StopListLine(this.station, {this.width = 32}) : assert(width.isEven);
|
||||
|
||||
|
@ -151,7 +151,7 @@ class StopListLinePainter extends CustomPainter {
|
|||
}
|
||||
|
||||
class StopOnLineDetails extends StatelessWidget {
|
||||
final StationEntry station;
|
||||
final Station station;
|
||||
StopOnLineDetails(this.station);
|
||||
|
||||
@override
|
||||
|
@ -172,11 +172,11 @@ class StopOnLineDetails extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
if (station.observations == "ONI")
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.5),
|
||||
child: Text("oprire ne-itinerarică", style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),),
|
||||
),
|
||||
// if (station.observations == "ONI")
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.5),
|
||||
// child: Text("oprire ne-itinerarică", style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),),
|
||||
// ),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8.0, 0.5, 8.0, 8.0),
|
||||
child: Text(
|
||||
|
@ -186,11 +186,12 @@ class StopOnLineDetails extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
StopOnLineTimeDetails(station),
|
||||
if (station.real)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: StopOnLineDelayDetails(station),
|
||||
),
|
||||
// TODO: Figure out how to display delay info
|
||||
// if (station.arrival != null && station.arrival.status != null || station.departure != null && station.departure.status != null)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(2.0),
|
||||
// child: StopOnLineDelayDetails(station),
|
||||
// ),
|
||||
Divider(
|
||||
height: 0,
|
||||
),
|
||||
|
@ -201,14 +202,14 @@ class StopOnLineDetails extends StatelessWidget {
|
|||
}
|
||||
|
||||
class StopOnLineTimeDetails extends StatelessWidget {
|
||||
final StationEntry station;
|
||||
final Station station;
|
||||
StopOnLineTimeDetails(this.station);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
if (station.arrivalTime.isNotEmpty)
|
||||
if (station.arrival != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Align(
|
||||
|
@ -221,14 +222,14 @@ class StopOnLineTimeDetails extends StatelessWidget {
|
|||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text(
|
||||
station.arrivalTime,
|
||||
station.arrival.scheduleTime,
|
||||
textAlign: TextAlign.left,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (station.waitTime.isNotEmpty)
|
||||
if (station.stoppingTime != null)
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
|
@ -242,7 +243,7 @@ class StopOnLineTimeDetails extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
"${station.waitTime} ${station.waitTime == "1" ? "minut" : "minute"}",
|
||||
"${station.stoppingTime} ${station.stoppingTime == 1 ? "minut" : "minute"}",
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
],
|
||||
|
@ -252,7 +253,7 @@ class StopOnLineTimeDetails extends StatelessWidget {
|
|||
)
|
||||
else
|
||||
Expanded(child: Container(),),
|
||||
if (station.departureTime.isNotEmpty)
|
||||
if (station.departure != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Align(
|
||||
|
@ -265,7 +266,7 @@ class StopOnLineTimeDetails extends StatelessWidget {
|
|||
textAlign: TextAlign.right,
|
||||
),
|
||||
Text(
|
||||
station.departureTime,
|
||||
station.departure.scheduleTime,
|
||||
textAlign: TextAlign.right,
|
||||
)
|
||||
],
|
||||
|
@ -277,32 +278,32 @@ class StopOnLineTimeDetails extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class StopOnLineDelayDetails extends StatelessWidget {
|
||||
final StationEntry station;
|
||||
StopOnLineDelayDetails(this.station);
|
||||
// class StopOnLineDelayDetails extends StatelessWidget {
|
||||
// final Station station;
|
||||
// StopOnLineDelayDetails(this.station);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.delay == 0) {
|
||||
return Text(
|
||||
"Fără întârziere",
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else if (station.delay < 0) {
|
||||
return Text(
|
||||
"${-(station.delay)} minute mai devreme",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.green.shade700),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"${station.delay} minute întârziere",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// if (station.delay == 0) {
|
||||
// return Text(
|
||||
// "Fără întârziere",
|
||||
// style: Theme.of(context).textTheme.caption,
|
||||
// textAlign: TextAlign.center,
|
||||
// );
|
||||
// }
|
||||
// else if (station.delay < 0) {
|
||||
// return Text(
|
||||
// "${-(station.delay)} minute mai devreme",
|
||||
// style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.green.shade700),
|
||||
// textAlign: TextAlign.center,
|
||||
// );
|
||||
// }
|
||||
// else {
|
||||
// return Text(
|
||||
// "${station.delay} minute întârziere",
|
||||
// style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),
|
||||
// textAlign: TextAlign.center,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/stations_list.dart';
|
||||
import 'package:info_tren/stations_list.dart.old';
|
||||
|
||||
import 'models/train_data.dart';
|
||||
|
||||
|
@ -29,33 +29,30 @@ class TrainInfoDisplayData extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(4.0),
|
||||
child: TotalDetails(trainData),
|
||||
),
|
||||
if (trainData.destination.station.isNotEmpty)
|
||||
...[
|
||||
CustomDivider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Destination(trainData),
|
||||
),
|
||||
],
|
||||
CustomDivider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Destination(trainData),
|
||||
),
|
||||
CustomDivider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: LastUpdate(trainData),
|
||||
),
|
||||
if (trainData.nextStop.station.isNotEmpty)
|
||||
...[
|
||||
CustomDivider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: NextStop(trainData),
|
||||
),
|
||||
],
|
||||
// if (trainData.nextStop.station.isNotEmpty)
|
||||
// ...[
|
||||
// CustomDivider(),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(4.0),
|
||||
// child: NextStop(trainData),
|
||||
// ),
|
||||
// ],
|
||||
CustomDivider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: TrainStatus(trainData),
|
||||
),
|
||||
Divider(color: Theme.of(context).accentColor,),
|
||||
Divider(color: Theme.of(context).colorScheme.secondary,),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: StationsList(trainData),
|
||||
|
@ -81,7 +78,7 @@ class TrainName extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
"${trainData.rang} ${trainData.trainNumber}",
|
||||
"${trainData.rank} ${trainData.number}",
|
||||
style: Theme.of(context).textTheme.headline3,
|
||||
);
|
||||
}
|
||||
|
@ -98,20 +95,20 @@ class TrainRoute extends StatelessWidget {
|
|||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
"${trainData.route.split("-")[0]}",
|
||||
style: Theme.of(context).textTheme.bodyText1.copyWith(fontStyle: FontStyle.italic),
|
||||
trainData.route.from,
|
||||
style: Theme.of(context).textTheme.bodyText1?.copyWith(fontStyle: FontStyle.italic),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"-",
|
||||
style: Theme.of(context).textTheme.bodyText1.copyWith(fontStyle: FontStyle.italic),
|
||||
style: Theme.of(context).textTheme.bodyText1?.copyWith(fontStyle: FontStyle.italic),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
"${trainData.route.split("-")[1]}",
|
||||
style: Theme.of(context).textTheme.bodyText1.copyWith(fontStyle: FontStyle.italic),
|
||||
trainData.route.to,
|
||||
style: Theme.of(context).textTheme.bodyText1?.copyWith(fontStyle: FontStyle.italic),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
|
@ -141,7 +138,7 @@ class TrainStatus extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
trainData.state,
|
||||
trainData.status.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.headline5,
|
||||
);
|
||||
|
@ -154,16 +151,16 @@ class Destination extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (trainData.destination.station.isEmpty) return Container();
|
||||
final destinationStation = trainData.stations.last;
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Destinația: ${trainData.destination.station}",
|
||||
"Destinația: ${destinationStation.name}",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
"Sosește în ${trainData.destination.dateAndTime.split(" ")[0]} la ${trainData.destination.dateAndTime.split(" ")[1]}",
|
||||
"Sosește la ${destinationStation.arrival!.scheduleTime}",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
|
@ -177,6 +174,9 @@ class LastUpdate extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (trainData.status == null) {
|
||||
return Container();
|
||||
}
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
|
@ -188,62 +188,63 @@ class LastUpdate extends StatelessWidget {
|
|||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Text(trainData.lastInfo.station, textAlign: TextAlign.left,),
|
||||
child: Text(trainData.status!.station, textAlign: TextAlign.left,),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Text(trainData.lastInfo.event, textAlign: TextAlign.right,),
|
||||
child: Text(trainData.status!.state.toString(), textAlign: TextAlign.right,),
|
||||
)
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: trainData.lastInfo.delay == 0
|
||||
child: trainData.status!.delay == 0
|
||||
? Text("Fără întârziere", style: Theme.of(context).textTheme.caption,)
|
||||
: trainData.lastInfo.delay > 0
|
||||
? Text("${trainData.lastInfo.delay} minute întârziere", style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),)
|
||||
: Text("${-(trainData.lastInfo.delay)} minute mai devreme", style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.green.shade700),)
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Text("Raportat la ${trainData.lastInfo.dateAndTime}"),
|
||||
: trainData.status!.delay > 0
|
||||
? Text("${trainData.status!.delay} minute întârziere", style: Theme.of(context).textTheme.bodyText2?.copyWith(color: Colors.red.shade700),)
|
||||
: Text("${-(trainData.status!.delay)} minute mai devreme", style: Theme.of(context).textTheme.bodyText2?.copyWith(color: Colors.green.shade700),)
|
||||
),
|
||||
// TODO: Implement status report time detection
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(2.0),
|
||||
// child: Text("Raportat la ${trainData.lastInfo.dateAndTime}"),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NextStop extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
NextStop(this.trainData);
|
||||
// class NextStop extends StatelessWidget {
|
||||
// final TrainData trainData;
|
||||
// NextStop(this.trainData);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Text("Următoarea oprire", style: Theme.of(context).textTheme.headline5,),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Text(trainData.nextStop.station, textAlign: TextAlign.left,),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Text(trainData.nextStop.dateAndTime, textAlign: TextAlign.right,),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(2.0),
|
||||
// child: Text("Următoarea oprire", style: Theme.of(context).textTheme.headline5,),
|
||||
// ),
|
||||
// Row(
|
||||
// children: <Widget>[
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(2.0),
|
||||
// child: Text(trainData.nextStop.station, textAlign: TextAlign.left,),
|
||||
// ),
|
||||
// Expanded(child: Container(),),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(2.0),
|
||||
// child: Text(trainData.nextStop.dateAndTime, textAlign: TextAlign.right,),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class TotalDetails extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
@ -254,18 +255,18 @@ class TotalDetails extends StatelessWidget {
|
|||
return Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
trainData.distance,
|
||||
'${trainData.stations.last.km} km',
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Expanded(
|
||||
child: Container()
|
||||
),
|
||||
Text(
|
||||
trainData.tripLength,
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
textAlign: TextAlign.right,
|
||||
)
|
||||
// Text(
|
||||
// trainData.tripLength,
|
||||
// style: Theme.of(context).textTheme.caption,
|
||||
// textAlign: TextAlign.right,
|
||||
// )
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_cupertino.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_material.dart';
|
||||
|
||||
mixin TrainInfoMixin {
|
||||
String title;
|
||||
bool showTrainData;
|
||||
TrainLookupResult lookupResult;
|
||||
bool requestedData;
|
||||
}
|
||||
|
||||
class TrainInfo extends StatelessWidget {
|
||||
static String routeName = "/trainInfo/display";
|
||||
|
||||
final int trainNumber;
|
||||
|
||||
TrainInfo({@required this.trainNumber});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TrainDataWebViewAdapter(
|
||||
builder: (context) {
|
||||
if (Platform.isAndroid) {
|
||||
return TrainInfoMaterial(trainNumber: trainNumber,);
|
||||
}
|
||||
else if (Platform.isIOS) {
|
||||
return TrainInfoCupertino(trainNumber: trainNumber,);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef FutureDisplayCallback<T>(BuildContext context, T data);
|
||||
|
||||
class FutureDisplay<T> extends StatelessWidget {
|
||||
final Future<T> future;
|
||||
final FutureDisplayCallback<T> builder;
|
||||
|
||||
FutureDisplay({Key key, @required this.future, @required this.builder}): super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: future,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) return builder(context, snapshot.data);
|
||||
if (snapshot.hasError) throw snapshot.error;
|
||||
if (snapshot.connectionState == ConnectionState.done) return Container();
|
||||
|
||||
Widget loadingWidget;
|
||||
if (Platform.isAndroid) {
|
||||
loadingWidget = CircularProgressIndicator();
|
||||
}
|
||||
else if (Platform.isIOS) {
|
||||
loadingWidget = CupertinoActivityIndicator();
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: loadingWidget,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,506 +0,0 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
import 'package:info_tren/train_info_page/train_info.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_constants.dart';
|
||||
|
||||
class DisplayTrainStation extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
DisplayTrainStation({@required this.station});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
FutureDisplay(
|
||||
future: Future.wait([
|
||||
station.delay,
|
||||
station.realOrEstimate,
|
||||
station.observations,
|
||||
]),
|
||||
builder: (context, data) {
|
||||
final isDelayed = (data[0] as int) > 0 && (data[1] as RealOrEstimate) == RealOrEstimate.real;
|
||||
final isOnTime = (data[0] as int) <= 0 && (data[1] as RealOrEstimate) == RealOrEstimate.real;
|
||||
final isNotScheduled = data[2] == "ONI";
|
||||
|
||||
return KmBadge(
|
||||
station: station,
|
||||
isNotScheduled: isNotScheduled,
|
||||
isDelayed: isDelayed,
|
||||
isOnTime: isOnTime,
|
||||
);
|
||||
}
|
||||
),
|
||||
Expanded(
|
||||
child: Title(
|
||||
station: station,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Time(
|
||||
station: station,
|
||||
),
|
||||
Delay(
|
||||
station: station,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KmBadge extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
final bool isNotScheduled;
|
||||
final bool isOnTime;
|
||||
final bool isDelayed;
|
||||
|
||||
KmBadge({
|
||||
@required this.station,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color foregroundColor = FOREGROUND_WHITE;
|
||||
Color backgroundColor;
|
||||
|
||||
if (isNotScheduled) {
|
||||
foregroundColor = Color.fromRGBO(225, 175, 30, 1);
|
||||
backgroundColor = Color.fromRGBO(80, 40, 10, 1);
|
||||
}
|
||||
else if (isOnTime) {
|
||||
foregroundColor = Color.fromRGBO(130, 175, 65, 1);
|
||||
backgroundColor = Color.fromRGBO(40, 80, 10, 1);
|
||||
}
|
||||
else if (isDelayed) {
|
||||
foregroundColor = Color.fromRGBO(225, 75, 30, 1);
|
||||
backgroundColor = Color.fromRGBO(80, 20, 10, 1);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: foregroundColor,
|
||||
),
|
||||
color: backgroundColor,
|
||||
// color: CupertinoColors.activeOrange,
|
||||
),
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: FutureDisplay<int>(
|
||||
future: station.km,
|
||||
builder: (context, value) {
|
||||
return Text(
|
||||
value.toString(),
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 18,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
|
||||
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"km",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 10,
|
||||
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Title extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
Title({
|
||||
@required this.station
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<String>>(
|
||||
future: Future.wait([
|
||||
station.stationName,
|
||||
station.observations
|
||||
]),
|
||||
builder: (context, items) {
|
||||
return Text(
|
||||
items[0],
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
|
||||
fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Time extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
Time({
|
||||
@required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<String>>(
|
||||
future: Future.wait([
|
||||
station.arrivalTime,
|
||||
station.stopsFor,
|
||||
station.departureTime,
|
||||
]),
|
||||
builder: (context, items) {
|
||||
if (items[0].isEmpty) {
|
||||
// Plecare
|
||||
return DepartureTime(
|
||||
station: station,
|
||||
firstStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (items[2].isEmpty) {
|
||||
// Sosire
|
||||
return ArrivalTime(
|
||||
station: station,
|
||||
finalStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
StopTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrivalTime extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
final bool finalStation;
|
||||
|
||||
ArrivalTime({
|
||||
@required this.station,
|
||||
this.finalStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<Object>>(
|
||||
future: Future.wait([
|
||||
station.arrivalTime,
|
||||
station.delay,
|
||||
]),
|
||||
builder: (context, data) {
|
||||
if (finalStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
Text("sosire la "),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (data[1] == 0) {
|
||||
return Text("${data[0]}");
|
||||
}
|
||||
else if (data[1] as int > 0) {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.activeGreen,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StopTime extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
StopTime({
|
||||
@required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<String>(
|
||||
future: station.stopsFor,
|
||||
builder: (context, stopsFor) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"staționează pentru",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
int stopsForInt = int.parse(stopsFor);
|
||||
if (stopsForInt == 1) {
|
||||
return Text(
|
||||
"1 minut",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else if (stopsForInt < 20) {
|
||||
return Text(
|
||||
"$stopsFor minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"$stopsFor de minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DepartureTime extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
final bool firstStation;
|
||||
|
||||
DepartureTime({
|
||||
@required this.station,
|
||||
this.firstStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<Object>>(
|
||||
future: Future.wait([
|
||||
station.departureTime,
|
||||
station.delay,
|
||||
]),
|
||||
builder: (context, data) {
|
||||
if (firstStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(child: Container(),),
|
||||
Text("plecare la "),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (data[1] == 0) {
|
||||
return Text("${data[0]}");
|
||||
}
|
||||
else if (data[1] as int > 0) {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.activeGreen,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Delay extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
Delay({
|
||||
@required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<int>(
|
||||
future: station.delay,
|
||||
builder: (context, delay) {
|
||||
if (delay == 0) return Container();
|
||||
|
||||
else if (delay > 0) {
|
||||
return Text(
|
||||
"$delay minute întârziere",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.destructiveRed,
|
||||
fontSize: 12,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
else if (delay < 0) {
|
||||
return Text(
|
||||
"${-delay} minute mai devreme",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
color: CupertinoColors.activeGreen,
|
||||
fontSize: 12,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,944 +0,0 @@
|
|||
import 'package:info_tren/train_info_page/train_info_animation_helpers.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_material_DisplayTrainStation.dart';
|
||||
import 'package:info_tren/utils/stream_list.dart';
|
||||
|
||||
import '../models/train_data.dart';
|
||||
import './train_info.dart';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TrainInfoMaterial extends StatefulWidget {
|
||||
final int trainNumber;
|
||||
|
||||
TrainInfoMaterial({@required this.trainNumber});
|
||||
|
||||
@override
|
||||
_TrainInfoMaterialState createState() => _TrainInfoMaterialState();
|
||||
}
|
||||
|
||||
class _TrainInfoMaterialState extends State<TrainInfoMaterial> with TrainInfoMixin {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
title = widget.trainNumber.toString();
|
||||
showTrainData = false;
|
||||
requestedData = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
if (!requestedData) {
|
||||
requestedData = true;
|
||||
|
||||
TrainDataWebViewAdapter.of(context).loadTrain(widget.trainNumber).then((value) {
|
||||
setState(() {
|
||||
lookupResult = value;
|
||||
});
|
||||
|
||||
if (lookupResult == TrainLookupResult.NOT_FOUND) {
|
||||
Future.delayed(Duration(seconds: 5), () {
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
}
|
||||
else if (lookupResult == TrainLookupResult.FOUND) {
|
||||
Future.delayed(Duration(seconds: 1, milliseconds: 500), () {
|
||||
setState(() {
|
||||
showTrainData = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!showTrainData) {
|
||||
return _TrainInfoMaterialBefore(
|
||||
title: title,
|
||||
lookupResult: lookupResult,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return _TrainDataMaterialAfter(
|
||||
title: title,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _TrainInfoMaterialBefore extends StatefulWidget {
|
||||
final String title;
|
||||
final TrainLookupResult lookupResult;
|
||||
|
||||
_TrainInfoMaterialBefore({@required this.title, @required this.lookupResult});
|
||||
|
||||
@override
|
||||
_TrainInfoMaterialBeforeState createState() => _TrainInfoMaterialBeforeState();
|
||||
}
|
||||
|
||||
class _TrainInfoMaterialBeforeState extends State<_TrainInfoMaterialBefore> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(widget.title ?? ""),
|
||||
),
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
child: StreamBuilder<ProgressReport>(
|
||||
stream: TrainDataWebViewAdapter.of(context).progressStream,
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.none:
|
||||
return Container();
|
||||
case ConnectionState.waiting:
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
CircularProgressIndicator(),
|
||||
Text(
|
||||
"Conectare...",
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
case ConnectionState.active:
|
||||
break;
|
||||
case ConnectionState.done:
|
||||
Navigator.of(context).pop();
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
ProgressReportDisplayEntry(
|
||||
key: ValueKey(1),
|
||||
completed: 1 <= snapshot.data.current,
|
||||
waitingText: "Se crează WebView",
|
||||
completedText: "WebView a fost creat",
|
||||
),
|
||||
ProgressReportDisplayEntry(
|
||||
key: ValueKey(2),
|
||||
completed: 2 <= snapshot.data.current,
|
||||
waitingText: "Se încarcă pagina Informatica Feroviară",
|
||||
completedText: "Pagina Informatica Feroviară a fost încărcată",
|
||||
),
|
||||
ProgressReportDisplayEntry(
|
||||
key: ValueKey(3),
|
||||
completed: 3 <= snapshot.data.current,
|
||||
waitingText: "Se încarcă informațiile despre tren",
|
||||
completedText: "Informațiile despre tren au fost încărcate",
|
||||
),
|
||||
if (widget.lookupResult != null)
|
||||
...[
|
||||
Container(height: 20,),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: AnimatedBackground(
|
||||
animationDuration: Duration(milliseconds: 250),
|
||||
initialColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
backgroundColor:
|
||||
widget.lookupResult == TrainLookupResult.FOUND
|
||||
? Colors.green
|
||||
: Colors.red,
|
||||
child: Center(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(child: Container(),),
|
||||
if (widget.lookupResult == TrainLookupResult.FOUND)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(8, 8, 0, 8),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation(Colors.greenAccent),
|
||||
)
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
widget.lookupResult == TrainLookupResult.FOUND
|
||||
? "Trenul a fost găsit"
|
||||
: widget.lookupResult == TrainLookupResult.NOT_FOUND
|
||||
? "Trenul nu a fost găsit"
|
||||
: "A apărut o eroare în căutarea trenului",
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool isSmallScreen(BuildContext context) => MediaQuery.of(context).size.height <= 425;
|
||||
|
||||
class _TrainDataMaterialAfter extends StatefulWidget {
|
||||
final String title;
|
||||
|
||||
_TrainDataMaterialAfter({@required this.title});
|
||||
|
||||
@override
|
||||
_TrainDataMaterialAfterState createState() => _TrainDataMaterialAfterState();
|
||||
}
|
||||
|
||||
class _TrainDataMaterialAfterState extends State<_TrainDataMaterialAfter> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<OnDemandTrainData>(
|
||||
future: TrainDataWebViewAdapter.of(context).trainData(onInvalidation: () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(widget.title ?? ""),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: isSmallScreen(context) ? null : AppBar(
|
||||
centerTitle: true,
|
||||
title: FutureBuilder<List<String>>(
|
||||
future: Future.wait([
|
||||
snapshot.data.rang,
|
||||
snapshot.data.trainNumber
|
||||
]),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return Text("Informații despre ${snapshot.data[0]} ${snapshot.data[1]}");
|
||||
}
|
||||
else {
|
||||
return Text(widget.title ?? "");
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
if (isSmallScreen(context))
|
||||
FutureBuilder<List<String>>(
|
||||
future: Future.wait([
|
||||
snapshot.data.rang,
|
||||
snapshot.data.trainNumber,
|
||||
]),
|
||||
builder: (context, snapshot) {
|
||||
var title = "INFO TREN";
|
||||
if (snapshot.hasData) title = "INFO TREN ─ ${snapshot.data[0]} ${snapshot.data[1]}";
|
||||
|
||||
return SlimAppBar(
|
||||
title: title,
|
||||
);
|
||||
}
|
||||
),
|
||||
Expanded(
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainID(trainData: snapshot.data,),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainOperator(trainData: snapshot.data,),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: DisplayTrainRoute(trainData: snapshot.data,),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainDeparture(trainData: snapshot.data,),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Divider(
|
||||
color: Colors.white70,
|
||||
height: isSmallScreen(context) ? 8 : 16,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainLastInfo(trainData: snapshot.data,),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(child: DisplayTrainNextStop(trainData: snapshot.data,)),
|
||||
Expanded(child: DisplayTrainDestination(trainData: snapshot.data,)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(child: DisplayTrainRouteDuration(trainData: snapshot.data,)),
|
||||
Expanded(child: DisplayTrainRouteDistance(trainData: snapshot.data,)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Divider(
|
||||
color: Colors.white70,
|
||||
height: isSmallScreen(context) ? 8 : 16,
|
||||
),
|
||||
),
|
||||
DisplayTrainStations(
|
||||
trainData: snapshot.data,
|
||||
pageLoadFuture: TrainDataWebViewAdapter.of(context).nextLoadFuture,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainID extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainID({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<String>>(
|
||||
future: Future.wait([
|
||||
trainData.rang,
|
||||
trainData.trainNumber,
|
||||
]),
|
||||
builder: (context, list) {
|
||||
return Text(
|
||||
"${list[0]} ${list[1]}",
|
||||
style: (isSmallScreen(context)
|
||||
? Theme.of(context).textTheme.headline4
|
||||
: Theme.of(context).textTheme.headline3).copyWith(
|
||||
color: Theme.of(context).textTheme.bodyText2.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainOperator extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainOperator({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<String>(
|
||||
future: trainData.operator,
|
||||
builder: (context, op) {
|
||||
return Text(
|
||||
op,
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRoute extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainRoute({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay(
|
||||
future: Future.wait([trainData.route.from, trainData.route.to]),
|
||||
builder: (context, routePieces) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
routePieces[0],
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Center(child: Text("-")),
|
||||
Expanded(child: Container(),),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
routePieces[1],
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainDeparture extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainDeparture({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: FutureDisplay<DateTime>(
|
||||
future: trainData.departureDate,
|
||||
builder: (context, dataPlecare) {
|
||||
return Text(
|
||||
"Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontWeight: FontWeight.w200,
|
||||
fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainLastInfo extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainLastInfo({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
"Ultima informație",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: FutureDisplay(
|
||||
future: trainData.lastInfo.station,
|
||||
builder: (context, station) {
|
||||
return Text(
|
||||
station,
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
textAlign: TextAlign.left,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: FutureDisplay(
|
||||
future: trainData.lastInfo.event,
|
||||
builder: (context, event) {
|
||||
return Text(
|
||||
event,
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
textAlign: TextAlign.right,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
FutureDisplay<DateTime>(
|
||||
future: trainData.lastInfo.dateAndTime,
|
||||
builder: (context, dt) {
|
||||
return Text(
|
||||
"Raportat în ${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year.toString().padLeft(4, '0')}, la ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
Expanded(child: Container(),),
|
||||
FutureBuilder(
|
||||
initialData: 0,
|
||||
future: trainData.lastInfo.delay,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data == 0) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
if (snapshot.data > 0) {
|
||||
return Text(
|
||||
"${snapshot.data} minute întârziere",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: 14,
|
||||
color: Color.fromRGBO(200, 30, 15, 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"${-snapshot.data} minute mai devreme",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: 12,
|
||||
color: Color.fromRGBO(15, 200, 15, 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainNextStop extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainNextStop({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: trainData.nextStop.stationName,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return Container(height: 0,);
|
||||
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
"Următoarea oprire",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
FutureDisplay(
|
||||
future: trainData.nextStop.stationName,
|
||||
builder: (context, station) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
|
||||
child: Text(
|
||||
station,
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FutureDisplay<DateTime>(
|
||||
future: trainData.nextStop.arrival,
|
||||
builder: (context, arrival) {
|
||||
const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
"la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainDestination extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainDestination({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: trainData.destination.stationName,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) return Container(height: 0,);
|
||||
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
"Destinația",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
FutureDisplay(
|
||||
future: trainData.destination.stationName,
|
||||
builder: (context, station) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
|
||||
child: Text(
|
||||
station,
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FutureDisplay<DateTime>(
|
||||
future: trainData.destination.arrival,
|
||||
builder: (context, arrival) {
|
||||
const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
"la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRouteDistance extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainRouteDistance({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Distanța rutei",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
FutureDisplay(
|
||||
future: trainData.routeDistance,
|
||||
builder: (context, distance) {
|
||||
return Text(
|
||||
"$distance km",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 14 : 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRouteDuration extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
|
||||
DisplayTrainRouteDuration({@required this.trainData});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Durata rutei",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 16 : 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
FutureDisplay<Duration>(
|
||||
future: trainData.routeDuration,
|
||||
builder: (context, duration) {
|
||||
var durationString = StringBuffer();
|
||||
|
||||
bool firstWritten = false;
|
||||
|
||||
if (duration.inDays > 0) {
|
||||
firstWritten = true;
|
||||
if (duration.inDays == 1) durationString.write("1 zi");
|
||||
else durationString.write("${duration.inDays} zile");
|
||||
duration -= Duration(days: duration.inDays);
|
||||
}
|
||||
|
||||
if (duration.inHours > 0) {
|
||||
if (firstWritten) {
|
||||
durationString.write(", ");
|
||||
}
|
||||
firstWritten = true;
|
||||
if (duration.inHours == 1) durationString.write("1 oră");
|
||||
else durationString.write("${duration.inHours} ore");
|
||||
duration -= Duration(hours: duration.inHours);
|
||||
}
|
||||
|
||||
if (duration.inMinutes > 0) {
|
||||
if (firstWritten) {
|
||||
durationString.write(", ");
|
||||
}
|
||||
firstWritten = true;
|
||||
if (duration.inMinutes == 1) durationString.write("1 minut");
|
||||
else durationString.write("${duration.inMinutes} minute");
|
||||
duration -= Duration(minutes: duration.inMinutes);
|
||||
}
|
||||
|
||||
return Text(
|
||||
durationString.toString(),
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 14 : 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainStations extends StatelessWidget {
|
||||
final OnDemandTrainData trainData;
|
||||
final Future pageLoadFuture;
|
||||
|
||||
DisplayTrainStations({@required this.trainData, @required this.pageLoadFuture});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder<List<OnDemandStation>>(
|
||||
stream: listifyStream(trainData.stations(pageLoadFuture: pageLoadFuture)),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Container(),
|
||||
);
|
||||
}
|
||||
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return IndexedSemantics(
|
||||
child: DisplayTrainStation(
|
||||
station: snapshot.data[index],
|
||||
),
|
||||
index: index,
|
||||
);
|
||||
},
|
||||
childCount: snapshot.data.length,
|
||||
addSemanticIndexes: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SlimAppBar extends StatelessWidget {
|
||||
final String title;
|
||||
final double size;
|
||||
// final Function onBackTap;
|
||||
|
||||
SlimAppBar({
|
||||
@required this.title,
|
||||
this.size = 24,
|
||||
// this.onBackTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: size,
|
||||
child: Container(
|
||||
color:
|
||||
Theme.of(context).appBarTheme?.color ??
|
||||
Theme.of(context).primaryColor,
|
||||
child: InkWell(
|
||||
onTap: (ModalRoute.of(context)?.canPop ?? false)
|
||||
? () => Navigator.of(context).pop()
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: size,
|
||||
width: size,
|
||||
child: (ModalRoute.of(context)?.canPop ?? false)
|
||||
? BackButtonIcon()
|
||||
: null,
|
||||
),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style:
|
||||
Theme.of(context).appBarTheme.textTheme?.caption?.copyWith(color: Theme.of(context).appBarTheme.textTheme?.bodyText2?.color) ??
|
||||
Theme.of(context).textTheme.caption.copyWith(color: Theme.of(context).textTheme.bodyText2.color),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: size,
|
||||
width: size,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,509 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/models/train_data.dart';
|
||||
import 'package:info_tren/train_info_page/train_info.dart';
|
||||
import 'package:info_tren/train_info_page/train_info_material.dart' show isSmallScreen;
|
||||
|
||||
class DisplayTrainStation extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
DisplayTrainStation({@required this.station});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
FutureDisplay(
|
||||
future: Future.wait([
|
||||
station.delay,
|
||||
station.realOrEstimate,
|
||||
station.observations,
|
||||
]),
|
||||
builder: (context, data) {
|
||||
final isDelayed = (data[0] as int) > 0 && (data[1] as RealOrEstimate) == RealOrEstimate.real;
|
||||
final isOnTime = (data[0] as int) <= 0 && (data[1] as RealOrEstimate) == RealOrEstimate.real;
|
||||
final isNotScheduled = data[2] == "ONI";
|
||||
|
||||
return KmBadge(
|
||||
station: station,
|
||||
isNotScheduled: isNotScheduled,
|
||||
isDelayed: isDelayed,
|
||||
isOnTime: isOnTime,
|
||||
);
|
||||
}
|
||||
),
|
||||
Expanded(
|
||||
child: Title(
|
||||
station: station,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Time(
|
||||
station: station,
|
||||
),
|
||||
Delay(
|
||||
station: station,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class KmBadge extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
final bool isNotScheduled;
|
||||
final bool isOnTime;
|
||||
final bool isDelayed;
|
||||
|
||||
KmBadge({
|
||||
@required this.station,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color foregroundColor = Colors.white70;
|
||||
Color backgroundColor;
|
||||
|
||||
if (isNotScheduled) {
|
||||
foregroundColor = Colors.orange.shade300;
|
||||
backgroundColor = Colors.orange.shade900.withOpacity(0.3);
|
||||
}
|
||||
else if (isOnTime) {
|
||||
foregroundColor = Colors.green.shade300;
|
||||
backgroundColor = Colors.green.shade900.withOpacity(0.3);
|
||||
}
|
||||
else if (isDelayed) {
|
||||
foregroundColor = Colors.red.shade300;
|
||||
backgroundColor = Colors.red.shade900.withOpacity(0.3);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
width: 2,
|
||||
color: foregroundColor,
|
||||
),
|
||||
color: backgroundColor,
|
||||
),
|
||||
width: isSmallScreen(context) ? 42 : 48,
|
||||
height: isSmallScreen(context) ? 42 : 48,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: FutureDisplay<int>(
|
||||
future: station.km,
|
||||
builder: (context, value) {
|
||||
return Text(
|
||||
value.toString(),
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 14 : 18,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"km",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: 10,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Title extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
Title({
|
||||
@required this.station
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<String>>(
|
||||
future: Future.wait([
|
||||
station.stationName,
|
||||
station.observations
|
||||
]),
|
||||
builder: (context, items) {
|
||||
return Text(
|
||||
items[0],
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
|
||||
fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Time extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
Time({
|
||||
@required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<String>>(
|
||||
future: Future.wait([
|
||||
station.arrivalTime,
|
||||
station.stopsFor,
|
||||
station.departureTime,
|
||||
]),
|
||||
builder: (context, items) {
|
||||
if (items[0].isEmpty) {
|
||||
// Plecare
|
||||
return DepartureTime(
|
||||
station: station,
|
||||
firstStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (items[2].isEmpty) {
|
||||
// Sosire
|
||||
return ArrivalTime(
|
||||
station: station,
|
||||
finalStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
StopTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrivalTime extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
final bool finalStation;
|
||||
|
||||
ArrivalTime({
|
||||
@required this.station,
|
||||
this.finalStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<Object>>(
|
||||
future: Future.wait([
|
||||
station.arrivalTime,
|
||||
station.delay,
|
||||
]),
|
||||
builder: (context, data) {
|
||||
if (finalStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: isSmallScreen(context) ? 18 : 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
Text("sosire la "),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (data[1] == 0) {
|
||||
return Text("${data[0]}");
|
||||
}
|
||||
else if (data[1] as int > 0) {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
color: Colors.red.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
color: Colors.green.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class StopTime extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
StopTime({
|
||||
@required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<String>(
|
||||
future: station.stopsFor,
|
||||
builder: (context, stopsFor) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"staționează pentru",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
int stopsForInt = int.parse(stopsFor);
|
||||
if (stopsForInt == 1) {
|
||||
return Text(
|
||||
"1 minut",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else if (stopsForInt < 20) {
|
||||
return Text(
|
||||
"$stopsFor minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"$stopsFor de minute",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DepartureTime extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
final bool firstStation;
|
||||
|
||||
DepartureTime({
|
||||
@required this.station,
|
||||
this.firstStation = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<List<Object>>(
|
||||
future: Future.wait([
|
||||
station.departureTime,
|
||||
station.delay,
|
||||
]),
|
||||
builder: (context, data) {
|
||||
if (firstStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(child: Container(),),
|
||||
Text("plecare la "),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (data[1] == 0) {
|
||||
return Text("${data[0]}");
|
||||
}
|
||||
else if (data[1] as int > 0) {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
color: Colors.red.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList();
|
||||
|
||||
final now = DateTime.now();
|
||||
final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]);
|
||||
final oldDate = newDate.subtract(Duration(minutes: data[1] as int));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${data[0]}",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
color: Colors.green.shade300,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Delay extends StatelessWidget {
|
||||
final OnDemandStation station;
|
||||
|
||||
Delay({
|
||||
@required this.station,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureDisplay<int>(
|
||||
future: station.delay,
|
||||
builder: (context, delay) {
|
||||
if (delay == 0) return Container();
|
||||
|
||||
else if (delay > 0) {
|
||||
return Text(
|
||||
"$delay minute întârziere",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
color: Colors.red.shade300,
|
||||
fontSize: 12,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
else if (delay < 0) {
|
||||
return Text(
|
||||
"${-delay} minute mai devreme",
|
||||
style: Theme.of(context).textTheme.bodyText2.copyWith(
|
||||
color: Colors.green.shade300,
|
||||
fontSize: 12,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,385 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:info_tren/train_info_page/train_info.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
part 'train_info_prompt.g.dart';
|
||||
|
||||
typedef TrainSelectedCallback(int trainNumber);
|
||||
|
||||
mixin TrainInfoPromptCommon {
|
||||
static String routeName = "/trainInfo/chooseTrain";
|
||||
|
||||
onTrainSelected(BuildContext context, int selection) {
|
||||
Navigator.of(context).pushNamed(TrainInfo.routeName, arguments: selection);
|
||||
}
|
||||
}
|
||||
|
||||
mixin TrainInfoPromptListHandling {
|
||||
List<TrainOperatorLines> operators = [];
|
||||
|
||||
Future loadOperators(BuildContext context) async {
|
||||
operators = [];
|
||||
|
||||
final operatorsString = await DefaultAssetBundle.of(context).loadString("assets/lines/files.txt");
|
||||
final operatorsFilesList = operatorsString.split("\n");
|
||||
|
||||
final decoder = JsonDecoder();
|
||||
|
||||
for (final operatorFile in operatorsFilesList) {
|
||||
final operatorString = await DefaultAssetBundle.of(context).loadString("assets/lines/$operatorFile");
|
||||
final operatorData = decoder.convert(operatorString);
|
||||
final _operator = TrainOperatorLines.fromJson(operatorData);
|
||||
operators.add(_operator);
|
||||
}
|
||||
}
|
||||
|
||||
Widget getOperatorsListView(BuildContext context, {String currentInput = "", @required TrainSelectedCallback onTrainSelected}) {
|
||||
var sliversTuple = operators.map(
|
||||
(op) => Tuple2(
|
||||
getFilteredLines(op, currentInput),
|
||||
getOperatorSliver(context, op, currentInput, onTrainSelected)
|
||||
)
|
||||
)
|
||||
.where((tuple) => tuple.item1.isNotEmpty).toList();
|
||||
if (currentInput.isNotEmpty) sliversTuple.sort((a, b) {
|
||||
final aTrain = a.item1.first;
|
||||
final bTrain = b.item1.first;
|
||||
|
||||
final inputAsRegExp = RegExp(currentInput);
|
||||
|
||||
final matchOnA = inputAsRegExp.firstMatch(aTrain.number);
|
||||
final matchOnB = inputAsRegExp.firstMatch(bTrain.number);
|
||||
|
||||
if (matchOnA.start != matchOnB.start) return matchOnA.start - matchOnB.start;
|
||||
|
||||
if (aTrain.number.length != bTrain.number.length) return aTrain.number.length - bTrain.number.length;
|
||||
|
||||
return aTrain.number.compareTo(bTrain.number);
|
||||
});
|
||||
var slivers = sliversTuple.map((tuple) => tuple.item2).toList();
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
...slivers,
|
||||
SliverToBoxAdapter(
|
||||
child: getUseCurrentInputWidget(currentInput, onTrainSelected),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget getUseCurrentInputWidget(String currentInput, TrainSelectedCallback onTrainSelected) {
|
||||
if (currentInput.isEmpty) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
if (int.tryParse(currentInput) == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
if (Platform.isAndroid)
|
||||
ListTile(
|
||||
title: Text("Caută trenul cu numărul $currentInput"),
|
||||
onTap: () {
|
||||
onTrainSelected(int.parse(currentInput));
|
||||
},
|
||||
)
|
||||
else if (Platform.isIOS)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
onTrainSelected(int.parse(currentInput));
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text("Caută trenul cu numărul $currentInput")
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<_TrainOperatorTrainDescription> getFilteredLines(TrainOperatorLines _operator, String currentInput) {
|
||||
if (currentInput.isNotEmpty) {
|
||||
final filteredLines = _operator.trains
|
||||
.where((elem) => elem.number.contains(currentInput))
|
||||
.toList();
|
||||
|
||||
filteredLines.sort((a, b) {
|
||||
final inputAsRegExp = RegExp(currentInput);
|
||||
|
||||
final matchOnA = inputAsRegExp.firstMatch(a.number);
|
||||
final matchOnB = inputAsRegExp.firstMatch(b.number);
|
||||
|
||||
if (matchOnA.start != matchOnB.start) return matchOnA.start - matchOnB.start;
|
||||
|
||||
if (a.number.length != b.number.length) return a.number.length - b.number.length;
|
||||
|
||||
return a.number.compareTo(b.number);
|
||||
});
|
||||
|
||||
return filteredLines;
|
||||
}
|
||||
else {
|
||||
return _operator.trains;
|
||||
}
|
||||
}
|
||||
|
||||
Widget getOperatorSliver(BuildContext context, TrainOperatorLines _operator, String currentInput, TrainSelectedCallback onTrainSelected) {
|
||||
final filteredLines = getFilteredLines(_operator, currentInput);
|
||||
|
||||
if (filteredLines.isEmpty) {
|
||||
return SliverToBoxAdapter(child: Container(),);
|
||||
}
|
||||
|
||||
return SliverPrototypeExtentList(
|
||||
prototypeItem: Column(
|
||||
children: <Widget>[
|
||||
getLineListItem(
|
||||
context,
|
||||
op: TrainOperatorLines(),
|
||||
line: _TrainOperatorTrainDescription()
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
getLineListItem(
|
||||
context,
|
||||
op: _operator,
|
||||
line: filteredLines[index],
|
||||
onTrainSelected: onTrainSelected
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
},
|
||||
childCount: filteredLines.length,
|
||||
addSemanticIndexes: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget getLineListItem(BuildContext context, {TrainOperatorLines op, _TrainOperatorTrainDescription line, TrainSelectedCallback onTrainSelected}) {
|
||||
if (Platform.isAndroid) {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
title: Text("${line.rang ?? ""} ${line.number ?? ""}"),
|
||||
subtitle: Text(op.operator ?? ""),
|
||||
onTap: () {
|
||||
onTrainSelected(line.internalNumber);
|
||||
},
|
||||
);
|
||||
}
|
||||
else if (Platform.isIOS) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTrainSelected(line.internalNumber);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 2, 16, 2),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
op.operator ?? "",
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text(
|
||||
"${line.rang ?? ""} ${line.number ?? ""}",
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoPromptMaterial extends StatefulWidget {
|
||||
@override
|
||||
_TrainInfoPromptMaterialState createState() => _TrainInfoPromptMaterialState();
|
||||
}
|
||||
|
||||
class _TrainInfoPromptMaterialState extends State<TrainInfoPromptMaterial> with TrainInfoPromptCommon, TrainInfoPromptListHandling {
|
||||
TextEditingController trainNoController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
loadOperators(context).then((_) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Informații despre tren"),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: TextField(
|
||||
controller: trainNoController,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: "Numărul trenului",
|
||||
),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
textInputAction: TextInputAction.search,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (_) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: getOperatorsListView(context, currentInput: trainNoController.text, onTrainSelected: (number) {
|
||||
onTrainSelected(context, number);
|
||||
})
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoPromptCupertino extends StatefulWidget {
|
||||
@override
|
||||
_TrainInfoPromptCupertinoState createState() => _TrainInfoPromptCupertinoState();
|
||||
}
|
||||
|
||||
class _TrainInfoPromptCupertinoState extends State<TrainInfoPromptCupertino> with TrainInfoPromptCommon, TrainInfoPromptListHandling {
|
||||
TextEditingController trainNoController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
loadOperators(context).then((_) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: Text("Informații despre tren"),
|
||||
),
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: CupertinoTextField(
|
||||
controller: trainNoController,
|
||||
autofocus: true,
|
||||
placeholder: "Numărul trenului",
|
||||
textInputAction: TextInputAction.search,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (_) {
|
||||
setState(() {});
|
||||
},
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: getOperatorsListView(
|
||||
context,
|
||||
currentInput: trainNoController.text, onTrainSelected: (number) {
|
||||
onTrainSelected(context, number);
|
||||
}
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class TrainOperatorLines {
|
||||
@JsonKey(name: "short_name")
|
||||
final String shortName;
|
||||
final String operator;
|
||||
@JsonKey(name: "versiune")
|
||||
final String version;
|
||||
@JsonKey(name: "trenuri")
|
||||
final List<_TrainOperatorTrainDescription> trains;
|
||||
|
||||
TrainOperatorLines({
|
||||
this.operator,
|
||||
this.shortName = "",
|
||||
this.version,
|
||||
this.trains,
|
||||
});
|
||||
|
||||
factory TrainOperatorLines.fromJson(Map<String, dynamic> json) => _$TrainOperatorLinesFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$TrainOperatorLinesToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class _TrainOperatorTrainDescription {
|
||||
final String rang;
|
||||
@JsonKey(name: "numar")
|
||||
final String number;
|
||||
@JsonKey(name: "numar_intern")
|
||||
final int internalNumber;
|
||||
|
||||
_TrainOperatorTrainDescription({
|
||||
this.number,
|
||||
this.rang,
|
||||
this.internalNumber
|
||||
});
|
||||
|
||||
factory _TrainOperatorTrainDescription.fromJson(Map<String, dynamic> json) => _$_TrainOperatorTrainDescriptionFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$_TrainOperatorTrainDescriptionToJson(this);
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'train_info_prompt.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
TrainOperatorLines _$TrainOperatorLinesFromJson(Map<String, dynamic> json) {
|
||||
return TrainOperatorLines(
|
||||
operator: json['operator'] as String,
|
||||
shortName: json['short_name'] as String,
|
||||
version: json['versiune'] as String,
|
||||
trains: (json['trenuri'] as List)
|
||||
?.map((e) => e == null
|
||||
? null
|
||||
: _TrainOperatorTrainDescription.fromJson(
|
||||
e as Map<String, dynamic>))
|
||||
?.toList());
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$TrainOperatorLinesToJson(TrainOperatorLines instance) =>
|
||||
<String, dynamic>{
|
||||
'short_name': instance.shortName,
|
||||
'operator': instance.operator,
|
||||
'versiune': instance.version,
|
||||
'trenuri': instance.trains
|
||||
};
|
||||
|
||||
_TrainOperatorTrainDescription _$_TrainOperatorTrainDescriptionFromJson(
|
||||
Map<String, dynamic> json) {
|
||||
return _TrainOperatorTrainDescription(
|
||||
number: json['numar'] as String,
|
||||
rang: json['rang'] as String,
|
||||
internalNumber: json['numar_intern'] as int);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$_TrainOperatorTrainDescriptionToJson(
|
||||
_TrainOperatorTrainDescription instance) =>
|
||||
<String, dynamic>{
|
||||
'rang': instance.rang,
|
||||
'numar': instance.number,
|
||||
'numar_intern': instance.internalNumber
|
||||
};
|
12
lib/utils/default_ui_design.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:info_tren/models/ui_design.dart';
|
||||
|
||||
UiDesign get defaultUiDesign {
|
||||
if (Platform.isIOS) {
|
||||
return UiDesign.CUPERTINO;
|
||||
}
|
||||
else {
|
||||
return UiDesign.MATERIAL;
|
||||
}
|
||||
}
|
12
lib/utils/state_to_string.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:info_tren/models/train_data.dart';
|
||||
|
||||
String stateToString(State state) {
|
||||
switch(state) {
|
||||
case State.PASSING:
|
||||
return 'trecere fără oprire';
|
||||
case State.ARRIVAL:
|
||||
return 'sosire';
|
||||
case State.DEPARTURE:
|
||||
return 'plecare';
|
||||
}
|
||||
}
|
12
lib/utils/string.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
extension TakeWhile on String {
|
||||
String takeWhile(Function charValidator) {
|
||||
StringBuffer output = StringBuffer();
|
||||
|
||||
for (final char in this.codeUnits) {
|
||||
if (charValidator(char)) output.writeCharCode(char);
|
||||
else break;
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
/// Evaluates a JavaScript function on the given WebView.
|
||||
///
|
||||
/// The JavaScript function must return a String.
|
||||
///
|
||||
/// On Android, the `String` resulted from the evaluation
|
||||
/// is JSON parsed. On iOS, the `String` is returned as is.
|
||||
///
|
||||
/// Other platforms are not supported. The returned value
|
||||
/// in this case will be `null`.
|
||||
Future<String> wInvoke({
|
||||
@required WebViewController webViewController,
|
||||
@required String jsFunctionContent,
|
||||
bool isFunctionAlready = false
|
||||
}) async {
|
||||
final actualJS = isFunctionAlready ?
|
||||
jsFunctionContent :
|
||||
"""
|
||||
(() => {
|
||||
$jsFunctionContent
|
||||
})()
|
||||
""";
|
||||
|
||||
final res = await webViewController.evaluateJavascript(actualJS);
|
||||
|
||||
if (Platform.isAndroid) return JsonDecoder().convert(res) as String;
|
||||
else if (Platform.isIOS) return res;
|
||||
else return null;
|
||||
}
|
196
pubspec.lock
|
@ -1,20 +1,27 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "24.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.36.4"
|
||||
version: "2.1.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.2"
|
||||
version: "2.2.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -22,69 +29,62 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.6.1"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.5"
|
||||
version: "2.1.0"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
version: "1.0.0"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "3.0.0"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
version: "2.0.4"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.1"
|
||||
version: "2.1.1"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
version: "7.1.0"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.2"
|
||||
version: "5.1.0"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.7.0"
|
||||
version: "8.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -99,20 +99,27 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
clock:
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
name: checked_yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
version: "2.0.1"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
version: "4.1.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -126,21 +133,14 @@ packages:
|
|||
name: convert
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "3.0.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.16.1"
|
||||
version: "3.0.1"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -154,122 +154,110 @@ packages:
|
|||
name: dart_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.9"
|
||||
fake_async:
|
||||
version: "2.0.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
name: file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "6.1.2"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.10.9"
|
||||
version: "1.0.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
front_end:
|
||||
dependency: transitive
|
||||
flutter_redux:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: front_end
|
||||
name: flutter_redux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.19"
|
||||
version: "0.8.2"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.7"
|
||||
version: "2.0.1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.14.0+2"
|
||||
version: "2.0.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.0+2"
|
||||
version: "0.13.3"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "3.0.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
version: "4.0.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
version: "1.0.3"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.1+1"
|
||||
version: "0.6.3"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "4.1.0"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
kernel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: kernel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.19"
|
||||
version: "5.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.11.3+2"
|
||||
version: "1.0.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -283,28 +271,21 @@ packages:
|
|||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.7.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.6+3"
|
||||
version: "1.0.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
package_resolver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_resolver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.10"
|
||||
version: "2.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -318,35 +299,42 @@ packages:
|
|||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
version: "1.11.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.5.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
version: "2.0.0"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.4"
|
||||
version: "1.0.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
version: "3.0.1"
|
||||
redux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: redux
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
rxdart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -360,14 +348,14 @@ packages:
|
|||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.5"
|
||||
version: "1.2.0"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.3"
|
||||
version: "1.0.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -379,7 +367,14 @@ packages:
|
|||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.4+2"
|
||||
version: "1.1.0"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_helper
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -407,7 +402,7 @@ packages:
|
|||
name: stream_transform
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.19"
|
||||
version: "2.0.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -422,27 +417,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1+1"
|
||||
version: "1.0.0"
|
||||
tuple:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: tuple
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
version: "2.0.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -463,28 +451,20 @@ packages:
|
|||
name: watcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.7+12"
|
||||
version: "1.0.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.14"
|
||||
webview_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_flutter
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.11+2"
|
||||
version: "2.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.16"
|
||||
version: "3.1.0"
|
||||
sdks:
|
||||
dart: ">=2.12.0 <3.0.0"
|
||||
flutter: ">=1.5.0"
|
||||
|
|
31
pubspec.yaml
|
@ -1,5 +1,5 @@
|
|||
name: info_tren
|
||||
description: O aplicație de vizualizare a datelor puse la dispoziție de Informatica Feroviară.xe
|
||||
description: O aplicație de vizualizare a datelor puse la dispoziție de Informatica Feroviară.
|
||||
|
||||
# The following defines the version and build number for your application.
|
||||
# A version number is three numbers separated by dots, like 1.2.43
|
||||
|
@ -14,7 +14,7 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform
|
|||
version: 2.0.6
|
||||
|
||||
environment:
|
||||
sdk: ">=2.3.0 <3.0.0"
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
@ -23,18 +23,17 @@ dependencies:
|
|||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
# cupertino_icons: ^0.1.2
|
||||
json_annotation: ^2.0.0
|
||||
rxdart: ^0.22.0
|
||||
http: ^0.12.0
|
||||
webview_flutter: ^0.3.0
|
||||
http: ^0.13.0
|
||||
cupertino_icons: ^0.1.2
|
||||
tuple: ^1.0.2
|
||||
tuple: ^2.0.0
|
||||
flutter_redux: ^0.8.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
build_runner: ^1.0.0
|
||||
json_serializable: ^3.0.0
|
||||
# flutter_test:
|
||||
# sdk: flutter
|
||||
build_runner: ^2.1.0
|
||||
json_serializable: ^5.0.0
|
||||
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
|
@ -64,7 +63,17 @@ flutter:
|
|||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
fonts:
|
||||
- family: Atkinson Hyperlegible
|
||||
fonts:
|
||||
- asset: fonts/ah/ah-Regular.ttf
|
||||
- asset: fonts/ah/ah-Italic.ttf
|
||||
style: italic
|
||||
- asset: fonts/ah/ah-Bold.ttf
|
||||
weight: 700
|
||||
- asset: fonts/ah/ah-BoldItalic.ttf
|
||||
weight: 700
|
||||
style: italic
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
|
|
BIN
web/favicon.png
Normal file
After Width: | Height: | Size: 917 B |
BIN
web/icons/Icon-192.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
web/icons/Icon-512.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
web/icons/Icon-maskable-192.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
web/icons/Icon-maskable-512.png
Normal file
After Width: | Height: | Size: 20 KiB |
101
web/index.html
Normal file
|
@ -0,0 +1,101 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
|
||||
The path provided below has to start and end with a slash "/" in order for
|
||||
it to work correctly.
|
||||
|
||||
For more details:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
|
||||
This is a placeholder for base href that will be replaced by the value of
|
||||
the `--base-href` argument provided to `flutter build`.
|
||||
-->
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="info_tren">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<title>info_tren</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<!-- This script installs service_worker.js to provide PWA functionality to
|
||||
application. For more information, see:
|
||||
https://developers.google.com/web/fundamentals/primers/service-workers -->
|
||||
<script>
|
||||
var serviceWorkerVersion = null;
|
||||
var scriptLoaded = false;
|
||||
function loadMainDartJs() {
|
||||
if (scriptLoaded) {
|
||||
return;
|
||||
}
|
||||
scriptLoaded = true;
|
||||
var scriptTag = document.createElement('script');
|
||||
scriptTag.src = 'main.dart.js';
|
||||
scriptTag.type = 'application/javascript';
|
||||
document.body.append(scriptTag);
|
||||
}
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
// Service workers are supported. Use them.
|
||||
window.addEventListener('load', function () {
|
||||
// Wait for registration to finish before dropping the <script> tag.
|
||||
// Otherwise, the browser will load the script multiple times,
|
||||
// potentially different versions.
|
||||
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
|
||||
navigator.serviceWorker.register(serviceWorkerUrl)
|
||||
.then((reg) => {
|
||||
function waitForActivation(serviceWorker) {
|
||||
serviceWorker.addEventListener('statechange', () => {
|
||||
if (serviceWorker.state == 'activated') {
|
||||
console.log('Installed new service worker.');
|
||||
loadMainDartJs();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!reg.active && (reg.installing || reg.waiting)) {
|
||||
// No active web worker and we have installed or are installing
|
||||
// one for the first time. Simply wait for it to activate.
|
||||
waitForActivation(reg.installing || reg.waiting);
|
||||
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
|
||||
// When the app updates the serviceWorkerVersion changes, so we
|
||||
// need to ask the service worker to update.
|
||||
console.log('New service worker available.');
|
||||
reg.update();
|
||||
waitForActivation(reg.installing);
|
||||
} else {
|
||||
// Existing service worker is still good.
|
||||
console.log('Loading app from service worker.');
|
||||
loadMainDartJs();
|
||||
}
|
||||
});
|
||||
|
||||
// If service worker doesn't succeed in a reasonable amount of time,
|
||||
// fallback to plaint <script> tag.
|
||||
setTimeout(() => {
|
||||
if (!scriptLoaded) {
|
||||
console.warn(
|
||||
'Failed to load app from service worker. Falling back to plain <script> tag.',
|
||||
);
|
||||
loadMainDartJs();
|
||||
}
|
||||
}, 4000);
|
||||
});
|
||||
} else {
|
||||
// Service workers not supported. Just drop the <script> tag.
|
||||
loadMainDartJs();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
35
web/manifest.json
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"name": "info_tren",
|
||||
"short_name": "info_tren",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#0175C2",
|
||||
"theme_color": "#0175C2",
|
||||
"description": "A new Flutter project.",
|
||||
"orientation": "portrait-primary",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/Icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|