Transition WebView to API #1

Merged
kbruen merged 3 commits from transition_webview_to_api into master 3 years ago
  1. 5
      CHANGELOG.TXT
  2. 29
      analysis_options.yaml
  3. 13
      android/.gitignore
  4. 2
      android/app/build.gradle
  5. 6
      android/app/src/main/kotlin/xyz/dcdevelop/info_tren/MainActivity.kt
  6. 12
      android/app/src/main/res/drawable-v21/launch_background.xml
  7. 18
      android/app/src/main/res/values-night/styles.xml
  8. BIN
      fonts/ah/ah-Bold.ttf
  9. BIN
      fonts/ah/ah-BoldItalic.ttf
  10. BIN
      fonts/ah/ah-Italic.ttf
  11. BIN
      fonts/ah/ah-Regular.ttf
  12. 33
      ios/.gitignore
  13. 4
      ios/Flutter/AppFrameworkInfo.plist
  14. 1
      ios/Flutter/Debug.xcconfig
  15. 18
      ios/Flutter/Flutter.podspec
  16. 1
      ios/Flutter/Release.xcconfig
  17. 1
      ios/Flutter/flutter_export_environment.sh
  18. 41
      ios/Podfile
  19. 22
      ios/Podfile.lock
  20. 165
      ios/Runner.xcodeproj/project.pbxproj
  21. 8
      ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  22. 8
      ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  23. 3
      ios/Runner.xcworkspace/contents.xcworkspacedata
  24. 8
      ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  25. 2
      ios/Runner/AppDelegate.swift
  26. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
  27. 19
      ios/Runner/Info.plist
  28. 9
      lib/api/train_data.dart
  29. 60
      lib/components/cupertino_divider.dart
  30. 42
      lib/components/future_display.dart
  31. 31
      lib/components/loading/loading.dart
  32. 28
      lib/components/loading/loading_cupertino.dart
  33. 26
      lib/components/loading/loading_material.dart
  34. 117
      lib/components/refresh_future_builder.dart
  35. 209
      lib/components/select_train_suggestions/select_train_suggestions.dart
  36. 74
      lib/components/select_train_suggestions/select_train_suggestions_cupertino.dart
  37. 47
      lib/components/select_train_suggestions/select_train_suggestions_material.dart
  38. 62
      lib/components/slim_app_bar.dart
  39. 23
      lib/hidden_webview.dart
  40. 226
      lib/main.dart
  41. 1238
      lib/models/train_data.dart
  42. 98
      lib/models/train_data.g.dart
  43. 42
      lib/models/train_operator_lines.dart
  44. 42
      lib/models/train_operator_lines.g.dart
  45. 15
      lib/models/ui_design.dart
  46. 68
      lib/pages/main/main_page.dart
  47. 30
      lib/pages/main/main_page_cupertino.dart
  48. 34
      lib/pages/main/main_page_material.dart
  49. 61
      lib/pages/train_info_page/select_train/select_train.dart
  50. 39
      lib/pages/train_info_page/select_train/select_train_cupertino.dart
  51. 43
      lib/pages/train_info_page/select_train/select_train_material.dart
  52. 2
      lib/pages/train_info_page/train_info_animation_helpers.dart.old
  53. 0
      lib/pages/train_info_page/train_info_constants.dart
  54. 69
      lib/pages/train_info_page/view_train/train_info.dart
  55. 741
      lib/pages/train_info_page/view_train/train_info_cupertino.dart
  56. 466
      lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart
  57. 659
      lib/pages/train_info_page/view_train/train_info_material.dart
  58. 476
      lib/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart
  59. 95
      lib/stations_list.dart.old
  60. 147
      lib/train_info_display.dart
  61. 72
      lib/train_info_page/train_info.dart
  62. 1022
      lib/train_info_page/train_info_cupertino.dart
  63. 506
      lib/train_info_page/train_info_cupertino_DisplayTrainStation.dart
  64. 944
      lib/train_info_page/train_info_material.dart
  65. 509
      lib/train_info_page/train_info_material_DisplayTrainStation.dart
  66. 385
      lib/train_info_page/train_info_prompt.dart
  67. 44
      lib/train_info_page/train_info_prompt.g.dart
  68. 12
      lib/utils/default_ui_design.dart
  69. 12
      lib/utils/state_to_string.dart
  70. 12
      lib/utils/string.dart
  71. 34
      lib/utils/webview_invoke.dart
  72. 194
      pubspec.lock
  73. 31
      pubspec.yaml
  74. BIN
      web/favicon.png
  75. BIN
      web/icons/Icon-192.png
  76. BIN
      web/icons/Icon-512.png
  77. BIN
      web/icons/Icon-maskable-192.png
  78. BIN
      web/icons/Icon-maskable-512.png
  79. 101
      web/index.html
  80. 35
      web/manifest.json

5
CHANGELOG.TXT

@ -1,3 +1,8 @@
v2.0.7
Switched from WebView to API
Updated app to latest Flutter
Tweaks
v2.0.6 v2.0.6
Brought feature parity with iOS _(except for v2.0.2, which is iOS specific)_. Brought feature parity with iOS _(except for v2.0.2, which is iOS specific)_.

29
analysis_options.yaml

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

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

2
android/app/build.gradle

@ -38,7 +38,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "ml.dandevelop.info_tren" applicationId "xyz.dcdevelop.infotren"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 28
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()

6
android/app/src/main/kotlin/xyz/dcdevelop/info_tren/MainActivity.kt

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

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

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

Binary file not shown.

BIN
fonts/ah/ah-BoldItalic.ttf

Binary file not shown.

BIN
fonts/ah/ah-Italic.ttf

Binary file not shown.

BIN
fonts/ah/ah-Regular.ttf

Binary file not shown.

33
ios/.gitignore vendored

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

4
ios/Flutter/AppFrameworkInfo.plist

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

1
ios/Flutter/Debug.xcconfig

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

18
ios/Flutter/Flutter.podspec

@ -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
ios/Flutter/Release.xcconfig

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

1
ios/Flutter/flutter_export_environment.sh

@ -5,7 +5,6 @@ export "FLUTTER_APPLICATION_PATH=/Users/dan.cojocaru/info_tren"
export "COCOAPODS_PARALLEL_CODE_SIGN=true" export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=/Users/dan.cojocaru/info_tren/lib/main.dart" export "FLUTTER_TARGET=/Users/dan.cojocaru/info_tren/lib/main.dart"
export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "FLUTTER_BUILD_NAME=2.0.6" export "FLUTTER_BUILD_NAME=2.0.6"
export "FLUTTER_BUILD_NUMBER=2.0.6" export "FLUTTER_BUILD_NUMBER=2.0.6"
export "DART_DEFINES=Zmx1dHRlci5pbnNwZWN0b3Iuc3RydWN0dXJlZEVycm9ycz10cnVl,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==" 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

22
ios/Podfile.lock

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

165
ios/Runner.xcodeproj/project.pbxproj

@ -3,15 +3,13 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 46; objectVersion = 50;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
722F441253D3B79676E4DE80 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F72320B12B1F4015789BBC8E /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 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 */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@ -33,12 +31,9 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
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>"; }; 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>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
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>"; }; 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>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
@ -47,7 +42,6 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F72320B12B1F4015789BBC8E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -55,21 +49,12 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
722F441253D3B79676E4DE80 /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
0B24EBF53F1DCC708FA961FD /* Frameworks */ = {
isa = PBXGroup;
children = (
F72320B12B1F4015789BBC8E /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -87,8 +72,6 @@
9740EEB11CF90186004384FC /* Flutter */, 9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */, 97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */, 97C146EF1CF9000F007C117D /* Products */,
A2E7A2EB20EFBBAC4AB0299B /* Pods */,
0B24EBF53F1DCC708FA961FD /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -107,7 +90,6 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */, 97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
@ -116,23 +98,6 @@
path = Runner; path = Runner;
sourceTree = "<group>"; 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 */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -140,14 +105,12 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = ( buildPhases = (
0D525F98970BF5A8EFFD825C /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */, 9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */, 97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */, 97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */, 97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
1B7EDCF8AB293318D8391906 /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
@ -165,18 +128,16 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1020; LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "The Chromium Authors"; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1; CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = NF9A3KMT8Q; LastSwiftMigration = 1100;
LastSwiftMigration = 0910;
ProvisioningStyle = Automatic;
}; };
}; };
}; };
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 9.3";
developmentRegion = en; developmentRegion = en;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
@ -200,7 +161,6 @@
files = ( files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
); );
@ -209,46 +169,6 @@
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase 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 */ = { 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -352,9 +272,10 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
@ -365,27 +286,19 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer"; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 2.0.7;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = NF9A3KMT8Q;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = (
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 4.0; SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Profile; name = Profile;
@ -437,7 +350,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -486,10 +399,12 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
@ -501,29 +416,19 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer"; CURRENT_PROJECT_VERSION = 2.0.7;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = NF9A3KMT8Q;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = (
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = On; SWIFT_VERSION = 5.0;
SWIFT_VERSION = 4.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
@ -534,28 +439,18 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer"; CURRENT_PROJECT_VERSION = 2.0.7;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = NF9A3KMT8Q;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = (
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(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)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = On; SWIFT_VERSION = 5.0;
SWIFT_VERSION = 4.0;
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;

8
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

8
ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

@ -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 <FileRef
location = "group:Runner.xcodeproj"> location = "group:Runner.xcodeproj">
</FileRef> </FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace> </Workspace>

8
ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

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

2
ios/Runner/AppDelegate.swift

@ -5,7 +5,7 @@ import Flutter
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
override func application( override func application(
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool { ) -> Bool {
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)

BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

19
ios/Runner/Info.plist

@ -17,24 +17,13 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string> <string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <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> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
@ -42,6 +31,8 @@
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
@ -52,7 +43,5 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>io.flutter.embedded_views_preview</key>
<true/>
</dict> </dict>
</plist> </plist>

9
lib/api/train_data.dart

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

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

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

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

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

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

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

209
lib/components/select_train_suggestions/select_train_suggestions.dart

@ -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);
}

74
lib/components/select_train_suggestions/select_train_suggestions_cupertino.dart

@ -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,
),
],
),
),
),
);
}
}

47
lib/components/select_train_suggestions/select_train_suggestions_material.dart

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

@ -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,
),
],
),
),
),
);
}
}

23
lib/hidden_webview.dart

@ -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/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:info_tren/models/train_data.dart'; // import 'package:flutter_redux/flutter_redux.dart';
import 'package:info_tren/train_info_page/train_info.dart'; import 'package:info_tren/models/ui_design.dart';
import 'package:info_tren/train_info_page/train_info_cupertino.dart'; import 'package:info_tren/pages/main/main_page.dart';
import 'package:info_tren/train_info_page/train_info_material.dart'; import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/train_info_page/train_info_prompt.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 { class StartPoint extends StatelessWidget {
final String appTitle = 'Info Tren';
@override @override
Widget build(BuildContext context) { 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( return MaterialApp(
title: 'Info Tren', title: appTitle,
theme: ThemeData( theme: ThemeData(
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
brightness: Brightness.dark, brightness: Brightness.dark,
primaryColor: Colors.blue.shade600, primaryColor: Colors.blue.shade600,
accentColor: Colors.blue.shade700, accentColor: Colors.blue.shade700,
// fontFamily: 'Atkinson Hyperlegible',
), ),
// home: MainPageMaterial(), routes: routesByUiDesign(UiDesign.MATERIAL),
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,
);
},
);
},
},
); );
} }
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(),
),
),
),
);
} }
} }

1238
lib/models/train_data.dart

File diff suppressed because it is too large Load Diff

98
lib/models/train_data.g.dart

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

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

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

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

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

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

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

@ -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),
);
}

39
lib/pages/train_info_page/select_train/select_train_cupertino.dart

@ -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,
),
],
),
),
);
}
}

43
lib/pages/train_info_page/select_train/select_train_material.dart

@ -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,
),
],
),
),
);
}
}

2
lib/train_info_page/train_info_animation_helpers.dart → lib/pages/train_info_page/train_info_animation_helpers.dart.old

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.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; import 'dart:io' show Platform;

0
lib/train_info_page/train_info_constants.dart → lib/pages/train_info_page/train_info_constants.dart

69
lib/pages/train_info_page/view_train/train_info.dart

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

@ -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,
),
);
}
}

466
lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart

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

@ -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,
),
);
}
}

476
lib/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart

@ -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();
}
}

95
lib/stations_list.dart → lib/stations_list.dart.old

@ -40,7 +40,7 @@ enum NotchStyle {
} }
class StopListLine extends StatelessWidget { class StopListLine extends StatelessWidget {
final StationEntry station; final Station station;
final int width; final int width;
StopListLine(this.station, {this.width = 32}) : assert(width.isEven); StopListLine(this.station, {this.width = 32}) : assert(width.isEven);
@ -151,7 +151,7 @@ class StopListLinePainter extends CustomPainter {
} }
class StopOnLineDetails extends StatelessWidget { class StopOnLineDetails extends StatelessWidget {
final StationEntry station; final Station station;
StopOnLineDetails(this.station); StopOnLineDetails(this.station);
@override @override
@ -172,11 +172,11 @@ class StopOnLineDetails extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
if (station.observations == "ONI") // if (station.observations == "ONI")
Padding( // Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.5), // 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),), // child: Text("oprire ne-itinerarică", style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),),
), // ),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(8.0, 0.5, 8.0, 8.0), padding: const EdgeInsets.fromLTRB(8.0, 0.5, 8.0, 8.0),
child: Text( child: Text(
@ -186,11 +186,12 @@ class StopOnLineDetails extends StatelessWidget {
), ),
), ),
StopOnLineTimeDetails(station), StopOnLineTimeDetails(station),
if (station.real) // TODO: Figure out how to display delay info
Padding( // if (station.arrival != null && station.arrival.status != null || station.departure != null && station.departure.status != null)
padding: const EdgeInsets.all(2.0), // Padding(
child: StopOnLineDelayDetails(station), // padding: const EdgeInsets.all(2.0),
), // child: StopOnLineDelayDetails(station),
// ),
Divider( Divider(
height: 0, height: 0,
), ),
@ -201,14 +202,14 @@ class StopOnLineDetails extends StatelessWidget {
} }
class StopOnLineTimeDetails extends StatelessWidget { class StopOnLineTimeDetails extends StatelessWidget {
final StationEntry station; final Station station;
StopOnLineTimeDetails(this.station); StopOnLineTimeDetails(this.station);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
children: <Widget>[ children: <Widget>[
if (station.arrivalTime.isNotEmpty) if (station.arrival != null)
Padding( Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: Align( child: Align(
@ -221,14 +222,14 @@ class StopOnLineTimeDetails extends StatelessWidget {
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
Text( Text(
station.arrivalTime, station.arrival.scheduleTime,
textAlign: TextAlign.left, textAlign: TextAlign.left,
) )
], ],
), ),
), ),
), ),
if (station.waitTime.isNotEmpty) if (station.stoppingTime != null)
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
@ -242,7 +243,7 @@ class StopOnLineTimeDetails extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Text( Text(
"${station.waitTime} ${station.waitTime == "1" ? "minut" : "minute"}", "${station.stoppingTime} ${station.stoppingTime == 1 ? "minut" : "minute"}",
textAlign: TextAlign.center, textAlign: TextAlign.center,
) )
], ],
@ -252,7 +253,7 @@ class StopOnLineTimeDetails extends StatelessWidget {
) )
else else
Expanded(child: Container(),), Expanded(child: Container(),),
if (station.departureTime.isNotEmpty) if (station.departure != null)
Padding( Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: Align( child: Align(
@ -265,7 +266,7 @@ class StopOnLineTimeDetails extends StatelessWidget {
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),
Text( Text(
station.departureTime, station.departure.scheduleTime,
textAlign: TextAlign.right, textAlign: TextAlign.right,
) )
], ],
@ -277,32 +278,32 @@ class StopOnLineTimeDetails extends StatelessWidget {
} }
} }
class StopOnLineDelayDetails extends StatelessWidget { // class StopOnLineDelayDetails extends StatelessWidget {
final StationEntry station; // final Station station;
StopOnLineDelayDetails(this.station); // StopOnLineDelayDetails(this.station);
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
if (station.delay == 0) { // if (station.delay == 0) {
return Text( // return Text(
"Fără întârziere", // "Fără întârziere",
style: Theme.of(context).textTheme.caption, // style: Theme.of(context).textTheme.caption,
textAlign: TextAlign.center, // textAlign: TextAlign.center,
); // );
} // }
else if (station.delay < 0) { // else if (station.delay < 0) {
return Text( // return Text(
"${-(station.delay)} minute mai devreme", // "${-(station.delay)} minute mai devreme",
style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.green.shade700), // style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.green.shade700),
textAlign: TextAlign.center, // textAlign: TextAlign.center,
); // );
} // }
else { // else {
return Text( // return Text(
"${station.delay} minute întârziere", // "${station.delay} minute întârziere",
style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700), // style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),
textAlign: TextAlign.center, // textAlign: TextAlign.center,
); // );
} // }
} // }
} // }

147
lib/train_info_display.dart

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; 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'; import 'models/train_data.dart';
@ -29,33 +29,30 @@ class TrainInfoDisplayData extends StatelessWidget {
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: TotalDetails(trainData), child: TotalDetails(trainData),
), ),
if (trainData.destination.station.isNotEmpty) CustomDivider(),
...[ Padding(
CustomDivider(), padding: const EdgeInsets.all(4.0),
Padding( child: Destination(trainData),
padding: const EdgeInsets.all(4.0), ),
child: Destination(trainData),
),
],
CustomDivider(), CustomDivider(),
Padding( Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: LastUpdate(trainData), child: LastUpdate(trainData),
), ),
if (trainData.nextStop.station.isNotEmpty) // if (trainData.nextStop.station.isNotEmpty)
...[ // ...[
CustomDivider(), // CustomDivider(),
Padding( // Padding(
padding: const EdgeInsets.all(4.0), // padding: const EdgeInsets.all(4.0),
child: NextStop(trainData), // child: NextStop(trainData),
), // ),
], // ],
CustomDivider(), CustomDivider(),
Padding( Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: TrainStatus(trainData), child: TrainStatus(trainData),
), ),
Divider(color: Theme.of(context).accentColor,), Divider(color: Theme.of(context).colorScheme.secondary,),
Padding( Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: StationsList(trainData), child: StationsList(trainData),
@ -81,7 +78,7 @@ class TrainName extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Text( return Text(
"${trainData.rang} ${trainData.trainNumber}", "${trainData.rank} ${trainData.number}",
style: Theme.of(context).textTheme.headline3, style: Theme.of(context).textTheme.headline3,
); );
} }
@ -98,20 +95,20 @@ class TrainRoute extends StatelessWidget {
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
"${trainData.route.split("-")[0]}", trainData.route.from,
style: Theme.of(context).textTheme.bodyText1.copyWith(fontStyle: FontStyle.italic), style: Theme.of(context).textTheme.bodyText1?.copyWith(fontStyle: FontStyle.italic),
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
), ),
Text( Text(
"-", "-",
style: Theme.of(context).textTheme.bodyText1.copyWith(fontStyle: FontStyle.italic), style: Theme.of(context).textTheme.bodyText1?.copyWith(fontStyle: FontStyle.italic),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Expanded( Expanded(
child: Text( child: Text(
"${trainData.route.split("-")[1]}", trainData.route.to,
style: Theme.of(context).textTheme.bodyText1.copyWith(fontStyle: FontStyle.italic), style: Theme.of(context).textTheme.bodyText1?.copyWith(fontStyle: FontStyle.italic),
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),
), ),
@ -141,7 +138,7 @@ class TrainStatus extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Text( return Text(
trainData.state, trainData.status.toString(),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline5, style: Theme.of(context).textTheme.headline5,
); );
@ -154,16 +151,16 @@ class Destination extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (trainData.destination.station.isEmpty) return Container(); final destinationStation = trainData.stations.last;
return Column( return Column(
children: <Widget>[ children: <Widget>[
Text( Text(
"Destinația: ${trainData.destination.station}", "Destinația: ${destinationStation.name}",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Text( Text(
"Sosește în ${trainData.destination.dateAndTime.split(" ")[0]} la ${trainData.destination.dateAndTime.split(" ")[1]}", "Sosește la ${destinationStation.arrival!.scheduleTime}",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@ -177,6 +174,9 @@ class LastUpdate extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (trainData.status == null) {
return Container();
}
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
@ -188,62 +188,63 @@ class LastUpdate extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Padding( Padding(
padding: const EdgeInsets.all(2.0), 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(),), Expanded(child: Container(),),
Padding( Padding(
padding: const EdgeInsets.all(2.0), 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(
padding: const EdgeInsets.all(2.0), 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,) ? Text("Fără întârziere", style: Theme.of(context).textTheme.caption,)
: trainData.lastInfo.delay > 0 : trainData.status!.delay > 0
? Text("${trainData.lastInfo.delay} minute întârziere", style: Theme.of(context).textTheme.bodyText2.copyWith(color: Colors.red.shade700),) ? Text("${trainData.status!.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),) : Text("${-(trainData.status!.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}"),
), ),
// TODO: Implement status report time detection
// Padding(
// padding: const EdgeInsets.all(2.0),
// child: Text("Raportat la ${trainData.lastInfo.dateAndTime}"),
// ),
], ],
); );
} }
} }
class NextStop extends StatelessWidget { // class NextStop extends StatelessWidget {
final TrainData trainData; // final TrainData trainData;
NextStop(this.trainData); // NextStop(this.trainData);
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
return Column( // return Column(
mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: <Widget>[ // children: <Widget>[
Padding( // Padding(
padding: const EdgeInsets.all(2.0), // padding: const EdgeInsets.all(2.0),
child: Text("Următoarea oprire", style: Theme.of(context).textTheme.headline5,), // child: Text("Următoarea oprire", style: Theme.of(context).textTheme.headline5,),
), // ),
Row( // Row(
children: <Widget>[ // children: <Widget>[
Padding( // Padding(
padding: const EdgeInsets.all(2.0), // padding: const EdgeInsets.all(2.0),
child: Text(trainData.nextStop.station, textAlign: TextAlign.left,), // child: Text(trainData.nextStop.station, textAlign: TextAlign.left,),
), // ),
Expanded(child: Container(),), // Expanded(child: Container(),),
Padding( // Padding(
padding: const EdgeInsets.all(2.0), // padding: const EdgeInsets.all(2.0),
child: Text(trainData.nextStop.dateAndTime, textAlign: TextAlign.right,), // child: Text(trainData.nextStop.dateAndTime, textAlign: TextAlign.right,),
) // )
], // ],
), // ),
], // ],
); // );
} // }
} // }
class TotalDetails extends StatelessWidget { class TotalDetails extends StatelessWidget {
final TrainData trainData; final TrainData trainData;
@ -254,18 +255,18 @@ class TotalDetails extends StatelessWidget {
return Row( return Row(
children: <Widget>[ children: <Widget>[
Text( Text(
trainData.distance, '${trainData.stations.last.km} km',
style: Theme.of(context).textTheme.caption, style: Theme.of(context).textTheme.caption,
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
Expanded( Expanded(
child: Container() child: Container()
), ),
Text( // Text(
trainData.tripLength, // trainData.tripLength,
style: Theme.of(context).textTheme.caption, // style: Theme.of(context).textTheme.caption,
textAlign: TextAlign.right, // textAlign: TextAlign.right,
) // )
], ],
); );
} }

72
lib/train_info_page/train_info.dart

@ -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,
);
},
);
}
}

1022
lib/train_info_page/train_info_cupertino.dart

File diff suppressed because it is too large Load Diff

506
lib/train_info_page/train_info_cupertino_DisplayTrainStation.dart

@ -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();
},
);
}
}

944
lib/train_info_page/train_info_material.dart

@ -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,
),
],
),
),
),
);
}
}

509
lib/train_info_page/train_info_material_DisplayTrainStation.dart

@ -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();
},
);
}
}

385
lib/train_info_page/train_info_prompt.dart

@ -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);
}

44
lib/train_info_page/train_info_prompt.g.dart

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

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

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

@ -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();
}
}

34
lib/utils/webview_invoke.dart

@ -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;
}

194
pubspec.lock

@ -1,20 +1,27 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "24.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.36.4" version: "2.1.0"
args: args:
dependency: transitive dependency: transitive
description: description:
name: args name: args
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.5.2" version: "2.2.0"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -22,69 +29,62 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.6.1" version: "2.6.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
build: build:
dependency: transitive dependency: transitive
description: description:
name: build name: build
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.5" version: "2.1.0"
build_config: build_config:
dependency: transitive dependency: transitive
description: description:
name: build_config name: build_config
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.0" version: "1.0.0"
build_daemon: build_daemon:
dependency: transitive dependency: transitive
description: description:
name: build_daemon name: build_daemon
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "3.0.0"
build_resolvers: build_resolvers:
dependency: transitive dependency: transitive
description: description:
name: build_resolvers name: build_resolvers
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.6" version: "2.0.4"
build_runner: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.6.1" version: "2.1.1"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
name: build_runner_core name: build_runner_core
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.6" version: "7.1.0"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
name: built_collection name: built_collection
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.2" version: "5.1.0"
built_value: built_value:
dependency: transitive dependency: transitive
description: description:
name: built_value name: built_value
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.7.0" version: "8.1.2"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -99,20 +99,27 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
clock: checked_yaml:
dependency: transitive dependency: transitive
description: description:
name: clock name: checked_yaml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted 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: code_builder:
dependency: transitive dependency: transitive
description: description:
name: code_builder name: code_builder
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.0" version: "4.1.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -126,21 +133,14 @@ packages:
name: convert name: convert
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "3.0.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
name: crypto name: crypto
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.6" version: "3.0.1"
csslib:
dependency: transitive
description:
name: csslib
url: "https://pub.dartlang.org"
source: hosted
version: "0.16.1"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -154,122 +154,110 @@ packages:
name: dart_style name: dart_style
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.9" version: "2.0.3"
fake_async: file:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: file
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "6.1.2"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
name: fixnum name: fixnum
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.10.9" version: "1.0.0"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_test: flutter_redux:
dependency: "direct dev" dependency: "direct main"
description: flutter description:
source: sdk name: flutter_redux
version: "0.0.0" url: "https://pub.dartlang.org"
front_end: source: hosted
version: "0.8.2"
frontend_server_client:
dependency: transitive dependency: transitive
description: description:
name: front_end name: frontend_server_client
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.19" version: "2.1.2"
glob: glob:
dependency: transitive dependency: transitive
description: description:
name: glob name: glob
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.7" version: "2.0.1"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
name: graphs name: graphs
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.0" version: "2.0.0"
html:
dependency: transitive
description:
name: html
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.0+2"
http: http:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.0+2" version: "0.13.3"
http_multi_server: http_multi_server:
dependency: transitive dependency: transitive
description: description:
name: http_multi_server name: http_multi_server
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "3.0.1"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
name: http_parser name: http_parser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.3" version: "4.0.0"
io: io:
dependency: transitive dependency: transitive
description: description:
name: io name: io
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.3" version: "1.0.3"
js: js:
dependency: transitive dependency: transitive
description: description:
name: js name: js
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.1+1" version: "0.6.3"
json_annotation: json_annotation:
dependency: "direct main" dependency: transitive
description: description:
name: json_annotation name: json_annotation
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.0" version: "4.1.0"
json_serializable: json_serializable:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: json_serializable name: json_serializable
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.0" version: "5.0.0"
kernel:
dependency: transitive
description:
name: kernel
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.19"
logging: logging:
dependency: transitive dependency: transitive
description: description:
name: logging name: logging
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.11.3+2" version: "1.0.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -283,28 +271,21 @@ packages:
name: meta name: meta
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.0" version: "1.7.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
name: mime name: mime
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.6+3" version: "1.0.0"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
name: package_config name: package_config
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.5" version: "2.0.0"
package_resolver:
dependency: transitive
description:
name: package_resolver
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.10"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -318,35 +299,42 @@ packages:
name: pedantic name: pedantic
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.11.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:
name: pool name: pool
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.0" version: "1.5.0"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
name: pub_semver name: pub_semver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.2" version: "2.0.0"
pubspec_parse: pubspec_parse:
dependency: transitive dependency: transitive
description: description:
name: pubspec_parse name: pubspec_parse
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.4" version: "1.0.0"
quiver: quiver:
dependency: transitive dependency: transitive
description: description:
name: quiver name: quiver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted 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: rxdart:
dependency: "direct main" dependency: "direct main"
description: description:
@ -360,14 +348,14 @@ packages:
name: shelf name: shelf
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.5" version: "1.2.0"
shelf_web_socket: shelf_web_socket:
dependency: transitive dependency: transitive
description: description:
name: shelf_web_socket name: shelf_web_socket
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.3" version: "1.0.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -379,7 +367,14 @@ packages:
name: source_gen name: source_gen
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted 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: source_span:
dependency: transitive dependency: transitive
description: description:
@ -407,7 +402,7 @@ packages:
name: stream_transform name: stream_transform
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.0.19" version: "2.0.0"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -422,27 +417,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
timing: timing:
dependency: transitive dependency: transitive
description: description:
name: timing name: timing
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.1+1" version: "1.0.0"
tuple: tuple:
dependency: "direct main" dependency: "direct main"
description: description:
name: tuple name: tuple
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.2" version: "2.0.0"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -463,28 +451,20 @@ packages:
name: watcher name: watcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.7+12" version: "1.0.0"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
name: web_socket_channel name: web_socket_channel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.14" version: "2.1.0"
webview_flutter:
dependency: "direct main"
description:
name: webview_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.11+2"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
name: yaml name: yaml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.16" version: "3.1.0"
sdks: sdks:
dart: ">=2.12.0 <3.0.0" dart: ">=2.12.0 <3.0.0"
flutter: ">=1.5.0"

31
pubspec.yaml

@ -1,5 +1,5 @@
name: info_tren 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. # The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43 # 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 version: 2.0.6
environment: environment:
sdk: ">=2.3.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"
dependencies: dependencies:
flutter: flutter:
@ -23,18 +23,17 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
# cupertino_icons: ^0.1.2 # cupertino_icons: ^0.1.2
json_annotation: ^2.0.0
rxdart: ^0.22.0 rxdart: ^0.22.0
http: ^0.12.0 http: ^0.13.0
webview_flutter: ^0.3.0
cupertino_icons: ^0.1.2 cupertino_icons: ^0.1.2
tuple: ^1.0.2 tuple: ^2.0.0
flutter_redux: ^0.8.2
dev_dependencies: dev_dependencies:
flutter_test: # flutter_test:
sdk: flutter # sdk: flutter
build_runner: ^1.0.0 build_runner: ^2.1.0
json_serializable: ^3.0.0 json_serializable: ^5.0.0
# For information on the generic Dart part of this file, see the # For information on the generic Dart part of this file, see the
@ -64,7 +63,17 @@ flutter:
# "family" key with the font family name, and a "fonts" key with a # "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For # list giving the asset and other descriptors for the font. For
# example: # 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 # - family: Schyler
# fonts: # fonts:
# - asset: fonts/Schyler-Regular.ttf # - asset: fonts/Schyler-Regular.ttf

BIN
web/favicon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

BIN
web/icons/Icon-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
web/icons/Icon-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
web/icons/Icon-maskable-192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
web/icons/Icon-maskable-512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

101
web/index.html

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

@ -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"
}
]
}
Loading…
Cancel
Save