Kenneth Bruen
2 years ago
30 changed files with 3062 additions and 612 deletions
@ -0,0 +1,59 @@ |
|||||||
|
import 'package:flutter/cupertino.dart'; |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart'; |
||||||
|
import 'package:info_tren/components/badge/badge_cupertino.dart'; |
||||||
|
import 'package:info_tren/components/badge/badge_fluent.dart'; |
||||||
|
import 'package:info_tren/components/badge/badge_material.dart'; |
||||||
|
import 'package:info_tren/models.dart'; |
||||||
|
import 'package:info_tren/providers.dart'; |
||||||
|
|
||||||
|
class Badge extends ConsumerWidget { |
||||||
|
final String text; |
||||||
|
final String caption; |
||||||
|
final bool isNotScheduled; |
||||||
|
final bool isOnTime; |
||||||
|
final bool isDelayed; |
||||||
|
|
||||||
|
const Badge({ |
||||||
|
super.key, |
||||||
|
required this.text, |
||||||
|
required this.caption, |
||||||
|
this.isNotScheduled = false, |
||||||
|
this.isOnTime = false, |
||||||
|
this.isDelayed = false, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context, WidgetRef ref) { |
||||||
|
final uiDesign = ref.watch(uiDesignProvider); |
||||||
|
|
||||||
|
switch (uiDesign) { |
||||||
|
case UiDesign.MATERIAL: |
||||||
|
return MaterialBadge( |
||||||
|
text: text, |
||||||
|
caption: caption, |
||||||
|
isNotScheduled: isNotScheduled, |
||||||
|
isOnTime: isOnTime, |
||||||
|
isDelayed: isDelayed, |
||||||
|
); |
||||||
|
case UiDesign.CUPERTINO: |
||||||
|
return CupertinoBadge( |
||||||
|
text: text, |
||||||
|
caption: caption, |
||||||
|
isNotScheduled: isNotScheduled, |
||||||
|
isOnTime: isOnTime, |
||||||
|
isDelayed: isDelayed, |
||||||
|
); |
||||||
|
case UiDesign.FLUENT: |
||||||
|
return FluentBadge( |
||||||
|
text: text, |
||||||
|
caption: caption, |
||||||
|
isNotScheduled: isNotScheduled, |
||||||
|
isOnTime: isOnTime, |
||||||
|
isDelayed: isDelayed, |
||||||
|
); |
||||||
|
default: |
||||||
|
throw UnmatchedUiDesignException(uiDesign); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,84 +1,5 @@ |
|||||||
import 'package:flutter/cupertino.dart'; |
import 'package:flutter/cupertino.dart'; |
||||||
import 'package:flutter/material.dart'; |
|
||||||
import 'package:info_tren/pages/train_info_page/train_info_constants.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_material.dart'; |
|
||||||
|
|
||||||
class MaterialBadge extends StatelessWidget { |
|
||||||
final String text; |
|
||||||
final String caption; |
|
||||||
final bool isNotScheduled; |
|
||||||
final bool isOnTime; |
|
||||||
final bool isDelayed; |
|
||||||
|
|
||||||
const MaterialBadge({ |
|
||||||
required this.text, |
|
||||||
required this.caption, |
|
||||||
this.isNotScheduled = false, |
|
||||||
this.isOnTime = false, |
|
||||||
this.isDelayed = false, |
|
||||||
super.key, |
|
||||||
}); |
|
||||||
|
|
||||||
@override |
|
||||||
Widget build(BuildContext context) { |
|
||||||
Color foregroundColor = Colors.white70; |
|
||||||
Color? backgroundColor; |
|
||||||
|
|
||||||
if (isNotScheduled) { |
|
||||||
foregroundColor = Colors.orange.shade300; |
|
||||||
backgroundColor = Colors.orange.shade900.withOpacity(0.3); |
|
||||||
} |
|
||||||
else if (isOnTime) { |
|
||||||
foregroundColor = Colors.green.shade300; |
|
||||||
backgroundColor = Colors.green.shade900.withOpacity(0.3); |
|
||||||
} |
|
||||||
else if (isDelayed) { |
|
||||||
foregroundColor = Colors.red.shade300; |
|
||||||
backgroundColor = Colors.red.shade900.withOpacity(0.3); |
|
||||||
} |
|
||||||
|
|
||||||
return Padding( |
|
||||||
padding: const EdgeInsets.all(8), |
|
||||||
child: Container( |
|
||||||
decoration: BoxDecoration( |
|
||||||
borderRadius: BorderRadius.circular(10), |
|
||||||
border: Border.all( |
|
||||||
width: 2, |
|
||||||
color: foregroundColor, |
|
||||||
), |
|
||||||
color: backgroundColor, |
|
||||||
), |
|
||||||
width: isSmallScreen(context) ? 42 : 48, |
|
||||||
height: isSmallScreen(context) ? 42 : 48, |
|
||||||
child: Column( |
|
||||||
mainAxisSize: MainAxisSize.min, |
|
||||||
children: <Widget>[ |
|
||||||
Expanded( |
|
||||||
child: Center( |
|
||||||
child: Text( |
|
||||||
text, |
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith( |
|
||||||
fontSize: isSmallScreen(context) ? 16 : 20, |
|
||||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200, |
|
||||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor, |
|
||||||
), |
|
||||||
textAlign: TextAlign.center, |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
Text( |
|
||||||
caption, |
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith( |
|
||||||
fontSize: 10, |
|
||||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor, |
|
||||||
), |
|
||||||
), |
|
||||||
], |
|
||||||
), |
|
||||||
), |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
class CupertinoBadge extends StatelessWidget { |
class CupertinoBadge extends StatelessWidget { |
||||||
final String text; |
final String text; |
@ -0,0 +1,80 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
|
||||||
|
|
||||||
|
class FluentBadge extends StatelessWidget { |
||||||
|
final String text; |
||||||
|
final String caption; |
||||||
|
final bool isNotScheduled; |
||||||
|
final bool isOnTime; |
||||||
|
final bool isDelayed; |
||||||
|
|
||||||
|
const FluentBadge({ |
||||||
|
required this.text, |
||||||
|
required this.caption, |
||||||
|
this.isNotScheduled = false, |
||||||
|
this.isOnTime = false, |
||||||
|
this.isDelayed = false, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
Color foregroundColor = FluentTheme.of(context).activeColor; |
||||||
|
Color? backgroundColor; |
||||||
|
|
||||||
|
if (isNotScheduled) { |
||||||
|
foregroundColor = const Color.fromRGBO(225, 175, 30, 1); |
||||||
|
backgroundColor = const Color.fromRGBO(80, 40, 10, 1); |
||||||
|
} |
||||||
|
else if (isOnTime) { |
||||||
|
foregroundColor = const Color.fromRGBO(130, 175, 65, 1); |
||||||
|
backgroundColor = const Color.fromRGBO(40, 80, 10, 1); |
||||||
|
} |
||||||
|
else if (isDelayed) { |
||||||
|
foregroundColor = const Color.fromRGBO(225, 75, 30, 1); |
||||||
|
backgroundColor = const Color.fromRGBO(80, 20, 10, 1); |
||||||
|
} |
||||||
|
|
||||||
|
return Padding( |
||||||
|
padding: const EdgeInsets.all(8), |
||||||
|
child: Container( |
||||||
|
decoration: BoxDecoration( |
||||||
|
borderRadius: BorderRadius.circular(10), |
||||||
|
border: Border.all( |
||||||
|
width: 2, |
||||||
|
color: foregroundColor, |
||||||
|
), |
||||||
|
color: backgroundColor, |
||||||
|
// color: CupertinoColors.activeOrange, |
||||||
|
), |
||||||
|
width: 48, |
||||||
|
height: 48, |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: Center( |
||||||
|
child: Text( |
||||||
|
text, |
||||||
|
style: FluentTheme.of(context).typography.bodyLarge?.copyWith( |
||||||
|
fontSize: 20, |
||||||
|
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200, |
||||||
|
color: MediaQuery.of(context).boldText ? Colors.white : foregroundColor, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
Text( |
||||||
|
caption, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 12, |
||||||
|
color: MediaQuery.of(context).boldText ? Colors.white : foregroundColor, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart'; |
||||||
|
|
||||||
|
class MaterialBadge extends StatelessWidget { |
||||||
|
final String text; |
||||||
|
final String caption; |
||||||
|
final bool isNotScheduled; |
||||||
|
final bool isOnTime; |
||||||
|
final bool isDelayed; |
||||||
|
|
||||||
|
const MaterialBadge({ |
||||||
|
required this.text, |
||||||
|
required this.caption, |
||||||
|
this.isNotScheduled = false, |
||||||
|
this.isOnTime = false, |
||||||
|
this.isDelayed = false, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
Color foregroundColor = Colors.white70; |
||||||
|
Color? backgroundColor; |
||||||
|
|
||||||
|
if (isNotScheduled) { |
||||||
|
foregroundColor = Colors.orange.shade300; |
||||||
|
backgroundColor = Colors.orange.shade900.withOpacity(0.3); |
||||||
|
} |
||||||
|
else if (isOnTime) { |
||||||
|
foregroundColor = Colors.green.shade300; |
||||||
|
backgroundColor = Colors.green.shade900.withOpacity(0.3); |
||||||
|
} |
||||||
|
else if (isDelayed) { |
||||||
|
foregroundColor = Colors.red.shade300; |
||||||
|
backgroundColor = Colors.red.shade900.withOpacity(0.3); |
||||||
|
} |
||||||
|
|
||||||
|
return Padding( |
||||||
|
padding: const EdgeInsets.all(8), |
||||||
|
child: Container( |
||||||
|
decoration: BoxDecoration( |
||||||
|
borderRadius: BorderRadius.circular(10), |
||||||
|
border: Border.all( |
||||||
|
width: 2, |
||||||
|
color: foregroundColor, |
||||||
|
), |
||||||
|
color: backgroundColor, |
||||||
|
), |
||||||
|
width: isSmallScreen(context) ? 42 : 48, |
||||||
|
height: isSmallScreen(context) ? 42 : 48, |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: Center( |
||||||
|
child: Text( |
||||||
|
text, |
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith( |
||||||
|
fontSize: isSmallScreen(context) ? 16 : 20, |
||||||
|
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200, |
||||||
|
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
Text( |
||||||
|
caption, |
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith( |
||||||
|
fontSize: 10, |
||||||
|
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
import 'package:info_tren/components/loading/loading.dart'; |
||||||
|
|
||||||
|
class LoadingFluent extends LoadingCommon { |
||||||
|
const LoadingFluent({required super.text, super.key}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Center( |
||||||
|
child: Column( |
||||||
|
crossAxisAlignment: CrossAxisAlignment.center, |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
const Padding( |
||||||
|
padding: EdgeInsets.all(8.0), |
||||||
|
child: ProgressRing(), |
||||||
|
), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Text(text), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart'; |
||||||
|
import 'package:info_tren/models.dart'; |
||||||
|
|
||||||
|
class SelectTrainSuggestionsFluent extends SelectTrainSuggestionsShared { |
||||||
|
const SelectTrainSuggestionsFluent({ |
||||||
|
super.key, |
||||||
|
required super.choices, |
||||||
|
required super.onTrainSelected, |
||||||
|
super.currentInput, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget getUseCurrentInputWidget(String currentInput, void Function(String) onTrainSelected) { |
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(0), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Button( |
||||||
|
child: Text(getUseCurrentInputWidgetText(currentInput)), |
||||||
|
onPressed: () => onTrainSelected(currentInput), |
||||||
|
), |
||||||
|
], |
||||||
|
) |
||||||
|
), |
||||||
|
const Divider(), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class OperatorAutocompleteTileFluent extends OperatorAutocompleteTile { |
||||||
|
const OperatorAutocompleteTileFluent({ |
||||||
|
Key? key, |
||||||
|
required String operatorName, |
||||||
|
required void Function(String) onTrainSelected, |
||||||
|
required TrainsResult train |
||||||
|
}): super( |
||||||
|
onTrainSelected: onTrainSelected, |
||||||
|
operatorName: operatorName, |
||||||
|
train: train, |
||||||
|
key: key, |
||||||
|
); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
GestureDetector( |
||||||
|
onTap: () { |
||||||
|
onTrainSelected(train.number); |
||||||
|
}, |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.fromLTRB(16, 4, 16, 4), |
||||||
|
child: SizedBox( |
||||||
|
width: double.infinity, |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
operatorName, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith(fontSize: 10, fontWeight: FontWeight.w200), |
||||||
|
textAlign: TextAlign.left, |
||||||
|
), |
||||||
|
Text( |
||||||
|
"${train.rank} ${train.number}", |
||||||
|
textAlign: TextAlign.left, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
const Divider(), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
import 'package:info_tren/pages/main/main_page.dart'; |
||||||
|
|
||||||
|
class MainPageFluent extends MainPageShared { |
||||||
|
const MainPageFluent({super.key}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return NavigationView( |
||||||
|
appBar: NavigationAppBar( |
||||||
|
automaticallyImplyLeading: false, |
||||||
|
title: Text(pageTitle), |
||||||
|
// centerTitle: true, |
||||||
|
// actions: [ |
||||||
|
// PopupMenuButton<int>( |
||||||
|
// icon: const Icon(Icons.more_vert), |
||||||
|
// tooltip: moreOptionsText, |
||||||
|
// itemBuilder: (_) => popupMenu.asMap().entries.map((e) => PopupMenuItem( |
||||||
|
// value: e.key, |
||||||
|
// child: Text(e.value.name), |
||||||
|
// )).toList(), |
||||||
|
// onSelected: (index) { |
||||||
|
// popupMenu[index].action?.call(context); |
||||||
|
// }, |
||||||
|
// ), |
||||||
|
// ], |
||||||
|
), |
||||||
|
content: SafeArea( |
||||||
|
child: Center( |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: options.map((option) => HoverButton( |
||||||
|
onPressed: option.action != null ? () => option.action!(context) : null, |
||||||
|
builder: (context, states) { |
||||||
|
return Card( |
||||||
|
backgroundColor: option.action != null ? (states.isHovering ? FluentTheme.of(context).accentColor.dark : FluentTheme.of(context).accentColor) : null, |
||||||
|
child: Column( |
||||||
|
children: [ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Text( |
||||||
|
option.name, |
||||||
|
style: FluentTheme.of(context) |
||||||
|
.typography |
||||||
|
.bodyLarge |
||||||
|
?.copyWith( |
||||||
|
color: |
||||||
|
FluentTheme.of(context).activeColor, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Text(option.description!), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
)).map((w) => Padding( |
||||||
|
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2), |
||||||
|
child: SizedBox( |
||||||
|
width: double.infinity, |
||||||
|
child: w, |
||||||
|
), |
||||||
|
)).toList(), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart'; |
||||||
|
|
||||||
|
class SelectStationPageFluent extends SelectStationPageShared { |
||||||
|
const SelectStationPageFluent({super.key}); |
||||||
|
|
||||||
|
@override |
||||||
|
State<StatefulWidget> createState() => SelectStationPageStateFluent(); |
||||||
|
} |
||||||
|
|
||||||
|
class SelectStationPageStateFluent extends SelectStationPageState { |
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return NavigationView( |
||||||
|
appBar: const NavigationAppBar( |
||||||
|
title: Text(SelectStationPageState.pageTitle), |
||||||
|
// centerTitle: true, |
||||||
|
), |
||||||
|
content: SafeArea( |
||||||
|
bottom: false, |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.max, |
||||||
|
children: [ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: TextBox( |
||||||
|
controller: textEditingController, |
||||||
|
autofocus: true, |
||||||
|
placeholder: SelectStationPageState.textFieldLabel, |
||||||
|
textInputAction: TextInputAction.search, |
||||||
|
onChanged: onTextChanged, |
||||||
|
), |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
child: ListView.builder( |
||||||
|
itemBuilder: (context, index) { |
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
ListTile( |
||||||
|
// dense: true, |
||||||
|
title: Text(filteredStations[index]), |
||||||
|
onPressed: () => onSuggestionSelected(filteredStations[index]), |
||||||
|
), |
||||||
|
const Divider( |
||||||
|
size: 1, |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
}, |
||||||
|
itemCount: filteredStations.length, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,267 @@ |
|||||||
|
import 'dart:math'; |
||||||
|
import 'dart:ui'; |
||||||
|
|
||||||
|
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/loading/loading.dart'; |
||||||
|
import 'package:info_tren/components/refresh_future_builder.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'; |
||||||
|
|
||||||
|
class ViewStationPageFluent extends ViewStationPageShared { |
||||||
|
const ViewStationPageFluent({super.key, required super.tab, required super.setTab}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget buildContent(BuildContext context, Future Function() refresh, Future Function(Future<StationData> Function()) _, RefreshFutureBuilderSnapshot<StationData> snapshot) { |
||||||
|
return NavigationView( |
||||||
|
appBar: NavigationAppBar( |
||||||
|
title: Consumer( |
||||||
|
builder: (context, ref, _) { |
||||||
|
final stationName = ref.watch(viewStationArgumentsProvider.select((value) => value.stationName)); |
||||||
|
return Text(snapshot.hasData ? snapshot.data!.stationName : stationName); |
||||||
|
} |
||||||
|
), |
||||||
|
), |
||||||
|
content: snapshot.state == RefreshFutureBuilderState.waiting || snapshot.state == RefreshFutureBuilderState.refreshError |
||||||
|
? const Loading(text: ViewStationPageShared.loadingText,) |
||||||
|
: snapshot.state == RefreshFutureBuilderState.error |
||||||
|
? Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Center( |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
Icon( |
||||||
|
FluentIcons.error, |
||||||
|
size: 32, |
||||||
|
color: Colors.red.normal, |
||||||
|
), |
||||||
|
const Text( |
||||||
|
ViewStationPageShared.errorText, |
||||||
|
style: TextStyle( |
||||||
|
inherit: true, |
||||||
|
fontSize: 32, |
||||||
|
), |
||||||
|
), |
||||||
|
Text( |
||||||
|
snapshot.error.toString(), |
||||||
|
style: FluentTheme.of(context).typography.subtitle, |
||||||
|
), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Button( |
||||||
|
onPressed: () { |
||||||
|
refresh(); |
||||||
|
}, |
||||||
|
child: const Text(ViewStationPageShared.retryText), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
) |
||||||
|
: null, |
||||||
|
pane: snapshot.hasData ? NavigationPane( |
||||||
|
onChanged: onTabChange, |
||||||
|
selected: tab.index, |
||||||
|
items: [ |
||||||
|
PaneItem( |
||||||
|
body: CustomScrollView( |
||||||
|
slivers: [ |
||||||
|
SliverToBoxAdapter(child: SafeArea(left: false, bottom: false, right: false,child: Container(),),), |
||||||
|
SliverList( |
||||||
|
delegate: SliverChildBuilderDelegate( |
||||||
|
(context, index) { |
||||||
|
return tab == ViewStationPageTab.arrivals ? buildStationArrivalItem(context, snapshot.data!.arrivals![index]) : buildStationDepartureItem(context, snapshot.data!.departures![index]); |
||||||
|
}, |
||||||
|
childCount: tab == ViewStationPageTab.arrivals ? snapshot.data!.arrivals?.length ?? 0 : snapshot.data!.departures?.length ?? 0, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
title: const Text(ViewStationPageShared.arrivals), |
||||||
|
icon: const Icon(FluentIcons.arrow_down_right8), |
||||||
|
), |
||||||
|
PaneItem( |
||||||
|
body: CustomScrollView( |
||||||
|
slivers: [ |
||||||
|
SliverToBoxAdapter(child: SafeArea(left: false, bottom: false, right: false,child: Container(),),), |
||||||
|
SliverList( |
||||||
|
delegate: SliverChildBuilderDelegate( |
||||||
|
(context, index) { |
||||||
|
return tab == ViewStationPageTab.arrivals ? buildStationArrivalItem(context, snapshot.data!.arrivals![index]) : buildStationDepartureItem(context, snapshot.data!.departures![index]); |
||||||
|
}, |
||||||
|
childCount: tab == ViewStationPageTab.arrivals ? snapshot.data!.arrivals?.length ?? 0 : snapshot.data!.departures?.length ?? 0, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
title: const Text(ViewStationPageShared.departures), |
||||||
|
icon: const Icon(FluentIcons.arrow_up_right8), |
||||||
|
), |
||||||
|
], |
||||||
|
) : null, |
||||||
|
// bottomNavigationBar: snapshot.hasData ? BottomNavigationBar( |
||||||
|
// items: const [ |
||||||
|
// BottomNavigationBarItem( |
||||||
|
// icon: Icon(Icons.arrow_downward), |
||||||
|
// label: ViewStationPageShared.arrivals, |
||||||
|
// ), |
||||||
|
// BottomNavigationBarItem( |
||||||
|
// icon: Icon(Icons.arrow_upward), |
||||||
|
// label: ViewStationPageShared.departures, |
||||||
|
// ), |
||||||
|
// ], |
||||||
|
// currentIndex: tab.index, |
||||||
|
// onTap: onTabChange, |
||||||
|
// ) : null, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
Widget buildStationItem(BuildContext context, StationArrDep item, {required bool arrival}) { |
||||||
|
return HoverButton( |
||||||
|
onPressed: () => onTrainTapped(context, item.train), |
||||||
|
builder: (context, states) { |
||||||
|
return Container( |
||||||
|
color: item.status.cancelled |
||||||
|
? Colors.red.withAlpha(100) |
||||||
|
: states.isPressing |
||||||
|
? FluentTheme.of(context).scaffoldBackgroundColor |
||||||
|
: states.isHovering |
||||||
|
? FluentTheme.of(context).inactiveBackgroundColor |
||||||
|
: null, |
||||||
|
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: const [ |
||||||
|
FontFeature.tabularFigures(), |
||||||
|
], |
||||||
|
decoration: item.status.cancelled || 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( |
||||||
|
'${newTime.toLocal().hour.toString().padLeft(2, '0')}:${newTime.toLocal().minute.toString().padLeft(2, '0')}', |
||||||
|
style: TextStyle( |
||||||
|
inherit: true, |
||||||
|
fontFeatures: const [ |
||||||
|
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') ? const Color.fromARGB(255, 255, 0, 0) : null, |
||||||
|
), |
||||||
|
), |
||||||
|
const TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.number,), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
subtitle: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan( |
||||||
|
text: item.status.cancelled |
||||||
|
? (arrival ? ViewStationPageShared.cancelledArrival : ViewStationPageShared.cancelledDeparture) |
||||||
|
: item.time.add(Duration(minutes: max(0, item.status.delay))).compareTo(DateTime.now()) < 0 |
||||||
|
? (arrival ? ViewStationPageShared.arrivedFrom : ViewStationPageShared.departedTo) |
||||||
|
: (arrival ? ViewStationPageShared.arrivesFrom : ViewStationPageShared.departsTo) |
||||||
|
), |
||||||
|
const TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.terminus), |
||||||
|
if (item.status.delay != 0) ...[ |
||||||
|
const 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) |
||||||
|
const TextSpan(text: ' și '), |
||||||
|
], |
||||||
|
TextSpan(text: (item.status.delay.abs() % 60).toString()), |
||||||
|
TextSpan(text: item.status.delay.abs() > 1 ? ' minute' : ' minut'), |
||||||
|
const 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: Badge( |
||||||
|
text: item.status.platform!, |
||||||
|
caption: 'Linia', |
||||||
|
isOnTime: item.status.real && item.status.delay <= 0, |
||||||
|
isDelayed: item.status.real && item.status.delay > 0, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget buildStationArrivalItem(BuildContext context, StationArrDep item) { |
||||||
|
return buildStationItem(context, item, arrival: true); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget buildStationDepartureItem(BuildContext context, StationArrDep item) { |
||||||
|
return buildStationItem(context, item, arrival: false); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
import 'package:flutter/services.dart'; |
||||||
|
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart'; |
||||||
|
|
||||||
|
class SelectTrainPageFluent extends SelectTrainPageShared { |
||||||
|
const SelectTrainPageFluent({super.key}); |
||||||
|
|
||||||
|
@override |
||||||
|
State<SelectTrainPageShared> createState() => SelectTrainPageStateFluent(); |
||||||
|
} |
||||||
|
|
||||||
|
class SelectTrainPageStateFluent extends SelectTrainPageState { |
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return NavigationView( |
||||||
|
appBar: NavigationAppBar( |
||||||
|
title: Text(pageTitle), |
||||||
|
), |
||||||
|
content: SafeArea( |
||||||
|
bottom: false, |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.max, |
||||||
|
children: <Widget>[ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: TextBox( |
||||||
|
controller: trainNoController, |
||||||
|
autofocus: true, |
||||||
|
placeholder: textFieldLabel, |
||||||
|
textInputAction: TextInputAction.search, |
||||||
|
keyboardType: TextInputType.number, |
||||||
|
onChanged: (_) => onTextChanged(), |
||||||
|
inputFormatters: [ |
||||||
|
FilteringTextInputFormatter.digitsOnly, |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
child: suggestionsList, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,807 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
import 'package:flutter/gestures.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/pages/train_info_page/view_train/train_info_fluent_DisplayTrainStation.dart'; |
||||||
|
import 'package:info_tren/utils/state_to_string.dart'; |
||||||
|
|
||||||
|
class TrainInfoLoadingFluent extends TrainInfoLoadingShared { |
||||||
|
TrainInfoLoadingFluent({required super.title, super.loadingText, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return NavigationView( |
||||||
|
appBar: NavigationAppBar( |
||||||
|
title: Text(title), |
||||||
|
), |
||||||
|
content: Center( |
||||||
|
child: loadingWidget, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class TrainInfoErrorFluent extends TrainInfoErrorShared { |
||||||
|
const TrainInfoErrorFluent({ |
||||||
|
required super.error, |
||||||
|
required super.title, |
||||||
|
super.refresh, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return NavigationView( |
||||||
|
appBar: NavigationAppBar( |
||||||
|
title: Text(title), |
||||||
|
), |
||||||
|
content: Center( |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
Text(error.toString()), |
||||||
|
if (refresh != null) |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(8), |
||||||
|
child: Button( |
||||||
|
child: const Text('Retry'), |
||||||
|
onPressed: () => refresh!(), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class TrainInfoFluent extends TrainInfoShared { |
||||||
|
const TrainInfoFluent({ |
||||||
|
super.key, |
||||||
|
required super.trainData, |
||||||
|
super.isRefreshing, |
||||||
|
super.refresh, |
||||||
|
super.onViewYesterdayTrain, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Builder( |
||||||
|
builder: (context) { |
||||||
|
return NavigationView( |
||||||
|
appBar: NavigationAppBar( |
||||||
|
title: Text( |
||||||
|
"Informații despre ${trainData.rank} ${trainData.number}", |
||||||
|
), |
||||||
|
actions: Row( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
if (refresh != null) ...[ |
||||||
|
Center( |
||||||
|
child: SizedBox( |
||||||
|
height: 32, |
||||||
|
width: 32, |
||||||
|
child: IconButton( |
||||||
|
icon: isRefreshing == true ? const ProgressRing() : const Icon( |
||||||
|
FluentIcons.refresh, |
||||||
|
size: 24, |
||||||
|
), |
||||||
|
onPressed: isRefreshing == true ? null : () { |
||||||
|
refresh!(); |
||||||
|
}, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
content: Column( |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: SafeArea( |
||||||
|
bottom: false, |
||||||
|
child: TrainInfoBody( |
||||||
|
trainData: trainData, |
||||||
|
onViewYesterdayTrain: onViewYesterdayTrain, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class TrainInfoBodyFluent extends TrainInfoBodyShared { |
||||||
|
const TrainInfoBodyFluent({ |
||||||
|
super.key, |
||||||
|
required super.trainData, |
||||||
|
super.onViewYesterdayTrain, |
||||||
|
super.refresh, |
||||||
|
super.isRefreshing, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
final mq = MediaQuery.of(context); |
||||||
|
|
||||||
|
if (mq.orientation == Orientation.landscape && mq.size.width >= 1000) { |
||||||
|
return Row( |
||||||
|
mainAxisSize: MainAxisSize.max, |
||||||
|
children: [ |
||||||
|
Container( |
||||||
|
constraints: const BoxConstraints( |
||||||
|
minWidth: 400, |
||||||
|
maxWidth: 400, |
||||||
|
), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.max, |
||||||
|
children: [ |
||||||
|
DisplayTrainID(trainData: trainData), |
||||||
|
DisplayTrainOperator(trainData: trainData), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 2.0), |
||||||
|
child: DisplayTrainRoute(trainData: trainData), |
||||||
|
), |
||||||
|
DisplayTrainDeparture(trainData: trainData), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: DisplayTrainLastInfo(trainData: trainData), |
||||||
|
), |
||||||
|
IntrinsicHeight( |
||||||
|
child: Row( |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: DisplayTrainRouteDuration( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: DisplayTrainRouteDistance( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
const Divider(), |
||||||
|
if (onViewYesterdayTrain != null && |
||||||
|
trainData.stations.first.departure!.scheduleTime |
||||||
|
.compareTo(DateTime.now()) > |
||||||
|
0) |
||||||
|
...[ |
||||||
|
DisplayTrainYesterdayWarningFluent( |
||||||
|
onViewYesterdayTrain!, |
||||||
|
), |
||||||
|
const Divider(), |
||||||
|
], |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
child: CustomScrollView( |
||||||
|
slivers: [ |
||||||
|
DisplayTrainStations( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: Container( |
||||||
|
height: MediaQuery |
||||||
|
.of(context) |
||||||
|
.viewPadding |
||||||
|
.bottom, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
return CustomScrollView( |
||||||
|
slivers: <Widget>[ |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: DisplayTrainID( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: DisplayTrainOperator( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
SliverPadding( |
||||||
|
padding: const EdgeInsets.only(left: 2, right: 2), |
||||||
|
sliver: SliverToBoxAdapter( |
||||||
|
child: DisplayTrainRoute( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: DisplayTrainDeparture( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: DisplayTrainLastInfo( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: IntrinsicHeight( |
||||||
|
child: Row( |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: DisplayTrainRouteDuration( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: DisplayTrainRouteDistance( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
const SliverToBoxAdapter( |
||||||
|
child: Divider(), |
||||||
|
), |
||||||
|
if (onViewYesterdayTrain != null && |
||||||
|
trainData.stations.first.departure!.scheduleTime |
||||||
|
.compareTo(DateTime.now()) > |
||||||
|
0) ...[ |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: DisplayTrainYesterdayWarningFluent( |
||||||
|
onViewYesterdayTrain!), |
||||||
|
), |
||||||
|
const SliverToBoxAdapter( |
||||||
|
child: Divider(), |
||||||
|
), |
||||||
|
], |
||||||
|
DisplayTrainStations( |
||||||
|
trainData: trainData, |
||||||
|
), |
||||||
|
SliverToBoxAdapter( |
||||||
|
child: Container( |
||||||
|
height: MediaQuery |
||||||
|
.of(context) |
||||||
|
.viewPadding |
||||||
|
.bottom, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainID extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainID({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Text.rich( |
||||||
|
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, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainOperator extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainOperator({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Text( |
||||||
|
trainData.operator, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontStyle: FontStyle.italic, |
||||||
|
fontSize: 14, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainRoute extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainRoute({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Row( |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: Center( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: Text( |
||||||
|
trainData.route.from, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 16, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
const Center(child: Text("-")), |
||||||
|
Expanded( |
||||||
|
child: Center( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: Text( |
||||||
|
trainData.route.to, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 16, |
||||||
|
), |
||||||
|
textAlign: TextAlign.right, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainDeparture extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainDeparture({required this.trainData, super.key}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Text( |
||||||
|
// "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}", |
||||||
|
"Plecare în ${trainData.date}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontStyle: FontStyle.italic, |
||||||
|
fontWeight: FontWeight.w200, |
||||||
|
fontSize: 16, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainLastInfo extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainLastInfo({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
if (trainData.status == null) { |
||||||
|
return Container(); |
||||||
|
} |
||||||
|
|
||||||
|
return Card( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Center( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Text( |
||||||
|
"Ultima informație", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
fontWeight: FontWeight.bold, |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
Row( |
||||||
|
children: <Widget>[ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: Text( |
||||||
|
trainData.status!.station, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 18, |
||||||
|
), |
||||||
|
textAlign: TextAlign.left, |
||||||
|
), |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
child: Container(), |
||||||
|
), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: Text( |
||||||
|
stateToString(trainData.status!.state), |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 18, |
||||||
|
), |
||||||
|
textAlign: TextAlign.right, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Row( |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
child: Container(), |
||||||
|
), |
||||||
|
Builder( |
||||||
|
builder: (context) { |
||||||
|
final data = trainData.status!.delay; |
||||||
|
if (data == 0) { |
||||||
|
return Container(); |
||||||
|
} |
||||||
|
|
||||||
|
if (data > 0) { |
||||||
|
return Text( |
||||||
|
"$data ${data == 1 ? 'minut' : 'minute'} întârziere", |
||||||
|
style: |
||||||
|
FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 16, |
||||||
|
color: Colors.red.lighter, |
||||||
|
// color: Colors.red.shade300, |
||||||
|
), |
||||||
|
); |
||||||
|
} else { |
||||||
|
return Text( |
||||||
|
"${-data} ${data == -1 ? 'minut' : 'minute'} mai devreme", |
||||||
|
style: |
||||||
|
FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 16, |
||||||
|
color: Colors.green.lighter, |
||||||
|
// color: Colors.green.shade300, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
}, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainDestination extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainDestination({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
final destination = trainData.stations.last; |
||||||
|
|
||||||
|
return Card( |
||||||
|
child: Center( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: Text( |
||||||
|
"Destinația", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
fontWeight: FontWeight.bold, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
), |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0), |
||||||
|
child: Text( |
||||||
|
destination.name, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 20, |
||||||
|
fontWeight: FontWeight.w500, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
), |
||||||
|
Builder( |
||||||
|
builder: (context) { |
||||||
|
final arrival = destination.arrival!.scheduleTime.toLocal(); |
||||||
|
final delay = |
||||||
|
trainData.stations.last.arrival!.status?.delay ?? 0; |
||||||
|
final arrivalWithDelay = |
||||||
|
arrival.add(Duration(minutes: delay)); |
||||||
|
final arrivalWithDelayString = |
||||||
|
'${arrivalWithDelay.hour}:${arrivalWithDelay.minute.toString().padLeft(2, "0")}'; |
||||||
|
// const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"]; |
||||||
|
|
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
// Text( |
||||||
|
// "în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}", |
||||||
|
// style: Theme.of(context).textTheme.bodyText2?.copyWith( |
||||||
|
// fontSize: isSmallScreen(context) ? 12 : 14, |
||||||
|
// ), |
||||||
|
// textAlign: TextAlign.center, |
||||||
|
// ), |
||||||
|
Text.rich( |
||||||
|
TextSpan( |
||||||
|
text: 'la', |
||||||
|
children: [ |
||||||
|
const TextSpan(text: ' '), |
||||||
|
TextSpan( |
||||||
|
text: |
||||||
|
'${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}', |
||||||
|
style: delay == 0 |
||||||
|
? null |
||||||
|
: const TextStyle( |
||||||
|
decoration: TextDecoration.lineThrough, |
||||||
|
), |
||||||
|
), |
||||||
|
if (delay != 0) ...[ |
||||||
|
const TextSpan(text: ' '), |
||||||
|
TextSpan( |
||||||
|
text: arrivalWithDelayString, |
||||||
|
style: TextStyle( |
||||||
|
color: delay > 0 |
||||||
|
? Colors.red.lighter |
||||||
|
: Colors.green.lighter, |
||||||
|
// color: delay > 0 |
||||||
|
// ? Colors.red.shade300 |
||||||
|
// : Colors.green.shade300, |
||||||
|
), |
||||||
|
), |
||||||
|
] |
||||||
|
], |
||||||
|
), |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 16, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
}, |
||||||
|
) |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainRouteDistance extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainRouteDistance({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Card( |
||||||
|
child: Center( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"Distanța rutei", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
fontWeight: FontWeight.bold, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
Text( |
||||||
|
"${trainData.stations.last.km} km", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 20, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainRouteDuration extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
|
||||||
|
const DisplayTrainRouteDuration({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Card( |
||||||
|
child: Center( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"Durata rutei", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
fontWeight: FontWeight.bold, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
Builder( |
||||||
|
builder: (context) { |
||||||
|
var duration = trainData.stations.last.arrival!.scheduleTime |
||||||
|
.difference( |
||||||
|
trainData.stations.first.departure!.scheduleTime); |
||||||
|
var durationString = StringBuffer(); |
||||||
|
|
||||||
|
bool firstWritten = false; |
||||||
|
|
||||||
|
if (duration.inDays > 0) { |
||||||
|
firstWritten = true; |
||||||
|
if (duration.inDays == 1) { |
||||||
|
durationString.write("1 zi"); |
||||||
|
} else { |
||||||
|
durationString.write("${duration.inDays} zile"); |
||||||
|
} |
||||||
|
duration -= Duration(days: duration.inDays); |
||||||
|
} |
||||||
|
|
||||||
|
if (duration.inHours > 0) { |
||||||
|
if (firstWritten) { |
||||||
|
durationString.write(", "); |
||||||
|
} |
||||||
|
firstWritten = true; |
||||||
|
if (duration.inHours == 1) { |
||||||
|
durationString.write("1 oră"); |
||||||
|
} else { |
||||||
|
durationString.write("${duration.inHours} ore"); |
||||||
|
} |
||||||
|
duration -= Duration(hours: duration.inHours); |
||||||
|
} |
||||||
|
|
||||||
|
if (duration.inMinutes > 0) { |
||||||
|
if (firstWritten) { |
||||||
|
durationString.write(", "); |
||||||
|
} |
||||||
|
firstWritten = true; |
||||||
|
if (duration.inMinutes == 1) { |
||||||
|
durationString.write("1 minut"); |
||||||
|
} else { |
||||||
|
durationString.write("${duration.inMinutes} minute"); |
||||||
|
} |
||||||
|
duration -= Duration(minutes: duration.inMinutes); |
||||||
|
} |
||||||
|
|
||||||
|
return Text( |
||||||
|
durationString.toString(), |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 20, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
); |
||||||
|
}, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainYesterdayWarningFluent |
||||||
|
extends DisplayTrainYesterdayWarningCommon { |
||||||
|
const DisplayTrainYesterdayWarningFluent(super.onViewYesterdayTrain, {super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
const TextSpan( |
||||||
|
text: DisplayTrainYesterdayWarningCommon.trainDidNotDepart, |
||||||
|
), |
||||||
|
const TextSpan(text: '\n'), |
||||||
|
TextSpan( |
||||||
|
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain, |
||||||
|
style: TextStyle( |
||||||
|
color: Colors.blue, |
||||||
|
), |
||||||
|
recognizer: TapGestureRecognizer() |
||||||
|
..onTap = onViewYesterdayTrain, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DisplayTrainStations extends StatelessWidget { |
||||||
|
final TrainData trainData; |
||||||
|
const DisplayTrainStations({required this.trainData, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return SliverList( |
||||||
|
delegate: SliverChildBuilderDelegate( |
||||||
|
(context, index) { |
||||||
|
return IndexedSemantics( |
||||||
|
index: index, |
||||||
|
child: DisplayTrainStation( |
||||||
|
station: trainData.stations[index], |
||||||
|
onTap: () { |
||||||
|
Navigator.of(context).pushNamed( |
||||||
|
ViewStationPage.routeName, |
||||||
|
arguments: ViewStationArguments(stationName: trainData.stations[index].name), |
||||||
|
); |
||||||
|
}, |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
childCount: trainData.stations.length, |
||||||
|
addSemanticIndexes: true, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,447 @@ |
|||||||
|
import 'package:fluent_ui/fluent_ui.dart'; |
||||||
|
import 'package:info_tren/components/badge/badge.dart'; |
||||||
|
import 'package:info_tren/models.dart'; |
||||||
|
|
||||||
|
class DisplayTrainStation extends StatelessWidget { |
||||||
|
final Station station; |
||||||
|
final void Function()? onTap; |
||||||
|
|
||||||
|
const DisplayTrainStation({required this.station, this.onTap, super.key,}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Padding( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: HoverButton( |
||||||
|
onPressed: onTap, |
||||||
|
builder: (context, states) { |
||||||
|
return Card( |
||||||
|
padding: const EdgeInsets.all(2), |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
crossAxisAlignment: CrossAxisAlignment.center, |
||||||
|
children: <Widget>[ |
||||||
|
Row( |
||||||
|
mainAxisSize: MainAxisSize.max, |
||||||
|
children: <Widget>[ |
||||||
|
Expanded( |
||||||
|
flex: 1, |
||||||
|
child: Align( |
||||||
|
alignment: Alignment.centerLeft, |
||||||
|
child: Builder( |
||||||
|
builder: (context) { |
||||||
|
final departureStatus = station.departure?.status; |
||||||
|
final arrivalStatus = station.arrival?.status; |
||||||
|
int delay; |
||||||
|
bool real; |
||||||
|
if (departureStatus == null) { |
||||||
|
delay = arrivalStatus?.delay ?? 0; |
||||||
|
real = arrivalStatus?.real ?? false; |
||||||
|
} |
||||||
|
else if (arrivalStatus == null) { |
||||||
|
delay = departureStatus.delay; |
||||||
|
real = departureStatus.real; |
||||||
|
} |
||||||
|
else { |
||||||
|
delay = departureStatus.delay; |
||||||
|
real = departureStatus.real; |
||||||
|
if (!real && arrivalStatus.real) { |
||||||
|
delay = arrivalStatus.delay; |
||||||
|
real = arrivalStatus.real; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final isDelayed = delay > 0 && real == true; |
||||||
|
final isOnTime = delay <= 0 && real == true; |
||||||
|
const isNotScheduled = false; |
||||||
|
|
||||||
|
return Badge( |
||||||
|
text: station.km.toString(), |
||||||
|
caption: 'km', |
||||||
|
isNotScheduled: isNotScheduled, |
||||||
|
isDelayed: isDelayed, |
||||||
|
isOnTime: isOnTime, |
||||||
|
); |
||||||
|
} |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
Title( |
||||||
|
station: station, |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
flex: 1, |
||||||
|
child: (station.platform == null) |
||||||
|
? Container() |
||||||
|
: Align( |
||||||
|
alignment: Alignment.centerRight, |
||||||
|
child: Badge(text: station.platform!, caption: 'linia',), |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
Time( |
||||||
|
station: station, |
||||||
|
), |
||||||
|
Delay( |
||||||
|
station: station, |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Title extends StatelessWidget { |
||||||
|
final Station station; |
||||||
|
|
||||||
|
const Title({ |
||||||
|
required this.station, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Text( |
||||||
|
station.name, |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300, |
||||||
|
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal, |
||||||
|
), |
||||||
|
textAlign: TextAlign.center, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Time extends StatelessWidget { |
||||||
|
final Station station; |
||||||
|
|
||||||
|
const Time({ |
||||||
|
required this.station, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
if (station.arrival == null) { |
||||||
|
// Plecare |
||||||
|
return DepartureTime( |
||||||
|
station: station, |
||||||
|
firstStation: true, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
if (station.departure == null) { |
||||||
|
// Sosire |
||||||
|
return ArrivalTime( |
||||||
|
station: station, |
||||||
|
finalStation: true, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return Row( |
||||||
|
crossAxisAlignment: CrossAxisAlignment.center, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"→", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
), |
||||||
|
), |
||||||
|
Container(width: 2,), |
||||||
|
ArrivalTime(station: station,), |
||||||
|
Expanded(child: Container(),), |
||||||
|
StopTime(station: station,), |
||||||
|
Expanded(child: Container(),), |
||||||
|
DepartureTime(station: station,), |
||||||
|
Container(width: 2,), |
||||||
|
Text( |
||||||
|
"→", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class ArrivalTime extends StatelessWidget { |
||||||
|
final Station station; |
||||||
|
final bool finalStation; |
||||||
|
|
||||||
|
const ArrivalTime({ |
||||||
|
required this.station, |
||||||
|
this.finalStation = false, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
if (station.arrival == null) { |
||||||
|
return Container(); |
||||||
|
} |
||||||
|
if (finalStation) { |
||||||
|
return Row( |
||||||
|
crossAxisAlignment: CrossAxisAlignment.center, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"→", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
), |
||||||
|
), |
||||||
|
Container(width: 2,), |
||||||
|
const Text("sosire la "), |
||||||
|
ArrivalTime(station: station,), |
||||||
|
Expanded(child: Container(),), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
final delay = station.arrival!.status?.delay ?? 0; |
||||||
|
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) { |
||||||
|
final oldDate = time; |
||||||
|
final newDate = oldDate.add(Duration(minutes: delay)); |
||||||
|
|
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
decoration: TextDecoration.lineThrough, |
||||||
|
), |
||||||
|
), |
||||||
|
Text( |
||||||
|
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
// color: Colors.red.shade300, |
||||||
|
color: Colors.red.lighter, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
final oldDate = time; |
||||||
|
final newDate = oldDate.add(Duration(minutes: delay)); |
||||||
|
|
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
decoration: TextDecoration.lineThrough, |
||||||
|
), |
||||||
|
), |
||||||
|
Text( |
||||||
|
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
// color: Colors.green.shade300, |
||||||
|
color: Colors.green.lighter, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class StopTime extends StatelessWidget { |
||||||
|
final Station station; |
||||||
|
|
||||||
|
const StopTime({ |
||||||
|
required this.station, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
const Text( |
||||||
|
"staționează pentru", |
||||||
|
textAlign: TextAlign.center, |
||||||
|
), |
||||||
|
Builder( |
||||||
|
builder: (context) { |
||||||
|
int stopsForInt = station.stoppingTime!; |
||||||
|
bool minutes = false; |
||||||
|
if (stopsForInt >= 60) { |
||||||
|
stopsForInt ~/= 60; |
||||||
|
minutes = true; |
||||||
|
} |
||||||
|
if (stopsForInt == 1) { |
||||||
|
return Text( |
||||||
|
"1 ${minutes ? 'minut' : 'secundă'}", |
||||||
|
textAlign: TextAlign.center, |
||||||
|
); |
||||||
|
} |
||||||
|
else if (stopsForInt < 20) { |
||||||
|
return Text( |
||||||
|
"$stopsForInt ${minutes ? 'minute' : 'secunde'}", |
||||||
|
textAlign: TextAlign.center, |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
return Text( |
||||||
|
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}", |
||||||
|
textAlign: TextAlign.center, |
||||||
|
); |
||||||
|
} |
||||||
|
}, |
||||||
|
) |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DepartureTime extends StatelessWidget { |
||||||
|
final Station station; |
||||||
|
final bool firstStation; |
||||||
|
|
||||||
|
const DepartureTime({ |
||||||
|
required this.station, |
||||||
|
this.firstStation = false, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
if (station.departure == null) { |
||||||
|
return Container(); |
||||||
|
} |
||||||
|
if (firstStation) { |
||||||
|
return Row( |
||||||
|
crossAxisAlignment: CrossAxisAlignment.center, |
||||||
|
children: <Widget>[ |
||||||
|
Expanded(child: Container(),), |
||||||
|
const Text("plecare la "), |
||||||
|
DepartureTime(station: station,), |
||||||
|
Container(width: 2,), |
||||||
|
Text( |
||||||
|
"→", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
fontSize: 22, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
final delay = station.departure!.status?.delay ?? 0; |
||||||
|
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) { |
||||||
|
final oldDate = time; |
||||||
|
final newDate = oldDate.add(Duration(minutes: delay)); |
||||||
|
|
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
decoration: TextDecoration.lineThrough, |
||||||
|
), |
||||||
|
), |
||||||
|
Text( |
||||||
|
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
// color: Colors.red.shade300, |
||||||
|
color: Colors.red.lighter, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
else { |
||||||
|
final oldDate = time; |
||||||
|
final newDate = oldDate.add(Duration(minutes: delay)); |
||||||
|
|
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: <Widget>[ |
||||||
|
Text( |
||||||
|
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
decoration: TextDecoration.lineThrough, |
||||||
|
), |
||||||
|
), |
||||||
|
Text( |
||||||
|
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
// color: Colors.green.shade300, |
||||||
|
color: Colors.green.lighter, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class Delay extends StatelessWidget { |
||||||
|
final Station station; |
||||||
|
|
||||||
|
const Delay({ |
||||||
|
required this.station, |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
if (station.arrival?.status == null && station.departure?.status == null) { |
||||||
|
return Container(); |
||||||
|
} |
||||||
|
var delay = station.arrival?.status?.delay; |
||||||
|
if (station.departure?.status?.real == true) { |
||||||
|
delay = station.departure?.status?.delay; |
||||||
|
} |
||||||
|
|
||||||
|
if (delay == 0 || delay == null) { |
||||||
|
return Container(); |
||||||
|
} else if (delay > 0) { |
||||||
|
return Text( |
||||||
|
"$delay ${delay == 1 ? 'minut' : 'minute'} întârziere", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
// color: Colors.red.shade300, |
||||||
|
color: Colors.red.lighter, |
||||||
|
fontSize: 14, |
||||||
|
fontStyle: FontStyle.italic, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
else if (delay < 0) { |
||||||
|
return Text( |
||||||
|
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme", |
||||||
|
style: FluentTheme.of(context).typography.body?.copyWith( |
||||||
|
// color: Colors.green.shade300, |
||||||
|
color: Colors.green.lighter, |
||||||
|
fontSize: 14, |
||||||
|
fontStyle: FontStyle.italic, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return Container(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue