Browse Source

Add Fluent UI for Win, Linux

Also add split screen train view for landscape
master v2.7.9
Kenneth Bruen 2 years ago
parent
commit
0e484bdc16
Signed by: kbruen
GPG Key ID: C1980A470C3EE5B1
  1. 6
      CHANGELOG.txt
  2. 59
      lib/components/badge/badge.dart
  3. 79
      lib/components/badge/badge_cupertino.dart
  4. 80
      lib/components/badge/badge_fluent.dart
  5. 79
      lib/components/badge/badge_material.dart
  6. 3
      lib/components/loading/loading.dart
  7. 26
      lib/components/loading/loading_fluent.dart
  8. 13
      lib/components/select_train_suggestions/select_train_suggestions.dart
  9. 84
      lib/components/select_train_suggestions/select_train_suggestions_fluent.dart
  10. 80
      lib/main.dart
  11. 3
      lib/models/ui_design.dart
  12. 3
      lib/pages/main/main_page.dart
  13. 73
      lib/pages/main/main_page_fluent.dart
  14. 3
      lib/pages/station_arrdep_page/select_station/select_station.dart
  15. 59
      lib/pages/station_arrdep_page/select_station/select_station_fluent.dart
  16. 6
      lib/pages/station_arrdep_page/view_station/view_station.dart
  17. 267
      lib/pages/station_arrdep_page/view_station/view_station_fluent.dart
  18. 4
      lib/pages/station_arrdep_page/view_station/view_station_material.dart
  19. 9
      lib/pages/train_info_page/select_train/select_train.dart
  20. 46
      lib/pages/train_info_page/select_train/select_train_fluent.dart
  21. 195
      lib/pages/train_info_page/view_train/train_info.dart
  22. 853
      lib/pages/train_info_page/view_train/train_info_cupertino.dart
  23. 6
      lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart
  24. 807
      lib/pages/train_info_page/view_train/train_info_fluent.dart
  25. 447
      lib/pages/train_info_page/view_train/train_info_fluent_DisplayTrainStation.dart
  26. 328
      lib/pages/train_info_page/view_train/train_info_material.dart
  27. 6
      lib/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart
  28. 3
      lib/utils/default_ui_design.dart
  29. 44
      pubspec.lock
  30. 3
      pubspec.yaml

6
CHANGELOG.txt

@ -1,3 +1,9 @@
v2.7.9
Add Fluent UI for Windows and Linux.
Add split view in landscape when viewing a train.
Add error handling and auto refresh when viewing a station's arrivals/departures.
General code fixes and migration (freezed, riverpod).
v2.7.8
Added cancelled trains in departures/arrivals board.
Selecting train in departures/arrivels board chooses appropriate departure date.

59
lib/components/badge/badge.dart

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

79
lib/components/badge.dart → lib/components/badge/badge_cupertino.dart

@ -1,84 +1,5 @@
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/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 {
final String text;

80
lib/components/badge/badge_fluent.dart

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

79
lib/components/badge/badge_material.dart

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

3
lib/components/loading/loading.dart

@ -1,6 +1,7 @@
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/loading/loading_cupertino.dart';
import 'package:info_tren/components/loading/loading_fluent.dart';
import 'package:info_tren/components/loading/loading_material.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/providers.dart';
@ -19,6 +20,8 @@ class Loading extends ConsumerWidget {
return LoadingMaterial(text: text ?? defaultText,);
case UiDesign.CUPERTINO:
return LoadingCupertino(text: text ?? defaultText,);
case UiDesign.FLUENT:
return LoadingFluent(text: text ?? defaultText,);
default:
throw UnmatchedUiDesignException(uiDesign);
}

26
lib/components/loading/loading_fluent.dart

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

13
lib/components/select_train_suggestions/select_train_suggestions.dart

@ -2,6 +2,7 @@
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';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_fluent.dart';
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';
@ -31,6 +32,12 @@ class SelectTrainSuggestions extends ConsumerWidget {
onTrainSelected: onTrainSelected,
currentInput: currentInput,
);
case UiDesign.FLUENT:
return SelectTrainSuggestionsFluent(
choices: choices,
onTrainSelected: onTrainSelected,
currentInput: currentInput,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}
@ -102,6 +109,12 @@ class OperatorAutocompleteSliver extends ConsumerWidget {
operatorName: operatorName,
train: train,
);
case UiDesign.FLUENT:
return OperatorAutocompleteTileFluent(
onTrainSelected: onTrainSelected,
operatorName: operatorName,
train: train,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}

84
lib/components/select_train_suggestions/select_train_suggestions_fluent.dart

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

80
lib/main.dart

@ -1,5 +1,10 @@
import 'package:flutter/material.dart';
import 'package:fluent_ui/fluent_ui.dart' as f;
import 'package:flutter/cupertino.dart' as c;
import 'package:flutter/material.dart' as m;
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/about/about_page.dart';
import 'package:info_tren/pages/main/main_page.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
@ -54,48 +59,59 @@ Map<String, WidgetBuilder> get routes => {
},
};
class StartPoint extends StatelessWidget {
class StartPoint extends ConsumerWidget {
final String appTitle = 'Info Tren';
const StartPoint({super.key});
@override
Widget build(BuildContext context) {
// if (Platform.isIOS || Platform.isMacOS) {
// return AnnotatedRegion(
// value: const SystemUiOverlayStyle(
// statusBarBrightness: Brightness.dark,
// ),
// child: CupertinoApp(
// title: appTitle,
// theme: CupertinoThemeData(
// primaryColor: Colors.blue.shade600,
// brightness: Brightness.dark,
// // textTheme: CupertinoTextThemeData(
// // textStyle: TextStyle(
// // fontFamily: 'Atkinson Hyperlegible',
// // ),
// // ),
// ),
// routes: routesByUiDesign(UiDesign.CUPERTINO),
// ),
// );
// }
// else {
return MaterialApp(
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
if (uiDesign == UiDesign.CUPERTINO) {
return AnnotatedRegion(
value: const SystemUiOverlayStyle(
statusBarBrightness: c.Brightness.dark,
),
child: c.CupertinoApp(
title: appTitle,
theme: c.CupertinoThemeData(
primaryColor: m.Colors.blue.shade600,
brightness: c.Brightness.dark,
// textTheme: CupertinoTextThemeData(
// textStyle: TextStyle(
// fontFamily: 'Atkinson Hyperlegible',
// ),
// ),
),
routes: routes,
),
);
}
else if (uiDesign == UiDesign.FLUENT) {
return f.FluentApp(
title: appTitle,
theme: f.ThemeData(
brightness: f.Brightness.dark,
accentColor: f.Colors.blue,
),
routes: routes,
);
}
else {
return m.MaterialApp(
title: appTitle,
theme: ThemeData(
primarySwatch: Colors.blue,
colorScheme: ColorScheme.fromSwatch(
brightness: Brightness.dark,
primarySwatch: Colors.blue,
accentColor: Colors.blue.shade700,
theme: m.ThemeData(
primarySwatch: m.Colors.blue,
colorScheme: m.ColorScheme.fromSwatch(
brightness: m.Brightness.dark,
primarySwatch: m.Colors.blue,
accentColor: m.Colors.blue.shade700,
),
useMaterial3: true,
// fontFamily: 'Atkinson Hyperlegible',
),
routes: routes,
);
// }
}
}
}

3
lib/models/ui_design.dart

@ -1,6 +1,7 @@
enum UiDesign {
MATERIAL,
CUPERTINO
CUPERTINO,
FLUENT,
}
class UnmatchedUiDesignException implements Exception {

3
lib/pages/main/main_page.dart

@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/about/about_page.dart';
import 'package:info_tren/pages/main/main_page_cupertino.dart';
import 'package:info_tren/pages/main/main_page_fluent.dart';
import 'package:info_tren/pages/main/main_page_material.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
@ -20,6 +21,8 @@ class MainPage extends ConsumerWidget {
return const MainPageMaterial();
case UiDesign.CUPERTINO:
return const MainPageCupertino();
case UiDesign.FLUENT:
return const MainPageFluent();
default:
throw UnmatchedUiDesignException(uiDesign);
}

73
lib/pages/main/main_page_fluent.dart

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

3
lib/pages/station_arrdep_page/select_station/select_station.dart

@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_cupertino.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_fluent.dart';
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_material.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/providers.dart';
@ -20,6 +21,8 @@ class SelectStationPage extends ConsumerWidget {
return const SelectStationPageMaterial();
case UiDesign.CUPERTINO:
return const SelectStationPageCupertino();
case UiDesign.FLUENT:
return const SelectStationPageFluent();
default:
throw UnmatchedUiDesignException(uiDesign);
}

59
lib/pages/station_arrdep_page/select_station/select_station_fluent.dart

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

6
lib/pages/station_arrdep_page/view_station/view_station.dart

@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.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_cupertino.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station_fluent.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station_material.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/providers.dart';
@ -31,6 +32,11 @@ class ViewStationPage extends HookConsumerWidget {
tab: tab.value,
setTab: (newTab) => tab.value = newTab,
);
case UiDesign.FLUENT:
return ViewStationPageFluent(
tab: tab.value,
setTab: (newTab) => tab.value = newTab,
);
}
}
}

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

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

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

@ -2,7 +2,7 @@ import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/badge.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';
@ -205,7 +205,7 @@ class ViewStationPageMaterial extends ViewStationPageShared {
IntrinsicHeight(
child: AspectRatio(
aspectRatio: 1,
child: MaterialBadge(
child: Badge(
text: item.status.platform!,
caption: 'Linia',
isOnTime: item.status.real && item.status.delay <= 0,

9
lib/pages/train_info_page/select_train/select_train.dart

@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train_cupertino.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train_fluent.dart';
import 'package:info_tren/pages/train_info_page/select_train/select_train_material.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/providers.dart';
@ -26,6 +27,8 @@ class SelectTrainPage extends ConsumerWidget {
return const SelectTrainPageMaterial();
case UiDesign.CUPERTINO:
return const SelectTrainPageCupertino();
case UiDesign.FLUENT:
return const SelectTrainPageFluent();
default:
throw UnmatchedUiDesignException(uiDesign);
}
@ -70,7 +73,11 @@ abstract class SelectTrainPageState extends State<SelectTrainPageShared> {
return posInNum1.compareTo(posInNum2);
}
return t1.number.length.compareTo(t2.number.length);
if (t1.number.length != t2.number.length) {
return t1.number.length.compareTo(t2.number.length);
}
return t1.number.compareTo(t2.number);
});
return filtered;

46
lib/pages/train_info_page/select_train/select_train_fluent.dart

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

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

@ -1,12 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:info_tren/api/train_data.dart';
import 'package:info_tren/components/loading/loading.dart';
import 'package:info_tren/components/refresh_future_builder.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_cupertino.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_fluent.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
import 'package:info_tren/providers.dart';
@ -30,34 +29,35 @@ class TrainInfo extends ConsumerWidget {
replaceFutureBuilder(() => getTrain(trainNumber, date: DateTime.now().subtract(const Duration(days: 1))));
}
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,);
}
switch (uiDesign) {
case UiDesign.MATERIAL:
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
return TrainInfoLoadingMaterial(title: trainNumber.toString(), loadingText: "Se încarcă...",);
}
else if (snapshot.state == RefreshFutureBuilderState.error) {
return TrainInfoErrorMaterial(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,);
}
return TrainInfoMaterial(
trainData: snapshot.data!,
refresh: refresh,
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
onViewYesterdayTrain: onViewYesterdayTrain,
);
case UiDesign.CUPERTINO:
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
return TrainInfoLoadingCupertino(title: trainNumber.toString(), loadingText: "Se încarcă...",);
}
else if (snapshot.state == RefreshFutureBuilderState.error) {
return TrainInfoErrorCupertino(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,);
}
return TrainInfoCupertino(
trainData: snapshot.data!,
refresh: refresh,
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
onViewYesterdayTrain: onViewYesterdayTrain,
);
case UiDesign.FLUENT:
return TrainInfoFluent(
trainData: snapshot.data!,
refresh: refresh,
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
onViewYesterdayTrain: onViewYesterdayTrain,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}
@ -73,23 +73,176 @@ class TrainInfoArguments {
TrainInfoArguments({required this.trainNumber, this.date});
}
abstract class TrainInfoLoading extends StatelessWidget {
abstract class TrainInfoShared extends StatelessWidget {
final TrainData trainData;
final Future Function()? refresh;
final void Function()? onViewYesterdayTrain;
final bool? isRefreshing;
const TrainInfoShared({
super.key,
required this.trainData,
this.refresh,
this.onViewYesterdayTrain,
this.isRefreshing,
});
}
class TrainInfoLoading extends ConsumerWidget {
final String title;
final String? loadingText;
const TrainInfoLoading({
super.key,
required this.title,
this.loadingText,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:
return TrainInfoLoadingMaterial(
title: title,
loadingText: loadingText,
);
case UiDesign.CUPERTINO:
return TrainInfoLoadingCupertino(
title: title,
loadingText: loadingText,
);
case UiDesign.FLUENT:
return TrainInfoLoadingFluent(
title: title,
loadingText: loadingText,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}
}
}
abstract class TrainInfoLoadingShared extends StatelessWidget {
final String title;
final Widget loadingWidget;
TrainInfoLoading({
TrainInfoLoadingShared({
required this.title,
String? loadingText,
super.key,
}) : loadingWidget = Loading(text: loadingText,);
}
abstract class TrainInfoError extends StatelessWidget {
class TrainInfoError extends ConsumerWidget {
final String title;
final Object error;
final Future Function()? refresh;
const TrainInfoError({
super.key,
required this.title,
required this.error,
this.refresh,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:
return TrainInfoErrorMaterial(
title: title,
error: error,
refresh: refresh,
);
case UiDesign.CUPERTINO:
return TrainInfoErrorCupertino(
title: title,
error: error,
refresh: refresh,
);
case UiDesign.FLUENT:
return TrainInfoErrorFluent(
title: title,
error: error,
refresh: refresh,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}
}
}
abstract class TrainInfoErrorShared extends StatelessWidget {
final String title;
final Object error;
final Future Function()? refresh;
const TrainInfoError({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 {
final TrainData trainData;
final void Function()? onViewYesterdayTrain;
final Future Function()? refresh;
final bool? isRefreshing;
const TrainInfoBody({
required this.trainData,
this.onViewYesterdayTrain,
this.refresh,
this.isRefreshing,
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final uiDesign = ref.watch(uiDesignProvider);
switch (uiDesign) {
case UiDesign.MATERIAL:
return TrainInfoBodyMaterial(
trainData: trainData,
onViewYesterdayTrain: onViewYesterdayTrain,
refresh: refresh,
isRefreshing: isRefreshing,
);
case UiDesign.CUPERTINO:
return TrainInfoBodyCupertino(
trainData: trainData,
onViewYesterdayTrain: onViewYesterdayTrain,
refresh: refresh,
isRefreshing: isRefreshing,
);
case UiDesign.FLUENT:
return TrainInfoBodyFluent(
trainData: trainData,
onViewYesterdayTrain: onViewYesterdayTrain,
refresh: refresh,
isRefreshing: isRefreshing,
);
default:
throw UnmatchedUiDesignException(uiDesign);
}
}
}
abstract class TrainInfoBodyShared extends StatelessWidget {
final TrainData trainData;
final void Function()? onViewYesterdayTrain;
final Future Function()? refresh;
final bool? isRefreshing;
const TrainInfoBodyShared({
required this.trainData,
this.onViewYesterdayTrain,
this.refresh,
this.isRefreshing,
super.key,
});
}
abstract class DisplayTrainYesterdayWarningCommon extends StatelessWidget {

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

@ -10,7 +10,7 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart';
import 'package:info_tren/utils/state_to_string.dart';
class TrainInfoLoadingCupertino extends TrainInfoLoading {
class TrainInfoLoadingCupertino extends TrainInfoLoadingShared {
TrainInfoLoadingCupertino({required super.title, super.loadingText, super.key,});
@override
@ -26,17 +26,13 @@ class TrainInfoLoadingCupertino extends TrainInfoLoading {
}
}
class TrainInfoErrorCupertino extends TrainInfoError {
class TrainInfoErrorCupertino extends TrainInfoErrorShared {
const TrainInfoErrorCupertino({
required Object error,
required String title,
Future Function()? refresh,
required super.error,
required super.title,
super.refresh,
super.key,
}) : super(
error: error,
title: title,
refresh: refresh,
);
});
@override
Widget build(BuildContext context) {
@ -64,17 +60,12 @@ class TrainInfoErrorCupertino extends TrainInfoError {
}
}
class TrainInfoCupertino extends StatelessWidget {
final TrainData trainData;
final Future Function()? refresh;
final bool? isRefreshing;
final void Function()? onViewYesterdayTrain;
class TrainInfoCupertino extends TrainInfoShared {
const TrainInfoCupertino({
required this.trainData,
this.refresh,
this.isRefreshing,
this.onViewYesterdayTrain,
required super.trainData,
super.refresh,
super.isRefreshing,
super.onViewYesterdayTrain,
super.key,
});
@ -93,195 +84,10 @@ class TrainInfoCupertino extends StatelessWidget {
child: SafeArea(
top: false,
bottom: false,
child: Builder(builder: (context) {
final topPadding = MediaQuery.of(context).padding.top;
return NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
// SliverPadding(
// padding: EdgeInsets.only(
// top: topPadding,
// ),
// ),
SliverPersistentHeaderPadding(
maxHeight: topPadding,
)
];
},
body: Builder(builder: (context) {
return CustomScrollView(
slivers: <Widget>[
if (refresh != null)
CupertinoSliverRefreshControl(
builder: (context, mode, pulledExtent,
refreshTriggerPullDistance, refreshIndicatorExtent) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: pulledExtent,
child: Column(
children: [
SizedBox(
height: min(
refreshIndicatorExtent, pulledExtent),
child: Center(
child: Builder(
builder: (context) {
if (mode ==
RefreshIndicatorMode.inactive) {
return Container();
} else if (mode ==
RefreshIndicatorMode.done) {
return const Text('Refreshed!');
} else if (mode ==
RefreshIndicatorMode.drag) {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(
animating: false,
),
Text('Pull to refresh...'),
],
);
} else if (mode ==
RefreshIndicatorMode.armed) {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(
animating: false,
),
Text('Release to refresh...'),
],
);
} else {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(),
Text('Refreshing'),
],
);
}
},
),
),
),
Expanded(
child: Container(),
),
],
),
),
],
);
},
onRefresh: refresh,
),
DisplayTrainID(
trainData: trainData,
),
DisplayTrainOperator(
trainData: trainData,
),
DisplayTrainRoute(
trainData: trainData,
),
DisplayTrainDeparture(
trainData: trainData,
),
const SliverToBoxAdapter(
child: CupertinoDivider(
color: foregroundWhite,
),
),
DisplayTrainLastInfo(
trainData: trainData,
),
const SliverToBoxAdapter(
child: CupertinoDivider(),
),
SliverToBoxAdapter(
child: IntrinsicHeight(
child: Row(
children: <Widget>[
// Expanded(
// child: DisplayTrainNextStop(trainData: trainData,),
// ),
Expanded(
child: DisplayTrainRouteDuration(
trainData: trainData,
),
),
// Expanded(
// child: DisplayTrainDestination(trainData: trainData,),
// ),
const SizedBox(
height: double.infinity,
child: CupertinoVerticalDivider(),
),
Expanded(
child: DisplayTrainRouteDistance(
trainData: trainData,
),
),
],
),
),
),
// SliverToBoxAdapter(
// child: CupertinoDivider(),
// ),
// SliverToBoxAdapter(
// child: IntrinsicHeight(
// child: Row(
// children: <Widget>[
// // Expanded(
// // child: DisplayTrainRouteDuration(trainData: trainData,),
// // ),
// Expanded(child: Container(),),
// SizedBox(
// height: double.infinity,
// child: CupertinoVerticalDivider(),
// ),
// Expanded(
// child: DisplayTrainRouteDistance(trainData: trainData,),
// )
// ],
// ),
// ),
// ),
const SliverToBoxAdapter(
child: CupertinoDivider(
color: foregroundWhite,
),
),
if (onViewYesterdayTrain != null && trainData.stations.first.departure!.scheduleTime.compareTo(DateTime.now()) > 0) ...[
SliverToBoxAdapter(
child: DisplayTrainYesterdayWarningCupertino(onViewYesterdayTrain!),
),
const SliverToBoxAdapter(
child: CupertinoDivider(
color: foregroundWhite,
),
),
],
DisplayTrainStations(
trainData: trainData,
),
SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).viewPadding.bottom,
),
),
],
);
}),
);
}),
child: TrainInfoBody(
trainData: trainData,
onViewYesterdayTrain: onViewYesterdayTrain,
),
),
);
@ -390,31 +196,382 @@ class TrainInfoCupertino extends StatelessWidget {
}
}
class TrainInfoBodyCupertino extends TrainInfoBodyShared {
const TrainInfoBodyCupertino({
super.key,
required super.trainData,
super.onViewYesterdayTrain,
super.isRefreshing,
super.refresh,
});
@override
Widget build(BuildContext context) {
final mq = MediaQuery.of(context);
final topPadding = mq.padding.top;
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),
DisplayTrainRoute(trainData: trainData),
DisplayTrainDeparture(trainData: trainData),
const CupertinoDivider(
color: foregroundWhite,
),
DisplayTrainLastInfo(trainData: trainData),
const CupertinoDivider(),
IntrinsicHeight(
child: Row(
children: <Widget>[
// Expanded(
// child: DisplayTrainNextStop(trainData: trainData,),
// ),
Expanded(
child: DisplayTrainRouteDuration(
trainData: trainData,
),
),
// Expanded(
// child: DisplayTrainDestination(trainData: trainData,),
// ),
const SizedBox(
height: double.infinity,
child: CupertinoVerticalDivider(),
),
Expanded(
child: DisplayTrainRouteDistance(
trainData: trainData,
),
),
],
),
),
const CupertinoDivider(
color: foregroundWhite,
),
if (onViewYesterdayTrain != null && trainData.stations.first.departure!.scheduleTime.compareTo(DateTime.now()) > 0) ...[
DisplayTrainYesterdayWarningCupertino(onViewYesterdayTrain!),
const CupertinoDivider(
color: foregroundWhite,
),
],
],
),
),
Expanded(
child: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
// SliverPadding(
// padding: EdgeInsets.only(
// top: topPadding,
// ),
// ),
SliverPersistentHeaderPadding(
maxHeight: topPadding,
)
];
},
body: CustomScrollView(
slivers: [
if (refresh != null)
CupertinoSliverRefreshControl(
builder: (context, mode, pulledExtent,
refreshTriggerPullDistance, refreshIndicatorExtent) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: pulledExtent,
child: Column(
children: [
SizedBox(
height: min(
refreshIndicatorExtent, pulledExtent),
child: Center(
child: Builder(
builder: (context) {
if (mode ==
RefreshIndicatorMode.inactive) {
return Container();
} else if (mode ==
RefreshIndicatorMode.done) {
return const Text('Refreshed!');
} else if (mode ==
RefreshIndicatorMode.drag) {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(
animating: false,
),
Text('Pull to refresh...'),
],
);
} else if (mode ==
RefreshIndicatorMode.armed) {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(
animating: false,
),
Text('Release to refresh...'),
],
);
} else {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(),
Text('Refreshing'),
],
);
}
},
),
),
),
Expanded(
child: Container(),
),
],
),
),
],
);
},
onRefresh: refresh,
),
DisplayTrainStations(
trainData: trainData,
),
SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).viewPadding.bottom,
),
),
],
),
),
),
],
);
}
return NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
// SliverPadding(
// padding: EdgeInsets.only(
// top: topPadding,
// ),
// ),
SliverPersistentHeaderPadding(
maxHeight: topPadding,
)
];
},
body: Builder(builder: (context) {
return CustomScrollView(
slivers: <Widget>[
if (refresh != null)
CupertinoSliverRefreshControl(
builder: (context, mode, pulledExtent,
refreshTriggerPullDistance, refreshIndicatorExtent) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: pulledExtent,
child: Column(
children: [
SizedBox(
height: min(
refreshIndicatorExtent, pulledExtent),
child: Center(
child: Builder(
builder: (context) {
if (mode ==
RefreshIndicatorMode.inactive) {
return Container();
} else if (mode ==
RefreshIndicatorMode.done) {
return const Text('Refreshed!');
} else if (mode ==
RefreshIndicatorMode.drag) {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(
animating: false,
),
Text('Pull to refresh...'),
],
);
} else if (mode ==
RefreshIndicatorMode.armed) {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(
animating: false,
),
Text('Release to refresh...'),
],
);
} else {
return Row(
mainAxisSize: MainAxisSize.min,
children: const [
CupertinoActivityIndicator(),
Text('Refreshing'),
],
);
}
},
),
),
),
Expanded(
child: Container(),
),
],
),
),
],
);
},
onRefresh: refresh,
),
...[
DisplayTrainID(
trainData: trainData,
),
DisplayTrainOperator(
trainData: trainData,
),
DisplayTrainRoute(
trainData: trainData,
),
DisplayTrainDeparture(
trainData: trainData,
),
const CupertinoDivider(
color: foregroundWhite,
),
DisplayTrainLastInfo(
trainData: trainData,
),
const CupertinoDivider(),
IntrinsicHeight(
child: Row(
children: <Widget>[
// Expanded(
// child: DisplayTrainNextStop(trainData: trainData,),
// ),
Expanded(
child: DisplayTrainRouteDuration(
trainData: trainData,
),
),
// Expanded(
// child: DisplayTrainDestination(trainData: trainData,),
// ),
const SizedBox(
height: double.infinity,
child: CupertinoVerticalDivider(),
),
Expanded(
child: DisplayTrainRouteDistance(
trainData: trainData,
),
),
],
),
),
const CupertinoDivider(
color: foregroundWhite,
),
if (onViewYesterdayTrain != null && trainData.stations.first.departure!.scheduleTime.compareTo(DateTime.now()) > 0) ...[
DisplayTrainYesterdayWarningCupertino(onViewYesterdayTrain!),
const CupertinoDivider(
color: foregroundWhite,
),
],
].map((e) => SliverToBoxAdapter(child: e)),
// SliverToBoxAdapter(
// child: CupertinoDivider(),
// ),
// SliverToBoxAdapter(
// child: IntrinsicHeight(
// child: Row(
// children: <Widget>[
// // Expanded(
// // child: DisplayTrainRouteDuration(trainData: trainData,),
// // ),
// Expanded(child: Container(),),
// SizedBox(
// height: double.infinity,
// child: CupertinoVerticalDivider(),
// ),
// Expanded(
// child: DisplayTrainRouteDistance(trainData: trainData,),
// )
// ],
// ),
// ),
// ),
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 SliverToBoxAdapter(
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: trainData.rank,
style: TextStyle(
color: trainData.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
),
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: 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: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
),
),
const TextSpan(text: ' '),
TextSpan(text: trainData.number,),
],
),
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
),
),
);
@ -428,41 +585,39 @@ class DisplayTrainRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Row(
children: <Widget>[
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
trainData.route.from,
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
),
return Row(
children: <Widget>[
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
trainData.route.from,
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
),
),
),
const Center(child: Text("-")),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
trainData.route.to,
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
textAlign: TextAlign.right,
),
),
const Center(child: Text("-")),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
trainData.route.to,
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
textAlign: TextAlign.right,
),
),
),
],
),
),
],
);
}
}
@ -474,15 +629,13 @@ class DisplayTrainOperator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Center(
child: Text(
trainData.operator,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
fontStyle: FontStyle.italic,
),
),
return Center(
child: Text(
trainData.operator,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
fontStyle: FontStyle.italic,
),
),
);
}
@ -495,18 +648,16 @@ class DisplayTrainDeparture extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(2),
child: Text(
// "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
"Plecare în ${trainData.date}",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w200,
),
textAlign: TextAlign.center,
),
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: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w200,
),
textAlign: TextAlign.center,
),
);
}
@ -520,90 +671,86 @@ class DisplayTrainLastInfo extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (trainData.status == null) {
return SliverToBoxAdapter(
child: Container(),
);
return Container();
}
return SliverToBoxAdapter(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(2),
child: Text(
"Ultima informație",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(2),
child: Text(
"Ultima informație",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(4),
child: Text(
trainData.status!.station,
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.left,
),
),
Expanded(
child: Container(),
),
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(4),
child: Text(
trainData.status!.station,
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.left,
),
Padding(
padding: const EdgeInsets.all(4),
child: Text(
stateToString(trainData.status!.state),
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.right,
),
),
Expanded(
child: Container(),
),
Padding(
padding: const EdgeInsets.all(4),
child: Text(
stateToString(trainData.status!.state),
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.right,
),
],
),
// FutureDisplay<DateTime>(
// future: trainData.lastInfo.dateAndTime,
// builder: (context, dt) {
// return Text(
// "Raportat în ${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year.toString().padLeft(4, '0')}, la ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}",
// textAlign: TextAlign.center,
// );
// },
// ),
Builder(
builder: (context) {
final data = trainData.status!.delay;
),
],
),
// FutureDisplay<DateTime>(
// future: trainData.lastInfo.dateAndTime,
// builder: (context, dt) {
// return Text(
// "Raportat în ${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year.toString().padLeft(4, '0')}, la ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}",
// textAlign: TextAlign.center,
// );
// },
// ),
Builder(
builder: (context) {
final data = trainData.status!.delay;
if (data == 0) {
return Container();
}
if (data == 0) {
return Container();
}
if (data > 0) {
return Text(
"$data ${data == 1 ? 'minut' : 'minute'} întârziere",
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
color: CupertinoColors.destructiveRed,
),
);
} else {
return Text(
"${-data} ${data == -1 ? 'minut' : 'minute'} mai devreme",
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 12,
color: CupertinoColors.systemGreen,
),
);
}
},
)
],
),
if (data > 0) {
return Text(
"$data ${data == 1 ? 'minut' : 'minute'} întârziere",
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
color: CupertinoColors.destructiveRed,
),
);
} else {
return Text(
"${-data} ${data == -1 ? 'minut' : 'minute'} mai devreme",
style:
CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 12,
color: CupertinoColors.systemGreen,
),
);
}
},
)
],
);
}
}

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

@ -1,5 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:info_tren/components/badge.dart';
import 'package:info_tren/components/badge/badge.dart';
import 'package:info_tren/models.dart';
class DisplayTrainStation extends StatelessWidget {
@ -47,7 +47,7 @@ class DisplayTrainStation extends StatelessWidget {
final isOnTime = delay <= 0 && real == true;
const isNotScheduled = false;
return CupertinoBadge(
return Badge(
text: station.km.toString(),
caption: 'km',
isNotScheduled: isNotScheduled,
@ -65,7 +65,7 @@ class DisplayTrainStation extends StatelessWidget {
flex: 1,
child: Align(
alignment: Alignment.centerRight,
child: station.platform == null ? Container() : CupertinoBadge(text: station.platform!, caption: 'linia'),
child: station.platform == null ? Container() : Badge(text: station.platform!, caption: 'linia'),
),
),
],

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

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

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

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

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

@ -7,7 +7,7 @@ import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart';
import 'package:info_tren/utils/state_to_string.dart';
class TrainInfoLoadingMaterial extends TrainInfoLoading {
class TrainInfoLoadingMaterial extends TrainInfoLoadingShared {
TrainInfoLoadingMaterial({required super.title, super.loadingText, super.key,});
@override
@ -24,7 +24,7 @@ class TrainInfoLoadingMaterial extends TrainInfoLoading {
}
}
class TrainInfoErrorMaterial extends TrainInfoError {
class TrainInfoErrorMaterial extends TrainInfoErrorShared {
const TrainInfoErrorMaterial({
required super.error,
required super.title,
@ -61,15 +61,12 @@ class TrainInfoErrorMaterial extends TrainInfoError {
bool isSmallScreen(BuildContext context) =>
MediaQuery.of(context).size.height <= 425;
class TrainInfoMaterial extends StatelessWidget {
final TrainData trainData;
final Future Function()? refresh;
final void Function()? onViewYesterdayTrain;
class TrainInfoMaterial extends TrainInfoShared {
const TrainInfoMaterial({
required this.trainData,
this.refresh,
this.onViewYesterdayTrain,
required super.trainData,
super.refresh,
super.onViewYesterdayTrain,
super.isRefreshing,
super.key,
});
@ -79,12 +76,13 @@ class TrainInfoMaterial extends StatelessWidget {
builder: (context) {
return Scaffold(
appBar: isSmallScreen(context)
? null
: AppBar(
centerTitle: true,
title: Text(
"Informații despre ${trainData.rank} ${trainData.number}"),
? null
: AppBar(
centerTitle: true,
title: Text(
"Informații despre ${trainData.rank} ${trainData.number}",
),
),
body: Column(
children: <Widget>[
if (isSmallScreen(context))
@ -93,8 +91,8 @@ class TrainInfoMaterial extends StatelessWidget {
left: false,
right: false,
child: SlimAppBar(
title:
'INFO TREN - ${trainData.rank} ${trainData.number}'),
title: 'INFO TREN - ${trainData.rank} ${trainData.number}',
),
),
Expanded(
child: SafeArea(
@ -102,102 +100,9 @@ class TrainInfoMaterial extends StatelessWidget {
top: isSmallScreen(context) ? false : true,
child: RefreshIndicator(
onRefresh: refresh ?? () async {},
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: DisplayTrainID(
trainData: trainData,
),
),
SliverToBoxAdapter(
child: DisplayTrainOperator(
trainData: trainData,
),
),
SliverPadding(
padding: const EdgeInsets.only(left: 2, right: 2),
sliver: SliverToBoxAdapter(
child: DisplayTrainRoute(
trainData: trainData,
),
),
),
SliverToBoxAdapter(
child: DisplayTrainDeparture(
trainData: trainData,
),
),
// SliverToBoxAdapter(
// child: Divider(
// color: Colors.white70,
// height: isSmallScreen(context) ? 8 : 16,
// ),
// ),
SliverToBoxAdapter(
child: DisplayTrainLastInfo(
trainData: trainData,
),
),
SliverToBoxAdapter(
child: IntrinsicHeight(
child: Row(
children: <Widget>[
// Expanded(child: DisplayTrainNextStop(trainData: trainData,)),
// Expanded(child: DisplayTrainDestination(trainData: trainData,)),
Expanded(
child: DisplayTrainRouteDuration(
trainData: trainData,
)),
Expanded(
child: DisplayTrainRouteDistance(
trainData: trainData,
),
),
],
),
),
),
// SliverToBoxAdapter(
// child: IntrinsicHeight(
// child: Row(
// children: <Widget>[
// // Expanded(child: DisplayTrainRouteDuration(trainData: trainData,)),
// Expanded(child: Container(),),
// Expanded(child: DisplayTrainRouteDistance(trainData: trainData,)),
// ],
// ),
// ),
// ),
SliverToBoxAdapter(
child: Divider(
color: Colors.white70,
height: isSmallScreen(context) ? 8 : 16,
),
),
if (onViewYesterdayTrain != null &&
trainData.stations.first.departure!.scheduleTime
.compareTo(DateTime.now()) >
0) ...[
SliverToBoxAdapter(
child: DisplayTrainYesterdayWarningMaterial(
onViewYesterdayTrain!),
),
SliverToBoxAdapter(
child: Divider(
color: Colors.white70,
height: isSmallScreen(context) ? 8 : 16,
),
),
],
DisplayTrainStations(
trainData: trainData,
),
SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).viewPadding.bottom,
),
),
],
child: TrainInfoBody(
trainData: trainData,
onViewYesterdayTrain: onViewYesterdayTrain,
),
),
),
@ -210,6 +115,201 @@ class TrainInfoMaterial extends StatelessWidget {
}
}
class TrainInfoBodyMaterial extends TrainInfoBodyShared {
const TrainInfoBodyMaterial({
super.key,
required super.trainData,
super.onViewYesterdayTrain,
super.isRefreshing,
super.refresh,
});
@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),
DisplayTrainLastInfo(trainData: trainData),
IntrinsicHeight(
child: Row(
children: <Widget>[
Expanded(
child: DisplayTrainRouteDuration(
trainData: trainData,
),
),
Expanded(
child: DisplayTrainRouteDistance(
trainData: trainData,
),
),
],
),
),
Divider(
color: Colors.white70,
height: isSmallScreen(context) ? 8 : 16,
),
if (onViewYesterdayTrain != null &&
trainData.stations.first.departure!.scheduleTime
.compareTo(DateTime.now()) >
0)
...[
DisplayTrainYesterdayWarningMaterial(
onViewYesterdayTrain!,
),
Divider(
color: Colors.white70,
height: isSmallScreen(context) ? 8 : 16,
),
],
],
),
),
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: Divider(
// color: Colors.white70,
// height: isSmallScreen(context) ? 8 : 16,
// ),
// ),
SliverToBoxAdapter(
child: DisplayTrainLastInfo(
trainData: trainData,
),
),
SliverToBoxAdapter(
child: IntrinsicHeight(
child: Row(
children: <Widget>[
// Expanded(child: DisplayTrainNextStop(trainData: trainData,)),
// Expanded(child: DisplayTrainDestination(trainData: trainData,)),
Expanded(
child: DisplayTrainRouteDuration(
trainData: trainData,
),
),
Expanded(
child: DisplayTrainRouteDistance(
trainData: trainData,
),
),
],
),
),
),
// SliverToBoxAdapter(
// child: IntrinsicHeight(
// child: Row(
// children: <Widget>[
// // Expanded(child: DisplayTrainRouteDuration(trainData: trainData,)),
// Expanded(child: Container(),),
// Expanded(child: DisplayTrainRouteDistance(trainData: trainData,)),
// ],
// ),
// ),
// ),
SliverToBoxAdapter(
child: Divider(
color: Colors.white70,
height: isSmallScreen(context) ? 8 : 16,
),
),
if (onViewYesterdayTrain != null &&
trainData.stations.first.departure!.scheduleTime
.compareTo(DateTime.now()) >
0) ...[
SliverToBoxAdapter(
child: DisplayTrainYesterdayWarningMaterial(
onViewYesterdayTrain!),
),
SliverToBoxAdapter(
child: Divider(
color: Colors.white70,
height: isSmallScreen(context) ? 8 : 16,
),
),
],
DisplayTrainStations(
trainData: trainData,
),
SliverToBoxAdapter(
child: Container(
height: MediaQuery
.of(context)
.viewPadding
.bottom,
),
),
],
);
}
}
}
class DisplayTrainID extends StatelessWidget {
final TrainData trainData;
@ -798,7 +898,7 @@ class DisplayTrainStations extends StatelessWidget {
onTap: () {
Navigator.of(context).pushNamed(
ViewStationPage.routeName,
arguments: trainData.stations[index].name,
arguments: ViewStationArguments(stationName: trainData.stations[index].name),
);
},
),

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

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:info_tren/models.dart';
import 'package:info_tren/components/badge.dart';
import 'package:info_tren/components/badge/badge.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
class DisplayTrainStation extends StatelessWidget {
@ -54,7 +54,7 @@ class DisplayTrainStation extends StatelessWidget {
final isOnTime = delay <= 0 && real == true;
const isNotScheduled = false;
return MaterialBadge(
return Badge(
text: station.km.toString(),
caption: 'km',
isNotScheduled: isNotScheduled,
@ -74,7 +74,7 @@ class DisplayTrainStation extends StatelessWidget {
? Container()
: Align(
alignment: Alignment.centerRight,
child: MaterialBadge(text: station.platform!, caption: 'linia',),
child: Badge(text: station.platform!, caption: 'linia',),
),
),
],

3
lib/utils/default_ui_design.dart

@ -6,6 +6,9 @@ UiDesign get defaultUiDesign {
if (Platform.isIOS) {
return UiDesign.CUPERTINO;
}
else if (Platform.isLinux || Platform.isWindows) {
return UiDesign.FLUENT;
}
else {
return UiDesign.MATERIAL;
}

44
pubspec.lock

@ -99,6 +99,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
code_builder:
dependency: transitive
description:
@ -162,6 +169,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
fluent_ui:
dependency: "direct main"
description:
name: fluent_ui
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.3+1"
flutter:
dependency: "direct main"
description: flutter
@ -181,6 +195,11 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_riverpod:
dependency: transitive
description:
@ -256,6 +275,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
intl:
dependency: transitive
description:
name: intl
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.0"
io:
dependency: transitive
description:
@ -311,7 +337,7 @@ packages:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
version: "0.1.5"
meta:
dependency: transitive
description:
@ -424,6 +450,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
recase:
dependency: transitive
description:
name: recase
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
riverpod:
dependency: transitive
description:
@ -431,6 +464,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
scroll_pos:
dependency: transitive
description:
name: scroll_pos
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
shared_preferences:
dependency: "direct main"
description:
@ -659,7 +699,7 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
version: "2.1.2"
watcher:
dependency: transitive
description:

3
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.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 2.7.8
version: 2.7.9
environment:
sdk: ">=2.17.0 <3.0.0"
@ -33,6 +33,7 @@ dependencies:
freezed_annotation: ^2.2.0
json_annotation: ^4.7.0
shared_preferences: ^2.0.15
fluent_ui: ^4.0.3+1
dev_dependencies:
# flutter_test:

Loading…
Cancel
Save