Compare commits

..

No commits in common. 'master' and 'v2.7.10' have entirely different histories.

  1. 7
      CHANGELOG.txt
  2. 4
      android/app/build.gradle
  3. 4
      android/build.gradle
  4. 2
      android/gradle/wrapper/gradle-wrapper.properties
  5. 4
      codemagic.yaml
  6. 2
      ios/Flutter/AppFrameworkInfo.plist
  7. 2
      ios/Podfile
  8. 34
      ios/Podfile.lock
  9. 76
      ios/Runner.xcodeproj/project.pbxproj
  10. 2
      ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  11. 3
      ios/Runner.xcworkspace/contents.xcworkspacedata
  12. 2
      ios/Runner/Info.plist
  13. 8
      lib/api/station_data.dart
  14. 2
      lib/api/train_data.dart
  15. 2
      lib/components/select_train_suggestions/select_train_suggestions.dart
  16. 5
      lib/components/select_train_suggestions/select_train_suggestions_cupertino.dart
  17. 5
      lib/components/select_train_suggestions/select_train_suggestions_fluent.dart
  18. 3
      lib/components/select_train_suggestions/select_train_suggestions_material.dart
  19. 2
      lib/components/slim_app_bar.dart
  20. 39
      lib/components/train_id_text_span.dart
  21. 50
      lib/main.dart
  22. 7
      lib/models.dart
  23. 2
      lib/models/station_arrdep.freezed.dart
  24. 4
      lib/models/station_data.freezed.dart
  25. 2
      lib/models/station_status.freezed.dart
  26. 3
      lib/models/station_train.freezed.dart
  27. 3
      lib/models/stations_result.freezed.dart
  28. 369
      lib/models/train_data.dart
  29. 2365
      lib/models/train_data.freezed.dart
  30. 217
      lib/models/train_data.g.dart
  31. 2
      lib/models/trains_result.freezed.dart
  32. 94
      lib/models/ui_timezone.dart
  33. 1
      lib/pages/settings/setings_page.dart
  34. 21
      lib/pages/settings/settings_page_cupertino.dart
  35. 23
      lib/pages/settings/settings_page_fluent.dart
  36. 23
      lib/pages/settings/settings_page_material.dart
  37. 3
      lib/pages/station_arrdep_page/view_station/view_station.dart
  38. 43
      lib/pages/station_arrdep_page/view_station/view_station_cupertino.dart
  39. 33
      lib/pages/station_arrdep_page/view_station/view_station_fluent.dart
  40. 35
      lib/pages/station_arrdep_page/view_station/view_station_material.dart
  41. 99
      lib/pages/train_info_page/view_train/train_info.dart
  42. 23
      lib/pages/train_info_page/view_train/train_info_cupertino.dart
  43. 208
      lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart
  44. 20
      lib/pages/train_info_page/view_train/train_info_fluent.dart
  45. 216
      lib/pages/train_info_page/view_train/train_info_fluent_DisplayTrainStation.dart
  46. 115
      lib/pages/train_info_page/view_train/train_info_material.dart
  47. 220
      lib/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart
  48. 47
      lib/providers.dart
  49. 120
      lib/providers.g.dart
  50. 15
      lib/utils/state_to_string.dart
  51. 4
      linux/flutter/generated_plugin_registrant.cc
  52. 1
      linux/flutter/generated_plugins.cmake
  53. 1
      macos/Flutter/Flutter-Debug.xcconfig
  54. 1
      macos/Flutter/Flutter-Release.xcconfig
  55. 40
      macos/Podfile
  56. 41
      macos/Podfile.lock
  57. 71
      macos/Runner.xcodeproj/project.pbxproj
  58. 2
      macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  59. 3
      macos/Runner.xcworkspace/contents.xcworkspacedata
  60. 649
      pubspec.lock
  61. 15
      pubspec.yaml
  62. 3
      windows/flutter/generated_plugin_registrant.cc
  63. 1
      windows/flutter/generated_plugins.cmake

7
CHANGELOG.txt

@ -1,10 +1,3 @@
v2.7.11
Add support for IC trains.
Allow choosing displayed timezone.
Show notes about wagon detachment, receival, or train number changes.
Use system accent color if available.
Use API v3.
v2.7.10
Add about page to Fluent UI.
Add settings page, allowing changing between UIs.

4
android/app/build.gradle

@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 33
compileSdkVersion 31
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@ -51,7 +51,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "ro.dcdev.infotren"
minSdkVersion 16
targetSdkVersion 33
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

4
android/build.gradle

@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -24,6 +24,6 @@ subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
task clean(type: Delete) {
delete rootProject.buildDir
}

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

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

4
codemagic.yaml

@ -30,8 +30,8 @@ workflows:
- |
#! /bin/sh
pwd=$(pwd)
mkdir -p build/ios/Payload
cp -r build/ios/iphoneos/Runner.app build/ios/Payload
mkdir -p build/ios/Payload/infotren.app
cp -r build/ios/iphoneos/Runner.app/* build/ios/Payload/infotren.app
mkdir -p build/ios/ipa
cd build/ios
zip -r Payload.zip Payload

2
ios/Flutter/AppFrameworkInfo.plist

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

2
ios/Podfile

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

34
ios/Podfile.lock

@ -1,34 +0,0 @@
PODS:
- Flutter (1.0.0)
- package_info_plus (0.4.5):
- Flutter
- shared_preferences_ios (0.0.1):
- Flutter
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `Flutter`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Flutter
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
shared_preferences_ios:
:path: ".symlinks/plugins/shared_preferences_ios/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.11.3

76
ios/Runner.xcodeproj/project.pbxproj

@ -13,7 +13,6 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
AF5528149967EA996B5AA109 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E6EB5FA5AA2228D5622CD62 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -32,11 +31,7 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
2088AE25E07C211FFB9CE536 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
2F80AD107B0E1CC9E1C01A5A /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
5DA42B3CD8940DB121C028E8 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
6E6EB5FA5AA2228D5622CD62 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@ -54,7 +49,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
AF5528149967EA996B5AA109 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -78,8 +72,6 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
B55F9B76DFEAB456725329A0 /* Pods */,
E56598AA51C5533E6B51BD5A /* Frameworks */,
);
sourceTree = "<group>";
};
@ -106,25 +98,6 @@
path = Runner;
sourceTree = "<group>";
};
B55F9B76DFEAB456725329A0 /* Pods */ = {
isa = PBXGroup;
children = (
2F80AD107B0E1CC9E1C01A5A /* Pods-Runner.debug.xcconfig */,
2088AE25E07C211FFB9CE536 /* Pods-Runner.release.xcconfig */,
5DA42B3CD8940DB121C028E8 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
E56598AA51C5533E6B51BD5A /* Frameworks */ = {
isa = PBXGroup;
children = (
6E6EB5FA5AA2228D5622CD62 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -132,14 +105,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
2B2F3198BD0D2214C77EC99E /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
D71FC49A789443CEBF7C5C70 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -156,7 +127,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
@ -198,28 +169,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
2B2F3198BD0D2214C77EC99E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -248,23 +197,6 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
D71FC49A789443CEBF7C5C70 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -340,7 +272,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -419,7 +351,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -468,7 +400,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;

2
ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

3
ios/Runner.xcworkspace/contents.xcworkspacedata generated

@ -4,7 +4,4 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

2
ios/Runner/Info.plist

@ -43,7 +43,5 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>

8
lib/api/station_data.dart

@ -4,11 +4,7 @@ import 'package:http/http.dart' as http;
import 'package:info_tren/api/common.dart';
import 'package:info_tren/models.dart';
Future<StationData> getStationData(String stationName, [DateTime? date]) async {
final uri = Uri.https(authority, 'v3/stations/$stationName');
if (date != null) {
uri.queryParameters['date'] = date.toIso8601String();
}
final response = await http.get(uri);
Future<StationData> getStationData(String stationName) async {
final response = await http.get(Uri.https(authority, 'v3/stations/$stationName'));
return StationData.fromJson(jsonDecode(response.body));
}

2
lib/api/train_data.dart

@ -4,7 +4,7 @@ import 'package:info_tren/models.dart';
Future<TrainData> getTrain(String trainNumber, {DateTime? date}) async {
date ??= DateTime.now();
final response = await http.get(Uri.https(authority, 'v3/trains/$trainNumber', {
final response = await http.get(Uri.https(authority, 'v2/train/$trainNumber', {
'date': date.toUtc().toIso8601String(),
}),);
return trainDataFromJson(response.body);

2
lib/components/select_train_suggestions/select_train_suggestions.dart

@ -1,3 +1,4 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_cupertino.dart';
@ -5,6 +6,7 @@ import 'package:info_tren/components/select_train_suggestions/select_train_sugge
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_material.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/providers.dart';
import 'package:info_tren/utils/default_ui_design.dart';
class SelectTrainSuggestions extends ConsumerWidget {
final List<TrainsResult> choices;

5
lib/components/select_train_suggestions/select_train_suggestions_cupertino.dart

@ -1,7 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:info_tren/components/cupertino_divider.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
class SelectTrainSuggestionsCupertino extends SelectTrainSuggestionsShared {
@ -70,8 +69,8 @@ class OperatorAutocompleteTileCupertino extends OperatorAutocompleteTile {
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
textAlign: TextAlign.left,
),
Text.rich(
trainIdSpan(rank: train.rank, number: train.number),
Text(
"${train.rank} ${train.number}",
textAlign: TextAlign.left,
),
],

5
lib/components/select_train_suggestions/select_train_suggestions_fluent.dart

@ -1,6 +1,5 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
class SelectTrainSuggestionsFluent extends SelectTrainSuggestionsShared {
@ -69,8 +68,8 @@ class OperatorAutocompleteTileFluent extends OperatorAutocompleteTile {
style: FluentTheme.of(context).typography.body?.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
textAlign: TextAlign.left,
),
Text.rich(
trainIdSpan(rank: train.rank, number: train.number),
Text(
"${train.rank} ${train.number}",
textAlign: TextAlign.left,
),
],

3
lib/components/select_train_suggestions/select_train_suggestions_material.dart

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
class SelectTrainSuggestionsMaterial extends SelectTrainSuggestionsShared {
@ -45,7 +44,7 @@ class OperatorAutocompleteTileMaterial extends OperatorAutocompleteTile {
Widget build(BuildContext context) {
return ListTile(
dense: true,
title: Text.rich(trainIdSpan(rank: train.rank, number: train.number)),
title: Text("${train.rank} ${train.number}"),
subtitle: Text(operatorName),
onTap: () {
onTrainSelected(train.number);

2
lib/components/slim_app_bar.dart

@ -44,7 +44,7 @@ class SlimAppBar extends StatelessWidget {
title,
textAlign: TextAlign.center,
style:
Theme.of(context).textTheme.titleSmall?.copyWith(color: Theme.of(context).textTheme.titleLarge?.color) ??
Theme.of(context).appBarTheme.textTheme?.bodySmall?.copyWith(color: Theme.of(context).appBarTheme.textTheme?.bodyMedium?.color) ??
Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).textTheme.bodyMedium?.color),
),
),

39
lib/components/train_id_text_span.dart

@ -1,39 +0,0 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
TextSpan trainIdSpan({
required String rank,
required String number,
Locale? locale,
MouseCursor? mouseCursor,
void Function(PointerEnterEvent)? onEnter,
void Function(PointerExitEvent)? onExit,
GestureRecognizer? recognizer,
String? semanticsLabel,
bool? spellOut,
TextStyle? style,
}) => TextSpan(
children: [
TextSpan(
text: rank,
style: TextStyle(
inherit: true,
color: rank.startsWith('IC')
? const Color.fromARGB(255, 0, 255, 0)
: rank.startsWith('IR')
? const Color.fromARGB(255, 255, 0, 0)
: null,
),
),
const TextSpan(text: ' '),
TextSpan(text: number),
],
locale: locale,
mouseCursor: mouseCursor,
onEnter: onEnter,
onExit: onExit,
recognizer: recognizer,
semanticsLabel: semanticsLabel,
spellOut: spellOut,
style: style,
);

50
lib/main.dart

@ -1,6 +1,5 @@
import 'dart:io';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:fluent_ui/fluent_ui.dart' as f;
import 'package:flutter/cupertino.dart' as c;
import 'package:flutter/gestures.dart';
@ -18,11 +17,8 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
import 'package:info_tren/providers.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/data/latest.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
initializeTimeZones();
final sharedPreferences = await SharedPreferences.getInstance();
runApp(
ProviderScope(
@ -109,8 +105,6 @@ class StartPoint extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
if (uiDesign == UiDesign.CUPERTINO) {
return DynamicColorBuilder(
builder: (lightScheme, darkScheme) {
return AnnotatedRegion(
value: const SystemUiOverlayStyle(
statusBarBrightness: c.Brightness.dark,
@ -118,7 +112,7 @@ class StartPoint extends ConsumerWidget {
child: c.CupertinoApp(
title: appTitle,
theme: c.CupertinoThemeData(
primaryColor: darkScheme?.primary ?? m.Colors.blue.shade600,
primaryColor: m.Colors.blue.shade600,
brightness: c.Brightness.dark,
// textTheme: CupertinoTextThemeData(
// textStyle: TextStyle(
@ -131,53 +125,27 @@ class StartPoint extends ConsumerWidget {
),
);
}
);
}
else if (uiDesign == UiDesign.FLUENT) {
return DynamicColorBuilder(
builder: (lightScheme, darkScheme) {
return f.FluentApp(
title: appTitle,
theme: f.FluentThemeData(
brightness: f.Brightness.light,
accentColor: lightScheme != null ? f.AccentColor.swatch({
'normal': lightScheme.primary,
}) : f.Colors.blue,
),
darkTheme: f.FluentThemeData(
theme: f.ThemeData(
brightness: f.Brightness.dark,
accentColor: darkScheme != null ? f.AccentColor.swatch({
'normal': darkScheme.primary,
}) : f.Colors.blue,
accentColor: f.Colors.blue,
),
routes: routes,
scrollBehavior: Platform.isLinux ? const DragFluentScrollBevahior() : const f.FluentScrollBehavior(),
);
}
);
}
else {
return DynamicColorBuilder(
builder: (lightScheme, darkScheme) {
lightScheme ??= m.ColorScheme.fromSwatch(
brightness: m.Brightness.light,
primarySwatch: m.Colors.blue,
);
darkScheme ??= m.ColorScheme.fromSwatch(
brightness: m.Brightness.dark,
primarySwatch: m.Colors.blue,
);
return m.MaterialApp(
title: appTitle,
theme: m.ThemeData(
colorScheme: lightScheme,
useMaterial3: true,
// fontFamily: 'Atkinson Hyperlegible',
primarySwatch: m.Colors.blue,
colorScheme: m.ColorScheme.fromSwatch(
brightness: m.Brightness.dark,
primarySwatch: m.Colors.blue,
accentColor: m.Colors.blue.shade700,
),
darkTheme: m.ThemeData(
colorScheme: darkScheme,
useMaterial3: true,
// fontFamily: 'Atkinson Hyperlegible',
),
@ -185,7 +153,5 @@ class StartPoint extends ConsumerWidget {
routes: routes,
);
}
);
}
}
}

7
lib/models.dart

@ -4,11 +4,10 @@ export 'package:info_tren/models/station_data.dart';
export 'package:info_tren/models/station_status.dart';
export 'package:info_tren/models/station_train.dart';
export 'package:info_tren/models/stations_result.dart';
export 'package:info_tren/models/train_data.dart';
export 'package:info_tren/models/train_data.dart' hide State;
export 'package:info_tren/models/trains_result.dart';
export 'package:info_tren/models/ui_design.dart';
export 'package:info_tren/models/ui_timezone.dart';
import 'package:info_tren/models/train_data.dart' show TrainDataStatusState;
import 'package:info_tren/models/train_data.dart' show State;
typedef TrainDataState = TrainDataStatusState;
typedef TrainDataState = State;

2
lib/models/station_arrdep.freezed.dart

@ -1,7 +1,7 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'station_arrdep.dart';

4
lib/models/station_data.freezed.dart

@ -1,7 +1,7 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'station_data.dart';
@ -158,7 +158,6 @@ class _$_StationData implements _StationData {
List<StationArrDep>? get arrivals {
final value = _arrivals;
if (value == null) return null;
if (_arrivals is EqualUnmodifiableListView) return _arrivals;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}
@ -168,7 +167,6 @@ class _$_StationData implements _StationData {
List<StationArrDep>? get departures {
final value = _departures;
if (value == null) return null;
if (_departures is EqualUnmodifiableListView) return _departures;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}

2
lib/models/station_status.freezed.dart

@ -1,7 +1,7 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'station_status.dart';

3
lib/models/station_train.freezed.dart

@ -1,7 +1,7 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'station_train.dart';
@ -189,7 +189,6 @@ class _$_StationTrain implements _StationTrain {
List<String>? get route {
final value = _route;
if (value == null) return null;
if (_route is EqualUnmodifiableListView) return _route;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}

3
lib/models/stations_result.freezed.dart

@ -1,7 +1,7 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'stations_result.dart';
@ -121,7 +121,6 @@ class _$_StationsResult implements _StationsResult {
List<String>? get stoppedAtBy {
final value = _stoppedAtBy;
if (value == null) return null;
if (_stoppedAtBy is EqualUnmodifiableListView) return _stoppedAtBy;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(value);
}

369
lib/models/train_data.dart

@ -4,118 +4,181 @@
import 'dart:convert';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'train_data.freezed.dart';
part 'train_data.g.dart';
TrainData trainDataFromJson(String str) => TrainData.fromJson(json.decode(str));
String trainDataToJson(TrainData data) => json.encode(data.toJson());
/// Results of scrapping InfoFer website for train info
@freezed
class TrainData with _$TrainData {
const TrainData._();
const factory TrainData({
required String rank,
required String number,
required String date,
required String operator,
required List<TrainDataGroup> groups,
}) = _TrainData;
factory TrainData.fromJson(Map<String, dynamic> json) => _$TrainDataFromJson(json);
TrainDataGroup get idealGroup {
var result = groups.first;
for (final group in groups) {
if (result.stations.map((s) => s.linkName).toSet().difference(group.stations.map((s) => s.linkName).toSet()).isEmpty) {
result = group;
}
}
class TrainData {
TrainData({
required this.date,
required this.number,
required this.operator,
required this.rank,
required this.route,
required this.stations,
this.status,
});
final String date;
final String number;
final String operator;
final String rank;
final Route route;
final List<Station> stations;
final TrainDataStatus? status;
factory TrainData.fromJson(Map<String, dynamic> json) => TrainData(
date: json["date"],
number: json["number"],
operator: json["operator"],
rank: json["rank"],
route: Route.fromJson(json["route"]),
stations: List<Station>.from(
json["stations"].map((x) => Station.fromJson(x))),
status: json["status"] == null
? null
: TrainDataStatus.fromJson(json["status"]),
);
return result;
Map<String, dynamic> toJson() => {
"date": date,
"number": number,
"operator": operator,
"rank": rank,
"route": route.toJson(),
"stations": List<dynamic>.from(stations.map((x) => x.toJson())),
"status": status?.toJson(),
};
}
List<TrainDataStation> get stations => idealGroup.stations;
TrainDataRoute get route => idealGroup.route;
TrainDataStatus? get status => idealGroup.status;
/// Route of the train
class Route {
Route({
required this.from,
required this.to,
});
final String from;
final String to;
factory Route.fromJson(Map<String, dynamic> json) => Route(
from: json["from"],
to: json["to"],
);
Map<String, dynamic> toJson() => {
"from": from,
"to": to,
};
}
@freezed
class TrainDataGroup with _$TrainDataGroup {
const factory TrainDataGroup({
required TrainDataRoute route,
required List<TrainDataStation> stations,
TrainDataStatus? status,
}) = _TrainDataGroup;
class Station {
Station({
this.arrival,
this.departure,
required this.km,
required this.name,
this.platform,
this.stoppingTime,
});
final StationArrDepTime? arrival;
final StationArrDepTime? departure;
final int km;
final String name;
final String? platform;
final int? stoppingTime;
factory Station.fromJson(Map<String, dynamic> json) => Station(
arrival: json["arrival"] == null
? null
: StationArrDepTime.fromJson(json["arrival"]),
departure: json["departure"] == null
? null
: StationArrDepTime.fromJson(json["departure"]),
km: json["km"],
name: json["name"],
platform: json["platform"],
stoppingTime:
json["stoppingTime"],
);
factory TrainDataGroup.fromJson(Map<String, dynamic> json) => _$TrainDataGroupFromJson(json);
Map<String, dynamic> toJson() => {
"arrival": arrival?.toJson(),
"departure": departure?.toJson(),
"km": km,
"name": name,
"platform": platform,
"stoppingTime": stoppingTime,
};
}
/// Route of the train
@freezed
class TrainDataRoute with _$TrainDataRoute {
const factory TrainDataRoute({
required String from,
required String to,
}) = _TrainDataRoute;
factory TrainDataRoute.fromJson(Map<String, dynamic> json) => _$TrainDataRouteFromJson(json);
}
class StationArrDepTime {
StationArrDepTime({
required this.scheduleTime,
this.status,
});
@freezed
class TrainDataStation with _$TrainDataStation {
const factory TrainDataStation({
required String name,
required String linkName,
required int km,
int? stoppingTime,
String? platform,
StationArrDepTime? arrival,
StationArrDepTime? departure,
@TrainDataNoteConverter()
required List<TrainDataNote> notes,
}) = _TrainDataStation;
factory TrainDataStation.fromJson(Map<String, dynamic> json) => _$TrainDataStationFromJson(json);
}
final DateTime scheduleTime;
final StationArrDepTimeStatus? status;
@freezed
class StationArrDepTime with _$StationArrDepTime {
const factory StationArrDepTime({
required DateTime scheduleTime,
StationArrDepTimeStatus? status,
}) = _StationArrDepTime;
factory StationArrDepTime.fromJson(Map<String, dynamic> json) =>
StationArrDepTime(
scheduleTime: DateTime.parse(json["scheduleTime"] as String),
status: json["status"] == null ? null : StationArrDepTimeStatus.fromJson(json["status"]),
);
factory StationArrDepTime.fromJson(Map<String, dynamic> json) => _$StationArrDepTimeFromJson(json);
Map<String, dynamic> toJson() => {
"scheduleTime": scheduleTime.toIso8601String(),
"status": status?.toJson(),
};
}
@freezed
class StationArrDepTimeStatus with _$StationArrDepTimeStatus {
const factory StationArrDepTimeStatus({
required int delay,
required bool real,
required bool cancelled,
}) = _StationArrDepTimeStatus;
class StationArrDepTimeStatus {
StationArrDepTimeStatus({
required this.delay,
required this.real,
});
final int delay;
final bool real;
factory StationArrDepTimeStatus.fromJson(Map<String, dynamic> json) => _$StationArrDepTimeStatusFromJson(json);
factory StationArrDepTimeStatus.fromJson(Map<String, dynamic> json) =>
StationArrDepTimeStatus(
delay: json["delay"],
real: json["real"],
);
Map<String, dynamic> toJson() => {
"delay": delay,
"real": real,
};
}
@freezed
class TrainDataStatus with _$TrainDataStatus {
const TrainDataStatus._();
class TrainDataStatus {
TrainDataStatus({
required this.delay,
required this.state,
required this.station,
});
const factory TrainDataStatus({
required int delay,
required String station,
required TrainDataStatusState state,
}) = _TrainDataStatus;
final int delay;
final State state;
final String station;
factory TrainDataStatus.fromJson(Map<String, dynamic> json) => _$TrainDataStatusFromJson(json);
factory TrainDataStatus.fromJson(Map<String, dynamic> json) =>
TrainDataStatus(
delay: json["delay"],
state: stateValues.map[json["state"]]!,
station: json["station"],
);
Map<String, dynamic> toJson() => {
"delay": delay,
"state": stateValues.reverse[state],
"station": station,
};
@override
String toString() {
@ -127,118 +190,38 @@ class TrainDataStatus with _$TrainDataStatus {
result += '${delay.abs()} min';
}
result += ' la ';
result += switch (state) {
TrainDataStatusState.passing => 'trecerea fără oprire prin',
TrainDataStatusState.arrival => 'sosirea în',
TrainDataStatusState.departure => 'plecarea din',
};
switch (state) {
case State.PASSING:
result += 'trecerea fără oprire prin';
break;
case State.ARRIVAL:
result += 'sosirea în';
break;
case State.DEPARTURE:
result += 'plecarea din';
break;
}
result += station;
return result;
}
}
enum TrainDataStatusState { passing, arrival, departure }
abstract class TrainDataNote {
final String kind;
const TrainDataNote({required this.kind});
Map<String, dynamic> toJson() => {
"kind": kind,
};
}
class TrainDataNoteConverter implements JsonConverter<TrainDataNote, Map<String, dynamic>> {
const TrainDataNoteConverter();
enum State { PASSING, ARRIVAL, DEPARTURE }
@override
TrainDataNote fromJson(Map<String, dynamic> json) {
return switch(json['kind']) {
'trainNumberChange' => TrainDataNoteTrainNumberChange.fromJson(json),
'departsAs' => TrainDataNoteDepartsAs.fromJson(json),
'detachingWagons' => TrainDataNoteDetachingWagons.fromJson(json),
'receivingWagons' => TrainDataNoteReceivingWagons.fromJson(json),
_ => TrainDataNoteUnknown.fromJson(json),
};
}
final stateValues = EnumValues({
"arrival": State.ARRIVAL,
"departure": State.DEPARTURE,
"passing": State.PASSING
});
@override
Map<String, dynamic> toJson(TrainDataNote object) {
return object.toJson();
}
}
class EnumValues<T> {
Map<String, T> map;
Map<T, String>? reverseMap;
@freezed
class TrainDataNoteTrainNumberChange with _$TrainDataNoteTrainNumberChange implements TrainDataNote {
@Implements<TrainDataNote>()
const factory TrainDataNoteTrainNumberChange({
// base
@Default("trainNumberChange")
String kind,
// impl
required String rank,
required String number,
}) = _TrainDataNoteTrainNumberChange;
factory TrainDataNoteTrainNumberChange.fromJson(Map<String, dynamic> json) => _$TrainDataNoteTrainNumberChangeFromJson(json);
}
@freezed
class TrainDataNoteDepartsAs with _$TrainDataNoteDepartsAs implements TrainDataNote {
@Implements<TrainDataNote>()
const factory TrainDataNoteDepartsAs({
// base
@Default("departsAs")
String kind,
// impl
required String rank,
required String number,
required DateTime departureDate,
}) = _TrainDataNoteDepartsAs;
factory TrainDataNoteDepartsAs.fromJson(Map<String, dynamic> json) => _$TrainDataNoteDepartsAsFromJson(json);
}
EnumValues(this.map);
@freezed
class TrainDataNoteDetachingWagons with _$TrainDataNoteDetachingWagons implements TrainDataNote {
@Implements<TrainDataNote>()
const factory TrainDataNoteDetachingWagons({
// base
@Default("detachingWagons")
String kind,
// impl
required String station,
}) = _TrainDataNoteDetachingWagons;
factory TrainDataNoteDetachingWagons.fromJson(Map<String, dynamic> json) => _$TrainDataNoteDetachingWagonsFromJson(json);
Map<T, String> get reverse {
reverseMap ??= map.map((k, v) => MapEntry(v, k));
return reverseMap!;
}
@freezed
class TrainDataNoteReceivingWagons with _$TrainDataNoteReceivingWagons implements TrainDataNote {
@Implements<TrainDataNote>()
const factory TrainDataNoteReceivingWagons({
// base
@Default("receivingWagons")
String kind,
// impl
required String station,
}) = _TrainDataNoteReceivingWagons;
factory TrainDataNoteReceivingWagons.fromJson(Map<String, dynamic> json) => _$TrainDataNoteReceivingWagonsFromJson(json);
}
@freezed
class TrainDataNoteUnknown with _$TrainDataNoteUnknown implements TrainDataNote {
@Implements<TrainDataNote>()
const factory TrainDataNoteUnknown({
// base
required String kind,
// impl
required Map<String, dynamic> extra,
}) = _TrainDataNoteUnknown;
factory TrainDataNoteUnknown.fromJson(Map<String, dynamic> json) => TrainDataNoteUnknown(
kind: json['kind'],
extra: Map.from(json)..remove('kind'),
);
}

2365
lib/models/train_data.freezed.dart

File diff suppressed because it is too large Load Diff

217
lib/models/train_data.g.dart

@ -1,217 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'train_data.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_TrainData _$$_TrainDataFromJson(Map<String, dynamic> json) => _$_TrainData(
rank: json['rank'] as String,
number: json['number'] as String,
date: json['date'] as String,
operator: json['operator'] as String,
groups: (json['groups'] as List<dynamic>)
.map((e) => TrainDataGroup.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$_TrainDataToJson(_$_TrainData instance) =>
<String, dynamic>{
'rank': instance.rank,
'number': instance.number,
'date': instance.date,
'operator': instance.operator,
'groups': instance.groups,
};
_$_TrainDataGroup _$$_TrainDataGroupFromJson(Map<String, dynamic> json) =>
_$_TrainDataGroup(
route: TrainDataRoute.fromJson(json['route'] as Map<String, dynamic>),
stations: (json['stations'] as List<dynamic>)
.map((e) => TrainDataStation.fromJson(e as Map<String, dynamic>))
.toList(),
status: json['status'] == null
? null
: TrainDataStatus.fromJson(json['status'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$_TrainDataGroupToJson(_$_TrainDataGroup instance) =>
<String, dynamic>{
'route': instance.route,
'stations': instance.stations,
'status': instance.status,
};
_$_TrainDataRoute _$$_TrainDataRouteFromJson(Map<String, dynamic> json) =>
_$_TrainDataRoute(
from: json['from'] as String,
to: json['to'] as String,
);
Map<String, dynamic> _$$_TrainDataRouteToJson(_$_TrainDataRoute instance) =>
<String, dynamic>{
'from': instance.from,
'to': instance.to,
};
_$_TrainDataStation _$$_TrainDataStationFromJson(Map<String, dynamic> json) =>
_$_TrainDataStation(
name: json['name'] as String,
linkName: json['linkName'] as String,
km: json['km'] as int,
stoppingTime: json['stoppingTime'] as int?,
platform: json['platform'] as String?,
arrival: json['arrival'] == null
? null
: StationArrDepTime.fromJson(json['arrival'] as Map<String, dynamic>),
departure: json['departure'] == null
? null
: StationArrDepTime.fromJson(
json['departure'] as Map<String, dynamic>),
notes: (json['notes'] as List<dynamic>)
.map((e) => const TrainDataNoteConverter()
.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$_TrainDataStationToJson(_$_TrainDataStation instance) =>
<String, dynamic>{
'name': instance.name,
'linkName': instance.linkName,
'km': instance.km,
'stoppingTime': instance.stoppingTime,
'platform': instance.platform,
'arrival': instance.arrival,
'departure': instance.departure,
'notes':
instance.notes.map(const TrainDataNoteConverter().toJson).toList(),
};
_$_StationArrDepTime _$$_StationArrDepTimeFromJson(Map<String, dynamic> json) =>
_$_StationArrDepTime(
scheduleTime: DateTime.parse(json['scheduleTime'] as String),
status: json['status'] == null
? null
: StationArrDepTimeStatus.fromJson(
json['status'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$_StationArrDepTimeToJson(
_$_StationArrDepTime instance) =>
<String, dynamic>{
'scheduleTime': instance.scheduleTime.toIso8601String(),
'status': instance.status,
};
_$_StationArrDepTimeStatus _$$_StationArrDepTimeStatusFromJson(
Map<String, dynamic> json) =>
_$_StationArrDepTimeStatus(
delay: json['delay'] as int,
real: json['real'] as bool,
cancelled: json['cancelled'] as bool,
);
Map<String, dynamic> _$$_StationArrDepTimeStatusToJson(
_$_StationArrDepTimeStatus instance) =>
<String, dynamic>{
'delay': instance.delay,
'real': instance.real,
'cancelled': instance.cancelled,
};
_$_TrainDataStatus _$$_TrainDataStatusFromJson(Map<String, dynamic> json) =>
_$_TrainDataStatus(
delay: json['delay'] as int,
station: json['station'] as String,
state: $enumDecode(_$TrainDataStatusStateEnumMap, json['state']),
);
Map<String, dynamic> _$$_TrainDataStatusToJson(_$_TrainDataStatus instance) =>
<String, dynamic>{
'delay': instance.delay,
'station': instance.station,
'state': _$TrainDataStatusStateEnumMap[instance.state]!,
};
const _$TrainDataStatusStateEnumMap = {
TrainDataStatusState.passing: 'passing',
TrainDataStatusState.arrival: 'arrival',
TrainDataStatusState.departure: 'departure',
};
_$_TrainDataNoteTrainNumberChange _$$_TrainDataNoteTrainNumberChangeFromJson(
Map<String, dynamic> json) =>
_$_TrainDataNoteTrainNumberChange(
kind: json['kind'] as String? ?? "trainNumberChange",
rank: json['rank'] as String,
number: json['number'] as String,
);
Map<String, dynamic> _$$_TrainDataNoteTrainNumberChangeToJson(
_$_TrainDataNoteTrainNumberChange instance) =>
<String, dynamic>{
'kind': instance.kind,
'rank': instance.rank,
'number': instance.number,
};
_$_TrainDataNoteDepartsAs _$$_TrainDataNoteDepartsAsFromJson(
Map<String, dynamic> json) =>
_$_TrainDataNoteDepartsAs(
kind: json['kind'] as String? ?? "departsAs",
rank: json['rank'] as String,
number: json['number'] as String,
departureDate: DateTime.parse(json['departureDate'] as String),
);
Map<String, dynamic> _$$_TrainDataNoteDepartsAsToJson(
_$_TrainDataNoteDepartsAs instance) =>
<String, dynamic>{
'kind': instance.kind,
'rank': instance.rank,
'number': instance.number,
'departureDate': instance.departureDate.toIso8601String(),
};
_$_TrainDataNoteDetachingWagons _$$_TrainDataNoteDetachingWagonsFromJson(
Map<String, dynamic> json) =>
_$_TrainDataNoteDetachingWagons(
kind: json['kind'] as String? ?? "detachingWagons",
station: json['station'] as String,
);
Map<String, dynamic> _$$_TrainDataNoteDetachingWagonsToJson(
_$_TrainDataNoteDetachingWagons instance) =>
<String, dynamic>{
'kind': instance.kind,
'station': instance.station,
};
_$_TrainDataNoteReceivingWagons _$$_TrainDataNoteReceivingWagonsFromJson(
Map<String, dynamic> json) =>
_$_TrainDataNoteReceivingWagons(
kind: json['kind'] as String? ?? "receivingWagons",
station: json['station'] as String,
);
Map<String, dynamic> _$$_TrainDataNoteReceivingWagonsToJson(
_$_TrainDataNoteReceivingWagons instance) =>
<String, dynamic>{
'kind': instance.kind,
'station': instance.station,
};
_$_TrainDataNoteUnknown _$$_TrainDataNoteUnknownFromJson(
Map<String, dynamic> json) =>
_$_TrainDataNoteUnknown(
kind: json['kind'] as String,
extra: json['extra'] as Map<String, dynamic>,
);
Map<String, dynamic> _$$_TrainDataNoteUnknownToJson(
_$_TrainDataNoteUnknown instance) =>
<String, dynamic>{
'kind': instance.kind,
'extra': instance.extra,
};

2
lib/models/trains_result.freezed.dart

@ -1,7 +1,7 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'trains_result.dart';

94
lib/models/ui_timezone.dart

@ -1,94 +0,0 @@
import 'package:timezone/timezone.dart' as tz;
enum UiTimeZoneType {
ro,
local,
utc,
iana,
}
extension UITimeZoneTypeName on UiTimeZoneType {
String get userInterfaceName => (const {
UiTimeZoneType.iana: 'Fus orar IANA',
UiTimeZoneType.local: 'Local',
UiTimeZoneType.ro: 'România',
UiTimeZoneType.utc: 'UTC',
})[this]!;
}
const Map<UiTimeZoneType, UiTimeZone Function(String)> fromSerStringConstructors = {
UiTimeZoneType.ro: RoUiTimeZone.fromSerString,
UiTimeZoneType.local: LocalUiTimeZone.fromSerString,
UiTimeZoneType.utc: UtcUiTimeZone.fromSerString,
UiTimeZoneType.iana: IanaUiTimeZone.fromSerString,
};
abstract class UiTimeZone {
final UiTimeZoneType type;
const UiTimeZone({required this.type});
DateTime convertDateTime(DateTime dt);
factory UiTimeZone.fromSerString(String ser) {
final arr = ser.split('\n');
return fromSerStringConstructors.map((key, value) => MapEntry(key.name, value))[arr[0]]!(ser);
}
String toSerString() {
return '${type.name}\n';
}
}
class RoUiTimeZone extends UiTimeZone {
static final roTz = tz.getLocation('Europe/Bucharest');
const RoUiTimeZone() : super(type: UiTimeZoneType.ro);
factory RoUiTimeZone.fromSerString(String ser) => const RoUiTimeZone();
@override
DateTime convertDateTime(DateTime dt) {
return tz.TZDateTime.from(dt, roTz);
}
}
class LocalUiTimeZone extends UiTimeZone {
const LocalUiTimeZone() : super(type: UiTimeZoneType.local);
factory LocalUiTimeZone.fromSerString(String ser) => LocalUiTimeZone();
@override
DateTime convertDateTime(DateTime dt) => dt.toLocal();
}
class UtcUiTimeZone extends UiTimeZone {
const UtcUiTimeZone() : super(type: UiTimeZoneType.utc);
factory UtcUiTimeZone.fromSerString(String ser) => UtcUiTimeZone();
@override
DateTime convertDateTime(DateTime dt) => dt.toUtc();
}
class IanaUiTimeZone extends UiTimeZone {
late final tz.Location location;
IanaUiTimeZone({required String ianaName}): super(type: UiTimeZoneType.iana) {
location = tz.getLocation(ianaName);
}
factory IanaUiTimeZone.fromSerString(String ser) => IanaUiTimeZone(
ianaName: ser.split('\n').skip(1).join('\n'),
);
@override
DateTime convertDateTime(DateTime dt) {
return tz.TZDateTime.from(dt, location);
}
@override
String toSerString() {
return '${type.name}\n${location.name}';
}
}

1
lib/pages/settings/setings_page.dart

@ -31,7 +31,6 @@ class SettingsPage extends ConsumerWidget {
abstract class SettingsPageShared extends StatelessWidget {
final String pageTitle = 'Setări';
final String appearanceTitle = 'Aspect';
final String timeZoneTitle = 'Fus orar';
const SettingsPageShared({super.key});

21
lib/pages/settings/settings_page_cupertino.dart

@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/cupertino_listtile.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/settings/setings_page.dart';
import 'package:info_tren/providers.dart';
@ -66,26 +67,6 @@ class SettingsPageCupertino extends SettingsPageShared {
);
},
),
Consumer(
builder: (context, ref, _) {
final currentTZ = ref.watch(uiTimeZoneProvider);
return CupertinoListTile(
title: Text(timeZoneTitle),
trailing: Text(currentTZ.type.userInterfaceName),
onTap: () async {
final choice = await singleChoice(
context: context,
choices: UiTimeZoneType.values.where((tz) => tz != UiTimeZoneType.iana).toList(),
labelBuilder: (UiTimeZoneType utzt) => utzt.userInterfaceName,
title: timeZoneTitle,
);
if (choice != null) {
ref.read(uiTimeZoneProvider.notifier).set(UiTimeZone.fromSerString('${choice.name}\n'));
}
},
);
},
),
],
),
);

23
lib/pages/settings/settings_page_fluent.dart

@ -34,29 +34,6 @@ class SettingsPageFluent extends SettingsPageShared {
);
},
),
Consumer(
builder: (context, ref, _) {
final currentTZ = ref.watch(uiTimeZoneProvider);
return ListTile(
title: Text(timeZoneTitle),
trailing: ComboBox<UiTimeZoneType>(
items: UiTimeZoneType.values.where((tz) => tz != UiTimeZoneType.iana).map((tzt) => ComboBoxItem(
value: tzt,
child: Text(tzt.userInterfaceName),
)).toList(),
value: currentTZ.type,
onChanged: (newTZ) {
if (newTZ != null) {
ref.read(uiTimeZoneProvider.notifier).set(UiTimeZone.fromSerString('${newTZ.name}\n'));
}
else {
ref.read(uiTimeZoneProvider.notifier).set(null);
}
},
),
);
},
),
],
),
),

23
lib/pages/settings/settings_page_material.dart

@ -35,29 +35,6 @@ class SettingsPageMaterial extends SettingsPageShared {
);
},
),
Consumer(
builder: (context, ref, _) {
final currentTZ = ref.watch(uiTimeZoneProvider);
return ListTile(
title: Text(timeZoneTitle),
trailing: DropdownButton<UiTimeZoneType>(
items: UiTimeZoneType.values.where((tz) => tz != UiTimeZoneType.iana).map((tzt) => DropdownMenuItem(
value: tzt,
child: Text(tzt.userInterfaceName),
)).toList(),
value: currentTZ.type,
onChanged: (newTZ) {
if (newTZ != null) {
ref.read(uiTimeZoneProvider.notifier).set(UiTimeZone.fromSerString('${newTZ.name}\n'));
}
else {
ref.read(uiTimeZoneProvider.notifier).set(null);
}
},
),
);
},
),
],
),
),

3
lib/pages/station_arrdep_page/view_station/view_station.dart

@ -43,9 +43,8 @@ class ViewStationPage extends HookConsumerWidget {
class ViewStationArguments {
final String stationName;
final DateTime? date;
const ViewStationArguments({required this.stationName, this.date});
const ViewStationArguments({required this.stationName});
}
abstract class ViewStationPageShared extends StatelessWidget {

43
lib/pages/station_arrdep_page/view_station/view_station_cupertino.dart

@ -3,7 +3,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/loading/loading.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/components/sliver_persistent_header_padding.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/providers.dart';
@ -67,7 +66,18 @@ class ViewStationPageCupertino extends ViewStationPageShared {
onTap: () => onTrainTapped(context, item.train),
child: CupertinoFormRow(
prefix: Text.rich(
trainIdSpan(rank: item.train.rank, number: item.train.number),
TextSpan(
children: [
TextSpan(
text: item.train.rank,
style: TextStyle(
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
),
),
const TextSpan(text: ' '),
TextSpan(text: item.train.number,),
],
),
),
helper: Text.rich(
TextSpan(
@ -78,13 +88,7 @@ class ViewStationPageCupertino extends ViewStationPageShared {
],
),
),
child: Consumer(
builder: (context, ref, _) {
final tz = ref.watch(uiTimeZoneProvider);
final time = tz.convertDateTime(item.time);
return Text('${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}');
},
),
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'),
),
);
}
@ -95,7 +99,18 @@ class ViewStationPageCupertino extends ViewStationPageShared {
onTap: () => onTrainTapped(context, item.train),
child: CupertinoFormRow(
prefix: Text.rich(
trainIdSpan(rank: item.train.rank, number: item.train.number),
TextSpan(
children: [
TextSpan(
text: item.train.rank,
style: TextStyle(
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
),
),
const TextSpan(text: ' '),
TextSpan(text: item.train.number,),
],
),
),
helper: Text.rich(
TextSpan(
@ -106,13 +121,7 @@ class ViewStationPageCupertino extends ViewStationPageShared {
],
),
),
child: Consumer(
builder: (context, ref, _) {
final tz = ref.watch(uiTimeZoneProvider);
final time = tz.convertDateTime(item.time);
return Text('${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}');
},
),
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'),
),
);
}

33
lib/pages/station_arrdep_page/view_station/view_station_fluent.dart

@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/badge/badge.dart';
import 'package:info_tren/components/loading/loading.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/providers.dart';
@ -142,12 +141,8 @@ class ViewStationPageFluent extends ViewStationPageShared {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer(
builder: (context, ref, _) {
final tz = ref.watch(uiTimeZoneProvider);
final time = tz.convertDateTime(item.time);
return Text(
'${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}',
Text(
'${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}',
style: TextStyle(
inherit: true,
fontFeatures: const [
@ -156,17 +151,14 @@ class ViewStationPageFluent extends ViewStationPageShared {
decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
fontSize: item.status.delay != 0 ? 12 : null,
),
);
},
),
if (item.status.delay != 0) Consumer(
builder: (context, ref, _) {
final tz = ref.watch(uiTimeZoneProvider);
final newTime = tz.convertDateTime(item.time.add(Duration(minutes: item.status.delay)));
if (item.status.delay != 0) Builder(
builder: (context) {
final newTime = item.time.add(Duration(minutes: item.status.delay));
final delay = item.status.delay > 0;
return Text(
'${newTime.hour.toString().padLeft(2, '0')}:${newTime.minute.toString().padLeft(2, '0')}',
'${newTime.toLocal().hour.toString().padLeft(2, '0')}:${newTime.toLocal().minute.toString().padLeft(2, '0')}',
style: TextStyle(
inherit: true,
fontFeatures: const [
@ -185,7 +177,18 @@ class ViewStationPageFluent extends ViewStationPageShared {
child: ListTile(
// isThreeLine: item.status.delay != 0,
title: Text.rich(
trainIdSpan(rank: item.train.rank, number: item.train.number),
TextSpan(
children: [
TextSpan(
text: item.train.rank,
style: TextStyle(
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
),
),
const TextSpan(text: ' '),
TextSpan(text: item.train.number,),
],
),
),
subtitle: Text.rich(
TextSpan(

35
lib/pages/station_arrdep_page/view_station/view_station_material.dart

@ -1,11 +1,10 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart' hide Badge;
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/badge/badge.dart';
import 'package:info_tren/components/loading/loading.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/providers.dart';
@ -107,12 +106,8 @@ class ViewStationPageMaterial extends ViewStationPageShared {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer(
builder: (context, ref, _) {
final tz = ref.watch(uiTimeZoneProvider);
final time = tz.convertDateTime(item.time);
return Text(
'${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}',
Text(
'${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}',
style: TextStyle(
inherit: true,
fontFeatures: const [
@ -121,17 +116,14 @@ class ViewStationPageMaterial extends ViewStationPageShared {
decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
fontSize: item.status.delay != 0 ? 12 : null,
),
);
},
),
if (item.status.delay != 0) Consumer(
builder: (context, ref, _) {
final tz = ref.watch(uiTimeZoneProvider);
final newTime = tz.convertDateTime(item.time.add(Duration(minutes: item.status.delay)));
if (item.status.delay != 0) Builder(
builder: (context) {
final newTime = item.time.add(Duration(minutes: item.status.delay));
final delay = item.status.delay > 0;
return Text(
'${newTime.hour.toString().padLeft(2, '0')}:${newTime.minute.toString().padLeft(2, '0')}',
'${newTime.toLocal().hour.toString().padLeft(2, '0')}:${newTime.toLocal().minute.toString().padLeft(2, '0')}',
style: TextStyle(
inherit: true,
fontFeatures: const [
@ -150,7 +142,18 @@ class ViewStationPageMaterial extends ViewStationPageShared {
child: ListTile(
isThreeLine: item.status.delay != 0,
title: Text.rich(
trainIdSpan(rank: item.train.rank, number: item.train.number),
TextSpan(
children: [
TextSpan(
text: item.train.rank,
style: TextStyle(
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
),
),
const TextSpan(text: ' '),
TextSpan(text: item.train.number,),
],
),
),
subtitle: Text.rich(
TextSpan(

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

@ -1,7 +1,4 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/api/train_data.dart';
import 'package:info_tren/components/loading/loading.dart';
@ -12,93 +9,59 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info_fluent.dar
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
import 'package:info_tren/providers.dart';
class TrainInfo extends HookConsumerWidget {
class TrainInfo extends ConsumerWidget {
static String routeName = "/trainInfo/display";
const TrainInfo({
super.key,
});
const TrainInfo({super.key,});
@override
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
final args = ref.watch(trainInfoArgumentsProvider);
final trainNumber = args.trainNumber;
final viewYesterday = useState(false);
final date = args.date ??
DateTime.now().copyWith(
hour: 12,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
);
final requestDate =
viewYesterday.value ? date.subtract(const Duration(days: 1)) : date;
final trainDataAsync = ref
.watch(trainInfoProvider(trainNumber: trainNumber, date: requestDate));
Future refresh() async {
ref.invalidate(
trainInfoProvider(trainNumber: trainNumber, date: requestDate),
);
await Future.delayed(const Duration(seconds: 1));
}
final date = args.date;
return RefreshFutureBuilder<TrainData>(
futureCreator: () => getTrain(trainNumber, date: date),
builder: (context, refresh, replaceFutureBuilder, snapshot) {
void onViewYesterdayTrain() {
viewYesterday.value = !viewYesterday.value;
replaceFutureBuilder(() => getTrain(trainNumber, date: DateTime.now().subtract(const Duration(days: 1))));
}
useEffect(() {
final handle = Timer.periodic(const Duration(minutes: 1), (timer) {
refresh();
});
return () {
handle.cancel();
};
});
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
return TrainInfoLoading(title: trainNumber.toString(), loadingText: "Se încarcă...",);
}
else if (snapshot.state == RefreshFutureBuilderState.error) {
return TrainInfoError(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,);
}
return trainDataAsync.when(
data: (data) {
switch (uiDesign) {
case UiDesign.MATERIAL:
return TrainInfoMaterial(
trainData: data,
trainData: snapshot.data!,
refresh: refresh,
isRefreshing: trainDataAsync.isRefreshing,
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
onViewYesterdayTrain: onViewYesterdayTrain,
);
case UiDesign.CUPERTINO:
return TrainInfoCupertino(
trainData: data,
trainData: snapshot.data!,
refresh: refresh,
isRefreshing: trainDataAsync.isRefreshing,
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
onViewYesterdayTrain: onViewYesterdayTrain,
);
case UiDesign.FLUENT:
return TrainInfoFluent(
trainData: data,
trainData: snapshot.data!,
refresh: refresh,
isRefreshing: trainDataAsync.isRefreshing,
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
onViewYesterdayTrain: onViewYesterdayTrain,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}
},
error: (e, st) {
return TrainInfoError(
title: '$trainNumber - Error',
error: e,
refresh: refresh,
);
},
loading: () {
return TrainInfoLoading(
title: trainNumber.toString(),
loadingText: "Se încarcă...",
);
},
);
}
}
@ -169,9 +132,7 @@ abstract class TrainInfoLoadingShared extends StatelessWidget {
required this.title,
String? loadingText,
super.key,
}) : loadingWidget = Loading(
text: loadingText,
);
}) : loadingWidget = Loading(text: loadingText,);
}
class TrainInfoError extends ConsumerWidget {
@ -220,12 +181,7 @@ abstract class TrainInfoErrorShared extends StatelessWidget {
final Object error;
final Future Function()? refresh;
const TrainInfoErrorShared({
required this.title,
required this.error,
this.refresh,
super.key,
});
const TrainInfoErrorShared({required this.title, required this.error, this.refresh, super.key,});
}
class TrainInfoBody extends ConsumerWidget {
@ -290,15 +246,10 @@ abstract class TrainInfoBodyShared extends StatelessWidget {
}
abstract class DisplayTrainYesterdayWarningCommon extends StatelessWidget {
static const trainDidNotDepart =
'Acest tren nu a plecat încă din prima gară.';
static const seeYesterdayTrain =
'Apasă aici pentru a vedea trenul care a plecat ieri.';
static const trainDidNotDepart = 'Acest tren nu a plecat încă din prima gară.';
static const seeYesterdayTrain = 'Apasă aici pentru a vedea trenul care a plecat ieri.';
final void Function() onViewYesterdayTrain;
const DisplayTrainYesterdayWarningCommon(
this.onViewYesterdayTrain, {
super.key,
});
const DisplayTrainYesterdayWarningCommon(this.onViewYesterdayTrain, {super.key,});
}

23
lib/pages/train_info_page/view_train/train_info_cupertino.dart

@ -4,7 +4,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:info_tren/components/cupertino_divider.dart';
import 'package:info_tren/components/sliver_persistent_header_padding.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/train_info_page/train_info_constants.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
@ -215,9 +214,7 @@ class TrainInfoBodyCupertino extends TrainInfoBodyShared {
return Row(
mainAxisSize: MainAxisSize.max,
children: [
SafeArea(
right: false,
child: Container(
Container(
constraints: const BoxConstraints(
minWidth: 400,
maxWidth: 400,
@ -272,7 +269,6 @@ class TrainInfoBodyCupertino extends TrainInfoBodyShared {
],
),
),
),
Expanded(
child: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
@ -563,7 +559,18 @@ class DisplayTrainID extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text.rich(
trainIdSpan(rank: trainData.rank, number: trainData.number),
TextSpan(
children: [
TextSpan(
text: trainData.rank,
style: TextStyle(
color: trainData.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
),
),
const TextSpan(text: ' '),
TextSpan(text: trainData.number,),
],
),
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
),
),
@ -1051,8 +1058,8 @@ class DisplayTrainYesterdayWarningCupertino extends DisplayTrainYesterdayWarning
const TextSpan(text: '\n'),
TextSpan(
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
style: TextStyle(
color: CupertinoTheme.of(context).primaryColor,
style: const TextStyle(
color: CupertinoColors.link,
),
recognizer: TapGestureRecognizer()
..onTap = onViewYesterdayTrain,

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

@ -1,18 +1,11 @@
import 'package:flutter/cupertino.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/badge/badge.dart';
import 'package:info_tren/components/cupertino_divider.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/providers.dart';
class DisplayTrainStation extends StatelessWidget {
final TrainDataStation station;
final Station station;
const DisplayTrainStation({
required this.station,
super.key,
});
const DisplayTrainStation({required this.station, super.key,});
@override
Widget build(BuildContext context) {
@ -20,26 +13,6 @@ class DisplayTrainStation extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
if (station.notes.whereType<TrainDataNoteDepartsAs>().isNotEmpty) ...[
Builder(
builder: (context) {
final note =
station.notes.whereType<TrainDataNoteDepartsAs>().first;
return Padding(
padding: const EdgeInsets.all(2.0),
child: Text.rich(
TextSpan(
children: [
const TextSpan(text: 'Trenul pleacă cu numărul '),
trainIdSpan(rank: note.rank, number: note.number),
],
),
),
);
},
),
const CupertinoDivider(),
],
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
@ -47,7 +20,8 @@ class DisplayTrainStation extends StatelessWidget {
flex: 1,
child: Align(
alignment: Alignment.centerLeft,
child: Builder(builder: (context) {
child: Builder(
builder: (context) {
final departureStatus = station.departure?.status;
final arrivalStatus = station.arrival?.status;
int delay;
@ -55,10 +29,12 @@ class DisplayTrainStation extends StatelessWidget {
if (departureStatus == null) {
delay = arrivalStatus?.delay ?? 0;
real = arrivalStatus?.real ?? false;
} else if (arrivalStatus == null) {
}
else if (arrivalStatus == null) {
delay = departureStatus.delay;
real = departureStatus.real;
} else {
}
else {
delay = departureStatus.delay;
real = departureStatus.real;
if (!real && arrivalStatus.real) {
@ -78,7 +54,8 @@ class DisplayTrainStation extends StatelessWidget {
isDelayed: isDelayed,
isOnTime: isOnTime,
);
}),
}
),
),
),
Title(
@ -88,9 +65,7 @@ class DisplayTrainStation extends StatelessWidget {
flex: 1,
child: Align(
alignment: Alignment.centerRight,
child: station.platform == null
? Container()
: Badge(text: station.platform!, caption: 'linia'),
child: station.platform == null ? Container() : Badge(text: station.platform!, caption: 'linia'),
),
),
],
@ -98,61 +73,16 @@ class DisplayTrainStation extends StatelessWidget {
Time(
station: station,
),
if (station.notes.whereType<TrainDataNoteDetachingWagons>().isNotEmpty)
Builder(
builder: (context) {
final note =
station.notes.whereType<TrainDataNoteDetachingWagons>().first;
return Text(
'Trenul detașează vagoane către ${note.station}',
textAlign: TextAlign.center,
);
},
),
if (station.notes.whereType<TrainDataNoteReceivingWagons>().isNotEmpty)
Builder(
builder: (context) {
final note =
station.notes.whereType<TrainDataNoteReceivingWagons>().first;
return Text(
'Trenul primește vagoane de la ${note.station}',
textAlign: TextAlign.center,
);
},
),
Delay(
station: station,
),
if (station.notes
.whereType<TrainDataNoteTrainNumberChange>()
.isNotEmpty) ...[
const CupertinoDivider(),
Builder(
builder: (context) {
final note = station.notes
.whereType<TrainDataNoteTrainNumberChange>()
.first;
return Padding(
padding: const EdgeInsets.all(2.0),
child: Text.rich(
TextSpan(
children: [
const TextSpan(text: 'Trenul își schimbă numărul în '),
trainIdSpan(rank: note.rank, number: note.number),
],
),
),
);
},
),
],
],
);
}
}
class Title extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Title({
required this.station,
@ -165,9 +95,7 @@ class Title extends StatelessWidget {
station.name,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 22,
fontWeight: MediaQuery.of(context).boldText
? FontWeight.w500
: FontWeight.w300,
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
),
textAlign: TextAlign.center,
@ -176,7 +104,7 @@ class Title extends StatelessWidget {
}
class Time extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Time({
required this.station,
@ -210,27 +138,13 @@ class Time extends StatelessWidget {
fontSize: 22,
),
),
Container(
width: 2,
),
ArrivalTime(
station: station,
),
Expanded(
child: Container(),
),
StopTime(
station: station,
),
Expanded(
child: Container(),
),
DepartureTime(
station: station,
),
Container(
width: 2,
),
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(
@ -242,8 +156,8 @@ class Time extends StatelessWidget {
}
}
class ArrivalTime extends ConsumerWidget {
final TrainDataStation station;
class ArrivalTime extends StatelessWidget {
final Station station;
final bool finalStation;
const ArrivalTime({
@ -253,9 +167,7 @@ class ArrivalTime extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tz = ref.watch(uiTimeZoneProvider);
Widget build(BuildContext context) {
if (finalStation) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
@ -266,26 +178,21 @@ class ArrivalTime extends ConsumerWidget {
fontSize: 22,
),
),
Container(
width: 2,
),
Container(width: 2,),
const Text("sosire la "),
ArrivalTime(
station: station,
),
Expanded(
child: Container(),
),
ArrivalTime(station: station,),
Expanded(child: Container(),),
],
);
} else {
}
else {
final delay = station.arrival!.status?.delay ?? 0;
final time = tz.convertDateTime(station.arrival!.scheduleTime);
final time = station.arrival!.scheduleTime.toLocal();
if (delay == 0) {
return Text(
"${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
} else if (delay > 0) {
return Text("${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
}
else if (delay > 0) {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -306,7 +213,8 @@ class ArrivalTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -333,7 +241,7 @@ class ArrivalTime extends ConsumerWidget {
}
class StopTime extends StatelessWidget {
final TrainDataStation station;
final Station station;
const StopTime({
required this.station,
@ -363,12 +271,14 @@ class StopTime extends StatelessWidget {
minutes ? '1 minut' : '1 secundă',
textAlign: TextAlign.center,
);
} else if (stopsForInt < 20) {
}
else if (stopsForInt < 20) {
return Text(
'$stopsForInt ${minutes ? 'minute' : 'seconde'}',
textAlign: TextAlign.center,
);
} else {
}
else {
return Text(
'$stopsForInt de ${minutes ? 'minute' : 'secunde'}',
textAlign: TextAlign.center,
@ -381,8 +291,8 @@ class StopTime extends StatelessWidget {
}
}
class DepartureTime extends ConsumerWidget {
final TrainDataStation station;
class DepartureTime extends StatelessWidget {
final Station station;
final bool firstStation;
const DepartureTime({
@ -392,22 +302,15 @@ class DepartureTime extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tz = ref.watch(uiTimeZoneProvider);
Widget build(BuildContext context) {
if (firstStation) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(),
),
Expanded(child: Container(),),
const Text("plecare la "),
DepartureTime(
station: station,
),
Container(
width: 2,
),
DepartureTime(station: station,),
Container(width: 2,),
Text(
"",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
@ -416,14 +319,15 @@ class DepartureTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final delay = station.departure!.status?.delay ?? 0;
final time = tz.convertDateTime(station.departure!.scheduleTime);
final time = station.departure!.scheduleTime.toLocal();
if (delay == 0) {
return Text(
"${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
} else if (delay > 0) {
return Text("${time.hour.toString().padLeft(2, "0")}:${time.minute.toString().padLeft(2, "0")}");
}
else if (delay > 0) {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -444,7 +348,8 @@ class DepartureTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -471,7 +376,7 @@ class DepartureTime extends ConsumerWidget {
}
class Delay extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Delay({
required this.station,
@ -499,7 +404,8 @@ class Delay extends StatelessWidget {
fontStyle: FontStyle.italic,
),
);
} else if (delay < 0) {
}
else if (delay < 0) {
return Text(
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(

20
lib/pages/train_info_page/view_train/train_info_fluent.dart

@ -1,6 +1,5 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/gestures.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
@ -304,7 +303,22 @@ class DisplayTrainID extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text.rich(
trainIdSpan(rank: trainData.rank, number: trainData.number),
TextSpan(
children: [
TextSpan(
text: trainData.rank,
style: TextStyle(
color: trainData.rank.startsWith('IR')
? const Color.fromARGB(255, 255, 0, 0)
: null,
),
),
const TextSpan(text: ' '),
TextSpan(
text: trainData.number,
),
],
),
style: FluentTheme.of(context).typography.title?.copyWith(
color: FluentTheme.of(context).typography.body?.color,
fontWeight: FontWeight.bold,
@ -748,7 +762,7 @@ class DisplayTrainYesterdayWarningFluent
TextSpan(
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
style: TextStyle(
color: FluentTheme.of(context).accentColor,// Colors.blue,
color: Colors.blue,
),
recognizer: TapGestureRecognizer()
..onTap = onViewYesterdayTrain,

216
lib/pages/train_info_page/view_train/train_info_fluent_DisplayTrainStation.dart

@ -1,44 +1,16 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/badge/badge.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/providers.dart';
class DisplayTrainStation extends StatelessWidget {
final TrainDataStation station;
final Station station;
final void Function()? onTap;
const DisplayTrainStation({
required this.station,
this.onTap,
super.key,
});
const DisplayTrainStation({required this.station, this.onTap, super.key,});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
if (station.notes.whereType<TrainDataNoteDepartsAs>().isNotEmpty)
Builder(
builder: (context) {
final note =
station.notes.whereType<TrainDataNoteDepartsAs>().first;
return Padding(
padding: const EdgeInsets.all(2.0),
child: Text.rich(
TextSpan(
children: [
const TextSpan(text: 'Trenul pleacă cu numărul '),
trainIdSpan(rank: note.rank, number: note.number),
],
),
),
);
},
),
Padding(
padding: const EdgeInsets.all(2),
child: HoverButton(
onPressed: onTap,
@ -56,7 +28,8 @@ class DisplayTrainStation extends StatelessWidget {
flex: 1,
child: Align(
alignment: Alignment.centerLeft,
child: Builder(builder: (context) {
child: Builder(
builder: (context) {
final departureStatus = station.departure?.status;
final arrivalStatus = station.arrival?.status;
int delay;
@ -64,10 +37,12 @@ class DisplayTrainStation extends StatelessWidget {
if (departureStatus == null) {
delay = arrivalStatus?.delay ?? 0;
real = arrivalStatus?.real ?? false;
} else if (arrivalStatus == null) {
}
else if (arrivalStatus == null) {
delay = departureStatus.delay;
real = departureStatus.real;
} else {
}
else {
delay = departureStatus.delay;
real = departureStatus.real;
if (!real && arrivalStatus.real) {
@ -87,7 +62,8 @@ class DisplayTrainStation extends StatelessWidget {
isDelayed: isDelayed,
isOnTime: isOnTime,
);
}),
}
),
),
),
Title(
@ -99,10 +75,7 @@ class DisplayTrainStation extends StatelessWidget {
? Container()
: Align(
alignment: Alignment.centerRight,
child: Badge(
text: station.platform!,
caption: 'linia',
),
child: Badge(text: station.platform!, caption: 'linia',),
),
),
],
@ -110,34 +83,6 @@ class DisplayTrainStation extends StatelessWidget {
Time(
station: station,
),
if (station.notes
.whereType<TrainDataNoteDetachingWagons>()
.isNotEmpty)
Builder(
builder: (context) {
final note = station.notes
.whereType<TrainDataNoteDetachingWagons>()
.first;
return Text(
'Trenul detașează vagoane către ${note.station}',
textAlign: TextAlign.center,
);
},
),
if (station.notes
.whereType<TrainDataNoteReceivingWagons>()
.isNotEmpty)
Builder(
builder: (context) {
final note = station.notes
.whereType<TrainDataNoteReceivingWagons>()
.first;
return Text(
'Trenul primește vagoane de la ${note.station}',
textAlign: TextAlign.center,
);
},
),
Delay(
station: station,
),
@ -146,35 +91,12 @@ class DisplayTrainStation extends StatelessWidget {
);
},
),
),
if (station.notes
.whereType<TrainDataNoteTrainNumberChange>()
.isNotEmpty)
Builder(
builder: (context) {
final note = station.notes
.whereType<TrainDataNoteTrainNumberChange>()
.first;
return Padding(
padding: const EdgeInsets.all(2.0),
child: Text.rich(
TextSpan(
children: [
const TextSpan(text: 'Trenul își schimbă numărul în '),
trainIdSpan(rank: note.rank, number: note.number),
],
),
),
);
},
),
],
);
}
}
class Title extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Title({
required this.station,
@ -187,9 +109,7 @@ class Title extends StatelessWidget {
station.name,
style: FluentTheme.of(context).typography.body?.copyWith(
fontSize: 22,
fontWeight: MediaQuery.of(context).boldText
? FontWeight.w500
: FontWeight.w300,
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
),
textAlign: TextAlign.center,
@ -198,7 +118,7 @@ class Title extends StatelessWidget {
}
class Time extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Time({
required this.station,
@ -232,27 +152,13 @@ class Time extends StatelessWidget {
fontSize: 22,
),
),
Container(
width: 2,
),
ArrivalTime(
station: station,
),
Expanded(
child: Container(),
),
StopTime(
station: station,
),
Expanded(
child: Container(),
),
DepartureTime(
station: station,
),
Container(
width: 2,
),
Container(width: 2,),
ArrivalTime(station: station,),
Expanded(child: Container(),),
StopTime(station: station,),
Expanded(child: Container(),),
DepartureTime(station: station,),
Container(width: 2,),
Text(
"",
style: FluentTheme.of(context).typography.body?.copyWith(
@ -264,8 +170,8 @@ class Time extends StatelessWidget {
}
}
class ArrivalTime extends ConsumerWidget {
final TrainDataStation station;
class ArrivalTime extends StatelessWidget {
final Station station;
final bool finalStation;
const ArrivalTime({
@ -275,8 +181,7 @@ class ArrivalTime extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tz = ref.watch(uiTimeZoneProvider);
Widget build(BuildContext context) {
if (station.arrival == null) {
return Container();
}
@ -290,26 +195,21 @@ class ArrivalTime extends ConsumerWidget {
fontSize: 22,
),
),
Container(
width: 2,
),
Container(width: 2,),
const Text("sosire la "),
ArrivalTime(
station: station,
),
Expanded(
child: Container(),
),
ArrivalTime(station: station,),
Expanded(child: Container(),),
],
);
} else {
}
else {
final delay = station.arrival!.status?.delay ?? 0;
final time = tz.convertDateTime(station.arrival!.scheduleTime);
final time = station.arrival!.scheduleTime.toLocal();
if (delay == 0) {
return Text(
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
} else if (delay > 0) {
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
}
else if (delay > 0) {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -331,7 +231,8 @@ class ArrivalTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -359,7 +260,7 @@ class ArrivalTime extends ConsumerWidget {
}
class StopTime extends StatelessWidget {
final TrainDataStation station;
final Station station;
const StopTime({
required this.station,
@ -388,12 +289,14 @@ class StopTime extends StatelessWidget {
"1 ${minutes ? 'minut' : 'secundă'}",
textAlign: TextAlign.center,
);
} else if (stopsForInt < 20) {
}
else if (stopsForInt < 20) {
return Text(
"$stopsForInt ${minutes ? 'minute' : 'secunde'}",
textAlign: TextAlign.center,
);
} else {
}
else {
return Text(
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}",
textAlign: TextAlign.center,
@ -406,8 +309,8 @@ class StopTime extends StatelessWidget {
}
}
class DepartureTime extends ConsumerWidget {
final TrainDataStation station;
class DepartureTime extends StatelessWidget {
final Station station;
final bool firstStation;
const DepartureTime({
@ -417,8 +320,7 @@ class DepartureTime extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tz = ref.watch(uiTimeZoneProvider);
Widget build(BuildContext context) {
if (station.departure == null) {
return Container();
}
@ -426,16 +328,10 @@ class DepartureTime extends ConsumerWidget {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(),
),
Expanded(child: Container(),),
const Text("plecare la "),
DepartureTime(
station: station,
),
Container(
width: 2,
),
DepartureTime(station: station,),
Container(width: 2,),
Text(
"",
style: FluentTheme.of(context).typography.body?.copyWith(
@ -444,14 +340,15 @@ class DepartureTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final delay = station.departure!.status?.delay ?? 0;
final time = tz.convertDateTime(station.departure!.scheduleTime);
final time = station.departure!.scheduleTime.toLocal();
if (delay == 0) {
return Text(
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
} else if (delay > 0) {
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
}
else if (delay > 0) {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -473,7 +370,8 @@ class DepartureTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -500,8 +398,9 @@ class DepartureTime extends ConsumerWidget {
}
}
class Delay extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Delay({
required this.station,
@ -530,7 +429,8 @@ class Delay extends StatelessWidget {
fontStyle: FontStyle.italic,
),
);
} else if (delay < 0) {
}
else if (delay < 0) {
return Text(
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
style: FluentTheme.of(context).typography.body?.copyWith(

115
lib/pages/train_info_page/view_train/train_info_material.dart

@ -1,7 +1,6 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:info_tren/components/slim_app_bar.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
@ -9,11 +8,7 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info_material_D
import 'package:info_tren/utils/state_to_string.dart';
class TrainInfoLoadingMaterial extends TrainInfoLoadingShared {
TrainInfoLoadingMaterial({
required super.title,
super.loadingText,
super.key,
});
TrainInfoLoadingMaterial({required super.title, super.loadingText, super.key,});
@override
Widget build(BuildContext context) {
@ -85,29 +80,8 @@ class TrainInfoMaterial extends TrainInfoShared {
: AppBar(
centerTitle: true,
title: Text(
'Informații despre ${trainData.rank} ${trainData.number}',
"Informații despre ${trainData.rank} ${trainData.number}",
),
actions: [
IconButton(
tooltip: 'Reîncarcă',
icon: (isRefreshing ?? false)
? const Center(
child: SizedBox(
height: 16,
width: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
)
: const Icon(Icons.refresh),
onPressed: (isRefreshing ?? false)
? null
: () {
refresh?.call();
},
),
],
),
body: Column(
children: <Widget>[
@ -197,7 +171,8 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
if (onViewYesterdayTrain != null &&
trainData.stations.first.departure!.scheduleTime
.compareTo(DateTime.now()) >
0) ...[
0)
...[
DisplayTrainYesterdayWarningMaterial(
onViewYesterdayTrain!,
),
@ -217,7 +192,10 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
),
SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).viewPadding.bottom,
height: MediaQuery
.of(context)
.viewPadding
.bottom,
),
),
],
@ -225,7 +203,8 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
),
],
);
} else {
}
else {
return CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
@ -304,8 +283,8 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
.compareTo(DateTime.now()) >
0) ...[
SliverToBoxAdapter(
child:
DisplayTrainYesterdayWarningMaterial(onViewYesterdayTrain!),
child: DisplayTrainYesterdayWarningMaterial(
onViewYesterdayTrain!),
),
SliverToBoxAdapter(
child: Divider(
@ -319,7 +298,10 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
),
SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).viewPadding.bottom,
height: MediaQuery
.of(context)
.viewPadding
.bottom,
),
),
],
@ -331,15 +313,27 @@ class TrainInfoBodyMaterial extends TrainInfoBodyShared {
class DisplayTrainID extends StatelessWidget {
final TrainData trainData;
const DisplayTrainID({
required this.trainData,
super.key,
});
const DisplayTrainID({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
return Text.rich(
trainIdSpan(rank: trainData.rank, number: trainData.number),
TextSpan(
children: [
TextSpan(
text: trainData.rank,
style: TextStyle(
color: trainData.rank.startsWith('IR')
? const Color.fromARGB(255, 255, 0, 0)
: null,
),
),
const TextSpan(text: ' '),
TextSpan(
text: trainData.number,
),
],
),
style: (isSmallScreen(context)
? Theme.of(context).textTheme.headlineMedium
: Theme.of(context).textTheme.displaySmall)
@ -355,10 +349,7 @@ class DisplayTrainID extends StatelessWidget {
class DisplayTrainOperator extends StatelessWidget {
final TrainData trainData;
const DisplayTrainOperator({
required this.trainData,
super.key,
});
const DisplayTrainOperator({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
@ -376,10 +367,7 @@ class DisplayTrainOperator extends StatelessWidget {
class DisplayTrainRoute extends StatelessWidget {
final TrainData trainData;
const DisplayTrainRoute({
required this.trainData,
super.key,
});
const DisplayTrainRoute({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
@ -444,10 +432,7 @@ class DisplayTrainDeparture extends StatelessWidget {
class DisplayTrainLastInfo extends StatelessWidget {
final TrainData trainData;
const DisplayTrainLastInfo({
required this.trainData,
super.key,
});
const DisplayTrainLastInfo({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
@ -639,10 +624,7 @@ class DisplayTrainLastInfo extends StatelessWidget {
class DisplayTrainDestination extends StatelessWidget {
final TrainData trainData;
const DisplayTrainDestination({
required this.trainData,
super.key,
});
const DisplayTrainDestination({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
@ -746,10 +728,7 @@ class DisplayTrainDestination extends StatelessWidget {
class DisplayTrainRouteDistance extends StatelessWidget {
final TrainData trainData;
const DisplayTrainRouteDistance({
required this.trainData,
super.key,
});
const DisplayTrainRouteDistance({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
@ -786,10 +765,7 @@ class DisplayTrainRouteDistance extends StatelessWidget {
class DisplayTrainRouteDuration extends StatelessWidget {
final TrainData trainData;
const DisplayTrainRouteDuration({
required this.trainData,
super.key,
});
const DisplayTrainRouteDuration({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
@ -872,10 +848,7 @@ class DisplayTrainRouteDuration extends StatelessWidget {
class DisplayTrainYesterdayWarningMaterial
extends DisplayTrainYesterdayWarningCommon {
const DisplayTrainYesterdayWarningMaterial(
super.onViewYesterdayTrain, {
super.key,
});
const DisplayTrainYesterdayWarningMaterial(super.onViewYesterdayTrain, {super.key,});
@override
Widget build(BuildContext context) {
@ -911,10 +884,7 @@ class DisplayTrainYesterdayWarningMaterial
class DisplayTrainStations extends StatelessWidget {
final TrainData trainData;
const DisplayTrainStations({
required this.trainData,
super.key,
});
const DisplayTrainStations({required this.trainData, super.key,});
@override
Widget build(BuildContext context) {
@ -928,8 +898,7 @@ class DisplayTrainStations extends StatelessWidget {
onTap: () {
Navigator.of(context).pushNamed(
ViewStationPage.routeName,
arguments: ViewStationArguments(
stationName: trainData.stations[index].name),
arguments: ViewStationArguments(stationName: trainData.stations[index].name),
);
},
),

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

@ -1,45 +1,17 @@
import 'package:flutter/material.dart' hide Badge;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/train_id_text_span.dart';
import 'package:flutter/material.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/components/badge/badge.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
import 'package:info_tren/providers.dart';
class DisplayTrainStation extends StatelessWidget {
final TrainDataStation station;
final Station station;
final void Function()? onTap;
const DisplayTrainStation({
required this.station,
this.onTap,
super.key,
});
const DisplayTrainStation({required this.station, this.onTap, super.key,});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
if (station.notes.whereType<TrainDataNoteDepartsAs>().isNotEmpty)
Builder(
builder: (context) {
final note =
station.notes.whereType<TrainDataNoteDepartsAs>().first;
return Padding(
padding: const EdgeInsets.all(2.0),
child: Text.rich(
TextSpan(
children: [
const TextSpan(text: 'Trenul pleacă cu numărul '),
trainIdSpan(rank: note.rank, number: note.number),
],
),
),
);
},
),
Card(
return Card(
child: InkWell(
onTap: onTap,
child: Padding(
@ -55,7 +27,8 @@ class DisplayTrainStation extends StatelessWidget {
flex: 1,
child: Align(
alignment: Alignment.centerLeft,
child: Builder(builder: (context) {
child: Builder(
builder: (context) {
final departureStatus = station.departure?.status;
final arrivalStatus = station.arrival?.status;
int delay;
@ -63,10 +36,12 @@ class DisplayTrainStation extends StatelessWidget {
if (departureStatus == null) {
delay = arrivalStatus?.delay ?? 0;
real = arrivalStatus?.real ?? false;
} else if (arrivalStatus == null) {
}
else if (arrivalStatus == null) {
delay = departureStatus.delay;
real = departureStatus.real;
} else {
}
else {
delay = departureStatus.delay;
real = departureStatus.real;
if (!real && arrivalStatus.real) {
@ -86,7 +61,8 @@ class DisplayTrainStation extends StatelessWidget {
isDelayed: isDelayed,
isOnTime: isOnTime,
);
}),
}
),
),
),
Title(
@ -98,10 +74,7 @@ class DisplayTrainStation extends StatelessWidget {
? Container()
: Align(
alignment: Alignment.centerRight,
child: Badge(
text: station.platform!,
caption: 'linia',
),
child: Badge(text: station.platform!, caption: 'linia',),
),
),
],
@ -109,34 +82,6 @@ class DisplayTrainStation extends StatelessWidget {
Time(
station: station,
),
if (station.notes
.whereType<TrainDataNoteDetachingWagons>()
.isNotEmpty)
Builder(
builder: (context) {
final note = station.notes
.whereType<TrainDataNoteDetachingWagons>()
.first;
return Text(
'Trenul detașează vagoane către ${note.station}',
textAlign: TextAlign.center,
);
},
),
if (station.notes
.whereType<TrainDataNoteReceivingWagons>()
.isNotEmpty)
Builder(
builder: (context) {
final note = station.notes
.whereType<TrainDataNoteReceivingWagons>()
.first;
return Text(
'Trenul primește vagoane de la ${note.station}',
textAlign: TextAlign.center,
);
},
),
Delay(
station: station,
),
@ -144,35 +89,12 @@ class DisplayTrainStation extends StatelessWidget {
),
),
),
),
if (station.notes
.whereType<TrainDataNoteTrainNumberChange>()
.isNotEmpty)
Builder(
builder: (context) {
final note = station.notes
.whereType<TrainDataNoteTrainNumberChange>()
.first;
return Padding(
padding: const EdgeInsets.all(2.0),
child: Text.rich(
TextSpan(
children: [
const TextSpan(text: 'Trenul își schimbă numărul în '),
trainIdSpan(rank: note.rank, number: note.number),
],
),
),
);
},
),
],
);
}
}
class Title extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Title({
required this.station,
@ -185,9 +107,7 @@ class Title extends StatelessWidget {
station.name,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontSize: isSmallScreen(context) ? 18 : 22,
fontWeight: MediaQuery.of(context).boldText
? FontWeight.w500
: FontWeight.w300,
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
),
textAlign: TextAlign.center,
@ -196,7 +116,7 @@ class Title extends StatelessWidget {
}
class Time extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Time({
required this.station,
@ -230,27 +150,13 @@ class Time extends StatelessWidget {
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,
),
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.bodyMedium?.copyWith(
@ -262,8 +168,8 @@ class Time extends StatelessWidget {
}
}
class ArrivalTime extends ConsumerWidget {
final TrainDataStation station;
class ArrivalTime extends StatelessWidget {
final Station station;
final bool finalStation;
const ArrivalTime({
@ -273,8 +179,7 @@ class ArrivalTime extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tz = ref.watch(uiTimeZoneProvider);
Widget build(BuildContext context) {
if (station.arrival == null) {
return Container();
}
@ -288,26 +193,21 @@ class ArrivalTime extends ConsumerWidget {
fontSize: isSmallScreen(context) ? 18 : 22,
),
),
Container(
width: 2,
),
Container(width: 2,),
const Text("sosire la "),
ArrivalTime(
station: station,
),
Expanded(
child: Container(),
),
ArrivalTime(station: station,),
Expanded(child: Container(),),
],
);
} else {
}
else {
final delay = station.arrival!.status?.delay ?? 0;
final time = tz.convertDateTime(station.arrival!.scheduleTime);
final time = station.arrival!.scheduleTime.toLocal();
if (delay == 0) {
return Text(
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
} else if (delay > 0) {
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
}
else if (delay > 0) {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -328,7 +228,8 @@ class ArrivalTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -355,7 +256,7 @@ class ArrivalTime extends ConsumerWidget {
}
class StopTime extends StatelessWidget {
final TrainDataStation station;
final Station station;
const StopTime({
required this.station,
@ -384,12 +285,14 @@ class StopTime extends StatelessWidget {
"1 ${minutes ? 'minut' : 'secundă'}",
textAlign: TextAlign.center,
);
} else if (stopsForInt < 20) {
}
else if (stopsForInt < 20) {
return Text(
"$stopsForInt ${minutes ? 'minute' : 'secunde'}",
textAlign: TextAlign.center,
);
} else {
}
else {
return Text(
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}",
textAlign: TextAlign.center,
@ -402,8 +305,8 @@ class StopTime extends StatelessWidget {
}
}
class DepartureTime extends ConsumerWidget {
final TrainDataStation station;
class DepartureTime extends StatelessWidget {
final Station station;
final bool firstStation;
const DepartureTime({
@ -413,8 +316,7 @@ class DepartureTime extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tz = ref.watch(uiTimeZoneProvider);
Widget build(BuildContext context) {
if (station.departure == null) {
return Container();
}
@ -422,16 +324,10 @@ class DepartureTime extends ConsumerWidget {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(),
),
Expanded(child: Container(),),
const Text("plecare la "),
DepartureTime(
station: station,
),
Container(
width: 2,
),
DepartureTime(station: station,),
Container(width: 2,),
Text(
"",
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
@ -440,14 +336,15 @@ class DepartureTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final delay = station.departure!.status?.delay ?? 0;
final time = tz.convertDateTime(station.departure!.scheduleTime);
final time = station.departure!.scheduleTime.toLocal();
if (delay == 0) {
return Text(
"${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
} else if (delay > 0) {
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
}
else if (delay > 0) {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -468,7 +365,8 @@ class DepartureTime extends ConsumerWidget {
),
],
);
} else {
}
else {
final oldDate = time;
final newDate = oldDate.add(Duration(minutes: delay));
@ -494,8 +392,9 @@ class DepartureTime extends ConsumerWidget {
}
}
class Delay extends StatelessWidget {
final TrainDataStation station;
final Station station;
const Delay({
required this.station,
@ -523,7 +422,8 @@ class Delay extends StatelessWidget {
fontStyle: FontStyle.italic,
),
);
} else if (delay < 0) {
}
else if (delay < 0) {
return Text(
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
style: Theme.of(context).textTheme.bodyMedium?.copyWith(

47
lib/providers.dart

@ -1,19 +1,14 @@
import 'dart:async';
import 'dart:developer';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/api/station_data.dart';
import 'package:info_tren/api/train_data.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/utils/default_ui_design.dart';
import 'package:info_tren/utils/iterable_extensions.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shared_preferences/shared_preferences.dart';
part 'providers.g.dart';
final sharedPreferencesProvider = Provider<SharedPreferences>(
(_) => throw UnimplementedError('Please override in ProviderScope'),
);
@ -41,47 +36,14 @@ final uiDesignProvider = StateNotifierProvider<UiDesignNotifier, UiDesign>(
(ref) => UiDesignNotifier(
sharedPreferences: ref.watch(sharedPreferencesProvider),
),
dependencies: [sharedPreferencesProvider],
);
class UiTimeZoneNotifier extends StateNotifier<UiTimeZone> {
final SharedPreferences sharedPreferences;
UiTimeZoneNotifier({required this.sharedPreferences,}) : super(const RoUiTimeZone()) {
final stored = sharedPreferences.getString('uiTimeZone');
if (stored != null) {
try {
state = UiTimeZone.fromSerString(stored);
}
catch (e) {
log('Invalid UiTimeZone ser: $stored, error: $e', level: 1000);
}
}
}
void set(UiTimeZone? timeZone) async {
if (timeZone != null) {
await sharedPreferences.setString('uiTimeZone', timeZone.toSerString());
}
else {
await sharedPreferences.remove('uiTimeZone');
}
state = timeZone ?? const LocalUiTimeZone();
}
}
final uiTimeZoneProvider = StateNotifierProvider<UiTimeZoneNotifier, UiTimeZone>(
(ref) => UiTimeZoneNotifier(
sharedPreferences: ref.watch(sharedPreferencesProvider),
),
dependencies: [sharedPreferencesProvider],
);
final trainInfoArgumentsProvider = Provider<TrainInfoArguments>(
(_) => throw UnimplementedError('Please override in ProviderScope'),
);
final stationDataProvider = FutureProvider.family((ref, ViewStationArguments args) async {
final data = await getStationData(args.stationName, args.date);
final stationDataProvider = FutureProvider.family((ref, String stationName) async {
final data = await getStationData(stationName);
final timer = Timer(const Duration(minutes: 2), () {
ref.invalidateSelf();
@ -97,9 +59,6 @@ final viewStationArgumentsProvider = Provider<ViewStationArguments>(
);
final viewStationDataProvider = Provider((ref) {
final args = ref.watch(viewStationArgumentsProvider);
final data = ref.watch(stationDataProvider(args));
final data = ref.watch(stationDataProvider(args.stationName));
return data;
}, dependencies: [viewStationArgumentsProvider, stationDataProvider]);
@Riverpod(keepAlive: true)
Future<TrainData> trainInfo(TrainInfoRef ref, {required String trainNumber, DateTime? date}) => getTrain(trainNumber, date: date);

120
lib/providers.g.dart

@ -1,120 +0,0 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'providers.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$trainInfoHash() => r'd25aabc3ba656acf6497ec6831e11892178b22c9';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
typedef TrainInfoRef = FutureProviderRef<TrainData>;
/// See also [trainInfo].
@ProviderFor(trainInfo)
const trainInfoProvider = TrainInfoFamily();
/// See also [trainInfo].
class TrainInfoFamily extends Family<AsyncValue<TrainData>> {
/// See also [trainInfo].
const TrainInfoFamily();
/// See also [trainInfo].
TrainInfoProvider call({
required String trainNumber,
DateTime? date,
}) {
return TrainInfoProvider(
trainNumber: trainNumber,
date: date,
);
}
@override
TrainInfoProvider getProviderOverride(
covariant TrainInfoProvider provider,
) {
return call(
trainNumber: provider.trainNumber,
date: provider.date,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'trainInfoProvider';
}
/// See also [trainInfo].
class TrainInfoProvider extends FutureProvider<TrainData> {
/// See also [trainInfo].
TrainInfoProvider({
required this.trainNumber,
this.date,
}) : super.internal(
(ref) => trainInfo(
ref,
trainNumber: trainNumber,
date: date,
),
from: trainInfoProvider,
name: r'trainInfoProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$trainInfoHash,
dependencies: TrainInfoFamily._dependencies,
allTransitiveDependencies: TrainInfoFamily._allTransitiveDependencies,
);
final String trainNumber;
final DateTime? date;
@override
bool operator ==(Object other) {
return other is TrainInfoProvider &&
other.trainNumber == trainNumber &&
other.date == date;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, trainNumber.hashCode);
hash = _SystemHash.combine(hash, date.hashCode);
return _SystemHash.finish(hash);
}
}
// ignore_for_file: unnecessary_raw_strings, subtype_of_sealed_class, invalid_use_of_internal_member, do_not_use_environment, prefer_const_constructors, public_member_api_docs, avoid_private_typedef_functions

15
lib/utils/state_to_string.dart

@ -1,7 +1,12 @@
import 'package:info_tren/models.dart';
String stateToString(TrainDataState state) => switch (state) {
TrainDataState.passing => 'trecere fără oprire',
TrainDataState.arrival => 'sosire',
TrainDataState.departure => 'plecare',
};
String stateToString(TrainDataState state) {
switch(state) {
case TrainDataState.PASSING:
return 'trecere fără oprire';
case TrainDataState.ARRIVAL:
return 'sosire';
case TrainDataState.DEPARTURE:
return 'plecare';
}
}

4
linux/flutter/generated_plugin_registrant.cc

@ -6,13 +6,9 @@
#include "generated_plugin_registrant.h"
#include <dynamic_color/dynamic_color_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

1
linux/flutter/generated_plugins.cmake

@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
url_launcher_linux
)

1
macos/Flutter/Flutter-Debug.xcconfig

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

1
macos/Flutter/Flutter-Release.xcconfig

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

40
macos/Podfile

@ -1,40 +0,0 @@
platform :osx, '10.14'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_macos_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end

41
macos/Podfile.lock

@ -1,41 +0,0 @@
PODS:
- dynamic_color (0.0.2):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- package_info_plus (0.0.1):
- FlutterMacOS
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
DEPENDENCIES:
- dynamic_color (from `Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
EXTERNAL SOURCES:
dynamic_color:
:path: Flutter/ephemeral/.symlinks/plugins/dynamic_color/macos
FlutterMacOS:
:path: Flutter/ephemeral
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
shared_preferences_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
SPEC CHECKSUMS:
dynamic_color: 2eaa27267de1ca20d879fbd6e01259773fb1670f
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
COCOAPODS: 1.12.1

71
macos/Runner.xcodeproj/project.pbxproj

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 51;
objects = {
/* Begin PBXAggregateTarget section */
@ -26,7 +26,6 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
520B64251BD594DD8421C698 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE5B44A3963C12914D375BF2 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -53,7 +52,6 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0796070C1CDACAE0CA888A94 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* Info Tren.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Info Tren.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -70,9 +68,6 @@
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
982BB43264C0B37351AE0773 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
BE5B44A3963C12914D375BF2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
FCA310A2438DD41F2A155FE5 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -80,7 +75,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
520B64251BD594DD8421C698 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -105,7 +99,6 @@
33CEB47122A05771004F2AC0 /* Flutter */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
EB1CD289E0F53193A2CB7A73 /* Pods */,
);
sourceTree = "<group>";
};
@ -155,22 +148,10 @@
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
BE5B44A3963C12914D375BF2 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
EB1CD289E0F53193A2CB7A73 /* Pods */ = {
isa = PBXGroup;
children = (
FCA310A2438DD41F2A155FE5 /* Pods-Runner.debug.xcconfig */,
0796070C1CDACAE0CA888A94 /* Pods-Runner.release.xcconfig */,
982BB43264C0B37351AE0773 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -178,13 +159,11 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
85962951F5B916ED962A8FBD /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
907AE34F1B4BFF5379ADD9D0 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@ -203,7 +182,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
@ -256,7 +235,6 @@
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -292,45 +270,6 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
85962951F5B916ED962A8FBD /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
907AE34F1B4BFF5379ADD9D0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -405,7 +344,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@ -484,7 +423,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@ -531,7 +470,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;

2
macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

3
macos/Runner.xcworkspace/contents.xcworkspacedata generated

@ -4,7 +4,4 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

649
pubspec.lock

File diff suppressed because it is too large Load Diff

15
pubspec.yaml

@ -11,10 +11,10 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 2.7.11
version: 2.7.10
environment:
sdk: ">=3.0.0 <4.0.0"
sdk: ">=2.17.0 <3.0.0"
dependencies:
flutter:
@ -30,23 +30,18 @@ dependencies:
url_launcher: ^6.1.5
flutter_hooks: ^0.18.5+1
hooks_riverpod: ^2.0.2
freezed_annotation: ^2.4.1
json_annotation: ^4.8.1
freezed_annotation: ^2.2.0
json_annotation: ^4.7.0
shared_preferences: ^2.0.15
fluent_ui: ^4.0.3+1
timezone: ^0.9.0
dynamic_color: ^1.6.6
riverpod_annotation: ^2.1.1
dev_dependencies:
# flutter_test:
# sdk: flutter
build_runner: ^2.1.0
json_serializable: ^6.5.4
freezed: ^2.4.1
freezed: 2.2.0
flutter_lints: ^2.0.1
riverpod_lint: ^1.3.2
riverpod_generator: ^2.2.3
# For information on the generic Dart part of this file, see the

3
windows/flutter/generated_plugin_registrant.cc

@ -6,12 +6,9 @@
#include "generated_plugin_registrant.h"
#include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
DynamicColorPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

1
windows/flutter/generated_plugins.cmake

@ -3,7 +3,6 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
url_launcher_windows
)

Loading…
Cancel
Save