Browse Source

Add cancelled trains to dep/arr board

master v2.7.8
Kenneth Bruen 2 years ago
parent
commit
50dd6c19c9
Signed by: kbruen
GPG Key ID: C1980A470C3EE5B1
  1. 5
      CHANGELOG.txt
  2. 2
      lib/api/train_data.dart
  3. 44
      lib/main.dart
  4. 13
      lib/models/station_data.dart
  5. 4
      lib/models/station_data.g.dart
  6. 19
      lib/pages/main/main_page_cupertino.dart
  7. 12
      lib/pages/station_arrdep_page/view_station/view_station.dart
  8. 4
      lib/pages/station_arrdep_page/view_station/view_station_cupertino.dart
  9. 139
      lib/pages/station_arrdep_page/view_station/view_station_material.dart
  10. 5
      lib/pages/train_info_page/select_train/select_train.dart
  11. 12
      lib/pages/train_info_page/view_train/train_info.dart
  12. 2
      pubspec.yaml

5
CHANGELOG.txt

@ -1,3 +1,8 @@
v2.7.8
Added cancelled trains in departures/arrivals board.
Selecting train in departures/arrivels board chooses appropriate departure date.
Temporarily switched all platforms to Material.
v2.7.7 v2.7.7
Improved departures/arrivals page: Improved departures/arrivals page:
- badge for platform (due to limitations in data, platform in only known when a train arrives/departs) - badge for platform (due to limitations in data, platform in only known when a train arrives/departs)

2
lib/api/train_data.dart

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

44
lib/main.dart

@ -44,8 +44,10 @@ Map<String, WidgetBuilder> routesByUiDesign(UiDesign uiDesign) => {
); );
}, },
TrainInfo.routeName: (context) { TrainInfo.routeName: (context) {
final args = ModalRoute.of(context)!.settings.arguments as TrainInfoArguments;
return TrainInfo( return TrainInfo(
trainNumber: ModalRoute.of(context)!.settings.arguments as String, trainNumber: args.trainNumber,
date: args.date,
uiDesign: uiDesign, uiDesign: uiDesign,
); );
}, },
@ -67,27 +69,27 @@ class StartPoint extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (Platform.isIOS || Platform.isMacOS) { // if (Platform.isIOS || Platform.isMacOS) {
return AnnotatedRegion( // return AnnotatedRegion(
value: const SystemUiOverlayStyle( // value: const SystemUiOverlayStyle(
statusBarBrightness: Brightness.dark, // statusBarBrightness: Brightness.dark,
),
child: CupertinoApp(
title: appTitle,
theme: CupertinoThemeData(
primaryColor: Colors.blue.shade600,
brightness: Brightness.dark,
// textTheme: CupertinoTextThemeData(
// textStyle: TextStyle(
// fontFamily: 'Atkinson Hyperlegible',
// ), // ),
// child: CupertinoApp(
// title: appTitle,
// theme: CupertinoThemeData(
// primaryColor: Colors.blue.shade600,
// brightness: Brightness.dark,
// // textTheme: CupertinoTextThemeData(
// // textStyle: TextStyle(
// // fontFamily: 'Atkinson Hyperlegible',
// // ),
// // ),
// ), // ),
), // routes: routesByUiDesign(UiDesign.CUPERTINO),
routes: routesByUiDesign(UiDesign.CUPERTINO), // ),
), // );
); // }
} // else {
else {
return MaterialApp( return MaterialApp(
title: appTitle, title: appTitle,
theme: ThemeData( theme: ThemeData(
@ -102,6 +104,6 @@ class StartPoint extends StatelessWidget {
), ),
routes: routesByUiDesign(UiDesign.MATERIAL), routes: routesByUiDesign(UiDesign.MATERIAL),
); );
} // }
} }
} }

13
lib/models/station_data.dart

@ -35,8 +35,16 @@ class StationTrain {
final String operator; final String operator;
final String terminus; final String terminus;
final List<String>? route; final List<String>? route;
final DateTime departureDate;
StationTrain({required this.rank, required this.number, required this.operator, required this.terminus, this.route,}); StationTrain({
required this.rank,
required this.number,
required this.operator,
required this.terminus,
this.route,
required this.departureDate,
});
factory StationTrain.fromJson(Map<String, dynamic> json) => _$StationTrainFromJson(json); factory StationTrain.fromJson(Map<String, dynamic> json) => _$StationTrainFromJson(json);
Map<String, dynamic> toJson() => _$StationTrainToJson(this); Map<String, dynamic> toJson() => _$StationTrainToJson(this);
@ -46,9 +54,10 @@ class StationTrain {
class StationStatus { class StationStatus {
final int delay; final int delay;
final bool real; final bool real;
final bool cancelled;
final String? platform; final String? platform;
StationStatus({required this.delay, required this.real, required this.platform}); StationStatus({required this.delay, required this.real, required this.cancelled, required this.platform});
factory StationStatus.fromJson(Map<String, dynamic> json) => _$StationStatusFromJson(json); factory StationStatus.fromJson(Map<String, dynamic> json) => _$StationStatusFromJson(json);
Map<String, dynamic> toJson() => _$StationStatusToJson(this); Map<String, dynamic> toJson() => _$StationStatusToJson(this);

4
lib/models/station_data.g.dart

@ -48,6 +48,7 @@ StationTrain _$StationTrainFromJson(Map<String, dynamic> json) => StationTrain(
terminus: json['terminus'] as String, terminus: json['terminus'] as String,
route: route:
(json['route'] as List<dynamic>?)?.map((e) => e as String).toList(), (json['route'] as List<dynamic>?)?.map((e) => e as String).toList(),
departureDate: DateTime.parse(json['departureDate'] as String),
); );
Map<String, dynamic> _$StationTrainToJson(StationTrain instance) => Map<String, dynamic> _$StationTrainToJson(StationTrain instance) =>
@ -57,12 +58,14 @@ Map<String, dynamic> _$StationTrainToJson(StationTrain instance) =>
'operator': instance.operator, 'operator': instance.operator,
'terminus': instance.terminus, 'terminus': instance.terminus,
'route': instance.route, 'route': instance.route,
'departureDate': instance.departureDate.toIso8601String(),
}; };
StationStatus _$StationStatusFromJson(Map<String, dynamic> json) => StationStatus _$StationStatusFromJson(Map<String, dynamic> json) =>
StationStatus( StationStatus(
delay: json['delay'] as int, delay: json['delay'] as int,
real: json['real'] as bool, real: json['real'] as bool,
cancelled: json['cancelled'] as bool,
platform: json['platform'] as String?, platform: json['platform'] as String?,
); );
@ -70,5 +73,6 @@ Map<String, dynamic> _$StationStatusToJson(StationStatus instance) =>
<String, dynamic>{ <String, dynamic>{
'delay': instance.delay, 'delay': instance.delay,
'real': instance.real, 'real': instance.real,
'cancelled': instance.cancelled,
'platform': instance.platform, 'platform': instance.platform,
}; };

19
lib/pages/main/main_page_cupertino.dart

@ -39,7 +39,24 @@ class MainPageCupertino extends MainPageShared {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: options.map((option) => CupertinoButton.filled( children: options.map((option) => CupertinoButton.filled(
child: Text(option.name), child: Text.rich(
TextSpan(
children: [
TextSpan(text: option.name),
if (option.description != null) ...[
TextSpan(text: '\n'),
TextSpan(
text: option.description,
style: TextStyle(
inherit: true,
fontSize: 14,
),
),
],
],
),
textAlign: TextAlign.center,
),
onPressed: option.action == null ? null : () => option.action!(context), onPressed: option.action == null ? null : () => option.action!(context),
)).map((w) => Padding( )).map((w) => Padding(
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2), padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),

12
lib/pages/station_arrdep_page/view_station/view_station.dart

@ -34,8 +34,10 @@ abstract class ViewStationPageState extends State<ViewStationPage> {
static const loadingText = 'Se încarcă...'; static const loadingText = 'Se încarcă...';
static const arrivesFrom = 'Sosește de la'; static const arrivesFrom = 'Sosește de la';
static const arrivedFrom = 'A sosit de la'; static const arrivedFrom = 'A sosit de la';
static const cancelledArrival = 'Anulat - de la';
static const departsTo = 'Pleacă către'; static const departsTo = 'Pleacă către';
static const departedTo = 'A plecat către'; static const departedTo = 'A plecat către';
static const cancelledDeparture = 'Anulat - către';
ViewStationPageTab tab = ViewStationPageTab.departures; ViewStationPageTab tab = ViewStationPageTab.departures;
late String stationName; late String stationName;
@ -72,8 +74,14 @@ abstract class ViewStationPageState extends State<ViewStationPage> {
}); });
} }
void onTrainTapped(String trainNumber) { void onTrainTapped(StationTrain train) {
Navigator.of(context).pushNamed(TrainInfo.routeName, arguments: trainNumber); Navigator.of(context).pushNamed(
TrainInfo.routeName,
arguments: TrainInfoArguments(
trainNumber: train.number,
date: train.departureDate,
),
);
} }
@override @override

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

@ -54,7 +54,7 @@ class ViewStationPageStateCupertino extends ViewStationPageState {
@override @override
Widget buildStationArrivalItem(BuildContext context, StationArrDep item) { Widget buildStationArrivalItem(BuildContext context, StationArrDep item) {
return GestureDetector( return GestureDetector(
onTap: () => onTrainTapped(item.train.number), onTap: () => onTrainTapped(item.train),
child: CupertinoFormRow( child: CupertinoFormRow(
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'), child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'),
prefix: Text.rich( prefix: Text.rich(
@ -87,7 +87,7 @@ class ViewStationPageStateCupertino extends ViewStationPageState {
@override @override
Widget buildStationDepartureItem(BuildContext context, StationArrDep item) { Widget buildStationDepartureItem(BuildContext context, StationArrDep item) {
return GestureDetector( return GestureDetector(
onTap: () => onTrainTapped(item.train.number), onTap: () => onTrainTapped(item.train),
child: CupertinoFormRow( child: CupertinoFormRow(
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'), child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'),
prefix: Text.rich( prefix: Text.rich(

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

@ -49,10 +49,11 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
); );
} }
@override Widget buildStationItem(BuildContext context, StationArrDep item, {required bool arrival}) {
Widget buildStationArrivalItem(BuildContext context, StationArrDep item) {
return InkWell( return InkWell(
onTap: () => onTrainTapped(item.train.number), onTap: () => onTrainTapped(item.train),
child: Container(
color: item.status.cancelled ? Colors.red.withAlpha(100) : null,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -69,7 +70,7 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
fontFeatures: [ fontFeatures: [
FontFeature.tabularFigures(), FontFeature.tabularFigures(),
], ],
decoration: item.status.delay != 0 ? TextDecoration.lineThrough : null, decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
fontSize: item.status.delay != 0 ? 12 : null, fontSize: item.status.delay != 0 ? 12 : null,
), ),
), ),
@ -114,7 +115,13 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
subtitle: Text.rich( subtitle: Text.rich(
TextSpan( TextSpan(
children: [ children: [
TextSpan(text: item.time.add(Duration(minutes: max(0, item.status.delay))).compareTo(DateTime.now()) < 0 ? ViewStationPageState.arrivedFrom : ViewStationPageState.arrivesFrom), TextSpan(
text: item.status.cancelled
? (arrival ? ViewStationPageState.cancelledArrival : ViewStationPageState.cancelledDeparture)
: item.time.add(Duration(minutes: max(0, item.status.delay))).compareTo(DateTime.now()) < 0
? (arrival ? ViewStationPageState.arrivedFrom : ViewStationPageState.departedTo)
: (arrival ? ViewStationPageState.arrivesFrom : ViewStationPageState.departsTo)
),
TextSpan(text: ' '), TextSpan(text: ' '),
TextSpan(text: item.train.terminus), TextSpan(text: item.train.terminus),
if (item.status.delay != 0) ...[ if (item.status.delay != 0) ...[
@ -165,125 +172,17 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
), ),
], ],
), ),
),
); );
} }
@override @override
Widget buildStationDepartureItem(BuildContext context, StationArrDep item) { Widget buildStationArrivalItem(BuildContext context, StationArrDep item) {
return InkWell( return buildStationItem(context, item, arrival: true);
onTap: () => onTrainTapped(item.train.number), }
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}',
style: TextStyle(
inherit: true,
fontFeatures: [
FontFeature.tabularFigures(),
],
decoration: item.status.delay != 0 ? TextDecoration.lineThrough : null,
fontSize: item.status.delay != 0 ? 12 : null,
),
),
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( @override
'${newTime.toLocal().hour.toString().padLeft(2, '0')}:${newTime.toLocal().minute.toString().padLeft(2, '0')}', Widget buildStationDepartureItem(BuildContext context, StationArrDep item) {
style: TextStyle( return buildStationItem(context, item, arrival: false);
inherit: true,
fontFeatures: [
FontFeature.tabularFigures(),
],
color: delay ? Colors.red : Colors.green,
),
);
},
),
],
),
),
Expanded(
child: IgnorePointer(
child: ListTile(
isThreeLine: item.status.delay != 0,
title: Text.rich(
TextSpan(
children: [
TextSpan(
text: item.train.rank,
style: TextStyle(
color: item.train.rank.startsWith('IR') ? Color.fromARGB(255, 255, 0, 0) : null,
),
),
TextSpan(text: ' '),
TextSpan(text: item.train.number,),
],
),
),
subtitle: Text.rich(
TextSpan(
children: [
TextSpan(text: item.time.add(Duration(minutes: max(0, item.status.delay))).compareTo(DateTime.now()) < 0 ? ViewStationPageState.departedTo : ViewStationPageState.departsTo),
TextSpan(text: ' '),
TextSpan(text: item.train.terminus),
if (item.status.delay != 0) ...[
TextSpan(text: '\n'),
if (item.status.delay.abs() >= 60) ...[
TextSpan(text: (item.status.delay.abs() ~/ 60).toString()),
TextSpan(text: item.status.delay.abs() >= 120 ? ' ore' : ' oră'),
if (item.status.delay.abs() % 60 != 0)
TextSpan(text: ' și '),
],
TextSpan(text: (item.status.delay.abs() % 60).toString()),
TextSpan(text: item.status.delay.abs() > 1 ? ' minute' : ' minut'),
TextSpan(text: ' '),
if (item.status.delay > 0)
TextSpan(
text: 'întârziere',
style: TextStyle(
inherit: true,
color: Colors.red,
),
)
else
TextSpan(
text: 'mai devreme',
style: TextStyle(
inherit: true,
color: Colors.green,
),
),
],
],
),
),
),
),
),
if (item.status.platform != null)
IntrinsicHeight(
child: AspectRatio(
aspectRatio: 1,
child: MaterialBadge(
text: item.status.platform!,
caption: 'Linia',
isOnTime: item.status.real && item.status.delay <= 0,
isDelayed: item.status.real && item.status.delay > 0,
),
),
),
],
),
);
} }
} }

5
lib/pages/train_info_page/select_train/select_train.dart

@ -23,7 +23,10 @@ class SelectTrainPage extends StatefulWidget {
void onTrainSelected(BuildContext context, String selection) { void onTrainSelected(BuildContext context, String selection) {
selection = selection.characters.takeWhile((char) => List.generate(10, (i) => i.toString()).contains(char)).join(); selection = selection.characters.takeWhile((char) => List.generate(10, (i) => i.toString()).contains(char)).join();
Navigator.of(context).pushNamed(TrainInfo.routeName, arguments: selection); Navigator.of(context).pushNamed(
TrainInfo.routeName,
arguments: TrainInfoArguments(trainNumber: selection),
);
} }
@override @override

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

@ -16,15 +16,16 @@ class TrainInfo extends StatelessWidget {
final UiDesign? uiDesign; final UiDesign? uiDesign;
final String trainNumber; final String trainNumber;
final DateTime? date;
TrainInfo({Key? key, required this.trainNumber, this.uiDesign}): super(key: key); TrainInfo({Key? key, required this.trainNumber, this.date, this.uiDesign}): super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final uiDesign = this.uiDesign ?? defaultUiDesign; final uiDesign = this.uiDesign ?? defaultUiDesign;
return RefreshFutureBuilder<TrainData>( return RefreshFutureBuilder<TrainData>(
futureCreator: () => getTrain(trainNumber), futureCreator: () => getTrain(trainNumber, date: date),
builder: (context, refresh, replaceFutureBuilder, snapshot) { builder: (context, refresh, replaceFutureBuilder, snapshot) {
void onViewYesterdayTrain() { void onViewYesterdayTrain() {
replaceFutureBuilder(() => getTrain(trainNumber, date: DateTime.now().subtract(const Duration(days: 1)))); replaceFutureBuilder(() => getTrain(trainNumber, date: DateTime.now().subtract(const Duration(days: 1))));
@ -66,6 +67,13 @@ class TrainInfo extends StatelessWidget {
} }
} }
class TrainInfoArguments {
final String trainNumber;
final DateTime? date;
TrainInfoArguments({required this.trainNumber, this.date});
}
abstract class TrainInfoLoading extends StatelessWidget { abstract class TrainInfoLoading extends StatelessWidget {
final String title; final String title;
final Widget loadingWidget; final Widget loadingWidget;

2
pubspec.yaml

@ -11,7 +11,7 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 2.7.7 version: 2.7.8
environment: environment:
sdk: ">=2.15.0 <3.0.0" sdk: ">=2.15.0 <3.0.0"

Loading…
Cancel
Save