Browse Source

Merge pull request #1 from dancojocaru2000/transition_webview_to_api

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

23
android/app/build.gradle

@ -26,24 +26,28 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion 28 compileSdkVersion 30
sourceSets { compileOptions {
main.java.srcDirs += 'src/main/kotlin' sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
} }
lintOptions { sourceSets {
disable 'InvalidPackage' main.java.srcDirs += 'src/main/kotlin'
} }
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.info_tren"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 28 targetSdkVersion 30
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@ -61,7 +65,4 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
} }

2
android/app/src/debug/AndroidManifest.xml

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ml.dandevelop.info_tren"> package="xyz.dcdevelop.info_tren">
<!-- Flutter needs it to communicate with the running application <!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->

44
android/app/src/main/AndroidManifest.xml

@ -1,38 +1,40 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ml.dandevelop.info_tren"> package="xyz.dcdevelop.infotren">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET"/> <application
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:label="Info Tren" android:label="Info Tren"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize">
android:screenOrientation="portrait"> <!-- Specifies an Android theme to apply to this Activity as soon as
<!-- Specify that the launch screen should continue being displayed --> the Android process has started. This theme is visible to the user
<!-- until Flutter renders its first frame. --> while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data <meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@drawable/launch_background" /> android:resource="@style/NormalTheme"
<!-- This keeps the window background of the activity showing />
until Flutter renders its first frame. It can be removed if <!-- Displays an Android View that continues showing the launch screen
there is no splash screen (such as the default splash screen Drawable until Flutter paints its first frame, then this splash
defined in @style/LaunchTheme). --> screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />

2
android/app/src/main/kotlin/ml/dandevelop/info_tren/MainActivity.kt → android/app/src/main/kotlin/xyz/dcdevelop/info_tren/MainActivity.kt

@ -1,4 +1,4 @@
package ml.dandevelop.info_tren package xyz.dcdevelop.info_tren
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.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>

12
android/app/src/main/res/values/styles.xml

@ -1,8 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame --> Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started.
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.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources> </resources>

2
android/app/src/profile/AndroidManifest.xml

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ml.dandevelop.info_tren"> package="xyz.dcdevelop.info_tren">
<!-- Flutter needs it to communicate with the running application <!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->

10
android/build.gradle

@ -1,12 +1,12 @@
buildscript { buildscript {
ext.kotlin_version = '1.2.71' ext.kotlin_version = '1.3.50'
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
@ -14,15 +14,13 @@ buildscript {
allprojects { allprojects {
repositories { repositories {
google() google()
jcenter() mavenCentral()
} }
} }
rootProject.buildDir = '../build' rootProject.buildDir = '../build'
subprojects { subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}" project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app') project.evaluationDependsOn(':app')
} }

3
android/gradle.properties

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

2
android/gradle/wrapper/gradle-wrapper.properties vendored

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip

18
android/settings.gradle

@ -1,15 +1,11 @@
include ':app' include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
def plugins = new Properties() assert localPropertiesFile.exists()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path -> def flutterSdkPath = properties.getProperty("flutter.sdk")
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
include ":$name" apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
project(":$name").projectDir = pluginDirectory
}

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>

2
ios/Flutter/Debug.xcconfig

@ -1,2 +1,2 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #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

2
ios/Flutter/Release.xcconfig

@ -1,2 +1,2 @@
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #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=="

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>

2
ios/Runner/Runner-Bridging-Header.h

@ -1 +1 @@
#import "GeneratedPluginRegistrant.h" #import "GeneratedPluginRegistrant.h"

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

198
pubspec.lock

@ -1,90 +1,90 @@
# 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:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.6.1" version: "2.8.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:
@ -98,21 +98,28 @@ packages:
name: charcode name: charcode
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.1"
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"

33
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
@ -11,10 +11,10 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 2.0.6 version: 2.0.7
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